PLT(Procedure Linkage Table) : 외부 프로시저를 연결해주는 테이블. PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해 사용할 수 있다

GOT(Global Offset Table) : PLT가 참조하는 테이블. 프로시저들의 주소가 들어있다


PLT와 GOT를 사용하는 이유 | Linker

어떤 코드(printf)가 쓰여있는 소스파일을 실행파일로 만들기 위해서는 컴파일이라는 과정을 거쳐야 한다. 컴파일을 통해 오브젝트 파일이 생성된다. 하지만 오브젝트 파일을그 자체로 실행하지 못하고 오브젝트 파일과 printf의 실행 코드를 찾아서 서로 연결시켜야 한다. 이런 오브젝트 파일들이 모여있는 곳을 라이브러리(Library)라고 한다. 이렇게 라이브러리 등 필요한 오브젝트 파이들을 연결시키는 작업을 Linking이라고 한다.

Link를 하는 방법에는 Static과 Dynamic이 있다.

Static Link 방식은 파일 생성시 라이브러리 내용을 포함한 실행파일을 만든다.

실행 파일 안에 모든 코드가 포함되기 때문에 라이브러리 연동 과정이 따로 필요 없고, 한번 생성한 파일에 대해서 필요한 라이브러리를 따로 관리하지 않아도 되기 때문에 편하지만 파일 크키가 커지는 단점이 있다. 또한 동일한 라이브러리를 사용하더라도 해당 라이브러리를 사용하는 모든 프로그래들은 라이브러리의 내용을 메모리에 매핑 시켜야 한다

Dynamic Link 방식은 공유라이브러리를 사용한다. 라이브러리를 하나의 메모리 공간에 매핑하고 여러 프로그램에서 공유하여 사용하는 것이다.

실행파일 안에 라이브러리 코드를 포함하지 않기 때문에 파일 크기도 작아지고 실행시 적은 메모리를 차지한다. 그러나 실행파일이 라이브러리에 의존하기 때문에 라이브러리가 없으면 실행할 수 없다.


PLT와 GOT는 Dynamic Link 방식으로 컴파일 했을때 사용되는데 그 이유는 라이브러리가 프로그램 외부에 있기 때문에 함수의 주소를 알아오는 과정이 필요하기 때문이다.


Dynamic Link 방식으로 프로그램이 만들어지면 함수를 호출 할 때 PLT를 참조하게 된다. PLT는 GOT로 점프를 하고 GOT에는 라이브러에 존재하는 실제 함수의 주소가 쓰여있어서 printf 함수를 호출하게 된다. 

여기서 첫 호출인지 아닌지에 따라 동작 과정이 조금 달라진다.

첫 번째 호출이라면 GOT에 실제 함수의 주소가 쓰여있지 않고 두번째 호출부터 GOT에 실제 함수의 주소가 쓰인다. 그래서 첫 호출 시에는 Linker가 dl_resolve라는 함수를 사용해 필요한 함수의 주소를 알아오고, GOT에 그 주소를 써준 후 해당 함수를 호출한다.


라이브러리란? | 라이브러니는 다른 프로그램들과 링크되기 위하여 존재하는 하나 이상의 서브루틴이나 함수들의 집합 파일 형태를 말한다. 보통 미리 컴파일된 형태인 오브젝트코드 형태로 존재한다. 라이브러리라는 기술이 생긴 이유는 코드의 재사용, 부품화 실현, 소스를 제공하지 않음으로서 중요 기술의 유출 방지, 개발자들의 개발시간 단축들의 장점이 있기 때문이다. 

라이브러리들은 사용자의 프로그램과 링크되어, 실행이 가능한 완전한 프로그램을 이룬다. 링크의 종류는 Static Link가 있고 Dynamic Link가 있다.


공유 라이브러리(.so/.sa) | 공유라이브러리를 사용하여 컴파일을 하면 링커가 실행 파일에다가 "실행될 때 우선 이 라이브러리를 로딩시킬 것"이라는 표시를 해둔다. 그리고 실행할 때 라이브러리에 있는 컴파일된 코드를 가져와 사용한다.

이렇게 하면 정적 라이브러리를 사용하는 것보다 파일 크기가 작아지고, 메모리를 적게 차지하고 메모리 사용에 효율적이다. 그러니 배포할 때 공유 라이브러리를 함께 배포해야 한다. 


공유 라이브러리 구성 | 공유 라이브러리 파일은 확장자가 파일포맷이 ELF이면 .so, a.out이면 .sa이다. (a.out은 과거 유닉스 계통 운영 체제에서 사용하던 실행 파일과 목적 파일 형식)

그리고 그 뒤에 버전 숫자가 붙는데 메이지 넘버와 마이너 넘버이다.(메이지 넘버는 라이브러리 버전들 간 잠재적 비호환성을 나타내고, 마이너 넘버는 버그 픽스들만을 나타낸다) 

Example ) 일반적으로 libexample.so 파일은 libexample.so.N에 링크되고 다시 이것은 libexample.so.N.M에 링크됨. N은 가장 높은 메이저 넘버이고, M은 가장 높은 마이너 넘버이다.


출처 : http://sens.tistory.com/33

        https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/

'@C언어 : Layer7' 카테고리의 다른 글

2018년에 한것..(?)  (0) 2018.12.24
HackerSchool FTZ 풀이  (0) 2018.06.15
Git 사용 보고서  (1) 2018.06.08
너의 타자소리가 들려  (0) 2018.05.23
구조체 예제  (0) 2018.05.15

-2018 - 10 - 6 ~(P3)-프로세스 메모리 읽고 쓰기


1. 프로세스 자기 자신의 base address(0x00400000)부터 10바이트만큼 읽어서 출력

2. TlHelp32.h 내부의 함수를 이용해 PID, base address를 얻는 함수 정의

3. ASLR 옵션이 적용된 프로세스의 base address + 0x108E부터 6바이트만큼 읽어서 출력

4. base address + 0x108E부터 2바이트를{ 0x00, 0xEB }로 쓰기

5. base address + 0x108E부터 6바이트만큼 읽어서 출력



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<stdio.h>
#include<stdlib.h>
#include<wchar.h>
#include<Windows.h>
#include<tlhelp32.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
 
void GetBaseAddressByName(PROCESSENTRY32 *sub)
{
    unsigned char buf1[] = { 0x000xEB };
    unsigned char buf2[20= { 0 }, bs[10= { 0 };
    int errCode;
    SIZE_T readSize;
 
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, sub->th32ProcessID);
 
    if (NULL != hProcess)
    {
        HMODULE hMod;
        //HINSTANCE 핸들은 보통 실행되고 있는 Win32 프로그램의 메모리 상에 올라가 있는 시작 주소값을 갖고 있음
        DWORD cbNeeded;
 
        if (EnumProcessModulesEx(hProcess, &hMod, sizeof(hMod),&cbNeeded,LIST_MODULES_64BIT))
            //EnumProcessModulesEx : 지정된 필터 조건을 충족시키는 지정된 프로세스의 각 모듈에 대한 핸들을 검색
        {
            GetModuleBaseName(hProcess, hMod, sub->szExeFile,sizeof(sub->szExeFile) / sizeof(TCHAR));
            //GetModuleBaseName : 지정된 모듈의 기본 이름을 검색
            //GetModuleBaseName 함수는 주로 디버거 및 다른 프로세스에서 모듈 정보를 추출해야하는 유사한 응용 프로그램에서 사용하도록 설계됨
                wprintf(TEXT("Base address : 0x%p\n"), hMod);
 
                printf("값 바꾸기 전..\n");
                ReadProcessMemory(hProcess, hMod + 0x108E, buf2, 6&readSize);
                printf("%u byte read!\n", readSize);
                for (int i = 0; i < 6; i++) {
                    printf("%#02x ", buf2[i]);
                }
                printf("\n");
                memset(buf2, 0sizeof(buf2));
 
                printf("값 바꾼 후..\n");
                WriteProcessMemory(hProcess, (LPVOID)((int)hMod + 0x108E), buf1, 2&readSize);
                printf("WriteProcessMemory error code : %d\n", GetLastError());
 
                printf("%u byte write!\n", readSize);
                ReadProcessMemory(hProcess, (LPCVOID)((int)hMod + 0x108E), buf2, 6&readSize);
                printf("ReadProcessMemory error code : %d\n", GetLastError());
                printf("%u byte read!\n", readSize);
                for (int i = 0; i < 6; i++) {
                    printf("%#02x ", buf2[i]);
                }
                printf("\n");
                errCode = GetLastError();
                printf("errCode : %d\n", errCode);
        }
    }
 
    CloseHandle(hProcess);
}
 
int main() {
    HANDLE hProcessSnap;
    PROCESSENTRY32 processinform, sub;
    processinform.dwSize = sizeof(PROCESSENTRY32);
    sub.dwSize = sizeof(PROCESSENTRY32);
 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
        printf("CreateToolhelp32Snapshot error \n");
 
    if (!Process32First(hProcessSnap, &processinform)) {
        printf("Process32First error ! \n");
        CloseHandle(hProcessSnap);
        return 0;
    }
    printf("LIST> \n");
    printf("\t\t[Process name \t[PID]\t[개수]\t[부모프로세스ID] \n");
 
    do {
        wprintf(L"%25s %8d %8d %16d \n"//유니코드 출려과 아스키코드 출력은 다르므로 유니코드를 출력하는 wprintf를 사용해야함
            processinform.szExeFile, processinform.th32ProcessID, processinform.cntThreads, processinform.th32ParentProcessID);
    } while (Process32Next(hProcessSnap, &processinform));
 
 
    printf("프로세스 이름 입력 :");
    memset(&sub, 0sizeof(sub));
    wscanf(L"%[^\n]s"&sub.szExeFile);  //%[^\n]s : 개행이 오기전까지 입력받음
    CloseHandle(hProcessSnap);
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
        printf("CreateToolhelp32Snapshot error \n");
 
    if (!Process32First(hProcessSnap, &processinform)) {
        printf("Process32First error ! \n");
        CloseHandle(hProcessSnap);
        return 0;
    }
    while (Process32Next(hProcessSnap, &processinform)) {
        if (!wcscmp(sub.szExeFile, processinform.szExeFile)) {
            sub.th32ProcessID = processinform.th32ProcessID;
            sub.cntThreads = processinform.cntThreads;
            sub.th32ParentProcessID = processinform.th32ParentProcessID;
            printf("\t\t[Process name] \t[PID]\t[개수]\t[부모프로세스 ID] \n");
            wprintf(L"%25s %8d %8d %16d \n"//유니코드 출려과 아스키코드 출력은 다르므로 유니코드를 출력하는 wprintf를 사용해야함
                sub.szExeFile, sub.th32ProcessID, sub.cntThreads, sub.th32ParentProcessID);
            GetBaseAddressByName(&sub);
        }
    }
    CloseHandle(hProcessSnap);
 
    return 0;
}
cs


ASLR가 걸려있는 프로그램은 계속 변하는 베이스 어드레스를 찾기위해 조금 다른 함수가 쓰였다...

코드에서 확인><

'@안티치트' 카테고리의 다른 글

실제 프로그램에 적용해보기(메모리 읽고 쓰기)  (0) 2018.11.05
다른 프로세스의 메모리 읽기  (0) 2018.10.05
프로세스 메모리 읽어오기  (0) 2018.10.05
START!  (0) 2018.10.05

  - 2018-03-17 ~ (P2) - 다른 프로세스의 메모리 읽기

1. 다른 프로세스(Cpp_AnotherProcess.exe)의 핸들 얻기

1-1 TlHelp32.h

1) CreateToolhelp32Snapshot(), Process32First(), Process32Next()로 pId 얻기

2) OpenProcess()로 핸들 얻기

※특정 프로세스의 핸들을 얻는 다른 방법 찾아보기

2. ReadProcessMemory()로 0x40108B부터 6바이트만큼 읽어서 버퍼에 저장

3. 버퍼 출력


프로그램의 기능은 이렇다

//1. 현재 돌아가는 프로그램의 모든 스냅샷 출력

//2. 메모리를 읽고싶은 프로그램 입력

//3. WriteProcessMemory()로 0x40108E부터 2바이트를 {0x00,0xEB}로 쓰기

//4. ReadProcessMemory()로 0x40108E부터 6바이트를 읽어서 버퍼에저장, 출력



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include<stdio.h>
#include<stdlib.h>
#include<wchar.h>
#include<Windows.h>
#include<tlhelp32.h>
 
 
void Read(PROCESSENTRY32 *sub) {
    HANDLE openPro;
    SIZE_T readSize;
    int buf1[3= { 0x00,0xEB }, buf2[20= { 0 };
    int errCode;
    openPro = OpenProcess(PROCESS_ALL_ACCESS, FALSE, sub->th32ProcessID);
 
    printf("값 바꾸기 전..\n");
    ReadProcessMemory(openPro, (LPCVOID)0x40108E, buf2, 6&readSize);
    for (int i = 0; i < 6; i++) {
        printf("%#02x ", buf2[i]);
    }
    printf("\n");
    memset(buf2, 0sizeof(buf2));
 
    printf("값 바꾼 후..\n");
    WriteProcessMemory(openPro, (LPVOID)0x40108E, buf1, 2&readSize);
    ReadProcessMemory(openPro, (LPCVOID)0x40108E, buf2, 6&readSize);
    for (int i = 0; i < 6; i++) {
        printf("%#02x ", buf2[i]);
    }
    printf("\n");
    errCode = GetLastError();
    printf("errCode : %d\n", errCode);
}
 
int main() {
    HANDLE hProcessSnap;
    PROCESSENTRY32 processinform,sub;
    processinform.dwSize = sizeof(PROCESSENTRY32);
    sub.dwSize = sizeof(PROCESSENTRY32);
 
    hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) 
        printf("CreateToolhelp32Snapshot error \n");
 
    if (!Process32First(hProcessSnap, &processinform)) {
        printf("Process32First error ! \n");
        CloseHandle(hProcessSnap);
        return 0;
    }
    printf("LIST> \n");
    printf("\t\t[Process name \t[PID]\t[개수]\t[부모프로세스ID] \n");
 
    do {
        wprintf(L"%25s %8d %8d %16d \n"//유니코드 출려과 아스키코드 출력은 다르므로 유니코드를 출력하는 wprintf를 사용해야함
            processinform.szExeFile, processinform.th32ProcessID, processinform.cntThreads, processinform.th32ParentProcessID);
    } while (Process32Next(hProcessSnap, &processinform));
 
 
    printf("프로세스 이름 입력 :");
    memset(&sub, 0sizeof(sub));
    wscanf(L"%s",&sub.szExeFile);
    CloseHandle(hProcessSnap);
    //이미 앞에서 한번 더 읽어줬기 때문에 이전의 핸들을 반환하고 다시 새로운 핸들을 생성해야한다
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
        printf("CreateToolhelp32Snapshot error \n");
 
    if (!Process32First(hProcessSnap, &processinform)) {
        printf("Process32First error ! \n");
        CloseHandle(hProcessSnap);
        return 0;
    }
    while (Process32Next(hProcessSnap, &processinform)) {
        if (!wcscmp(sub.szExeFile, processinform.szExeFile)) {
            sub.th32ProcessID = processinform.th32ProcessID;
            sub.cntThreads = processinform.cntThreads;
            sub.th32ParentProcessID = processinform.th32ParentProcessID;
            printf("\t\t[Process name] \t[PID]\t[개수]\t[부모프로세스 ID] \n");
            wprintf(L"%25s %8d %8d %16d \n"//유니코드 출려과 아스키코드 출력은 다르므로 유니코드를 출력하는 wprintf를 사용해야함
                sub.szExeFile, sub.th32ProcessID, sub.cntThreads, sub.th32ParentProcessID);
            Read(&sub);
        }
    }
    CloseHandle(hProcessSnap);
 
    return 0;
}
 
cs



CreateToolhelp32Snapshot()

지정된 프로세스뿐만 아니라 이러한 프로세스가 사용하는 힙, 모듈 및 스레드의 스냅 샷을 가져옵니다.

1
HANDLE CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );

cs


매개변수
DWORD dwFlags : 상황에 따라 많음

DWORD th32ProcessID :스냅 샷에 포함될 프로세스의 프로세스 식별자. 이 매개 변수는 현재 프로세스를 나타내는 0 일 수 있음

추가의 내용이 많으니 링크를 ! 

https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot



Process32First()

시스템 스냅 샷에서 발생한 첫 번째 프로세스에 대한 정보를 검색

1
BOOL Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );

cs


매개변수

HANDLE hSnapshot : CreateToolhelp32Snapshot() 함수에 대한 이전 호출에서 반환 된 스냅 샷 핸들입니다.

LPPROCESSENTRY32 lppe :  processentry32 구조체에 대한 포인터. 실행 파일의 이름, 프로세스 식별자 및 상위 프로세스의 프로세스 ID와 같은 프로세스 정보를 포함합니다.( 구조체의 사이즈는 deSize로 해야함)

프로세스 목록의 첫 번째 항목이 버퍼에 복사 된 경우 TRUE를 반환하고 그렇지 않으면 FALSE를 반환


Process32Next()

시스템 스냅 샷에 기록 된 다음 프로세스에 대한 정보를 검색합니다.

1
BOOL Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
cs
매개변수
Process32First()와 기능은 비슷 링크를 ! 
https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/nf-tlhelp32-process32next



ReadProcessMemory()

지정된 프로세스에서 메모리 영역에서 데이터를 읽습니다. 읽을 전체 영역에 액세스하거나 작업이 실패합니다.

1
2
3
4
5
6
7
8
BOOL WINAPI ReadProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPCVOID lpBaseAddress,
  _Out_ LPVOID  lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesRead
);
 
cs

매개변수

hProcess : 읽히는 메모리가 있는 프로세스에 대한 핸들입니다. 핸들은 프로세스에 대한 PROCESS_VM_READ 액세스 권한이 있어야 합니다.


lpBaseAddress : 읽을 지정된 프로세스의 기본 주소에 대한 포인터 


IpBuffer :지정된 프로세스의 주소 공간으로부터 내용을 수신하는 버퍼에 대한 포인터


nSize : 지정된 프로세스에서 읽을 바이트 수


lpNumberOfBytesRead : 지정된 버퍼로 전송된 바이트 수를 수신하는 변수에 대한 포인터


링크 ! https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms680553(v=vs.85).aspx


WriteProcessMemory()

지정된 프로세스의 메모리 영역에 데이터를 기록합니다. 기록할 전체 영역에 액세스해야 합니다. 그렇지 않으면 작업이 실패합니다.


1
2
3
4
5
6
7
BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);
cs

ReadProcessMemory()와 거의 비슷... 링크를!

https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms681674(v=vs.85).aspx


'@안티치트' 카테고리의 다른 글

실제 프로그램에 적용해보기(메모리 읽고 쓰기)  (0) 2018.11.05
프로세스 메모리 읽고 쓰기(ASLR)  (0) 2018.10.05
프로세스 메모리 읽어오기  (0) 2018.10.05
START!  (0) 2018.10.05

- 2018-03-10 ~ (P1) - 프로세스 자신의 메모리 읽기

1. 프로세스 자기 자신의 핸들 얻기

1-1 GetCurrentProcess()로 핸들 얻기

1-2  1) GetCurrentProcessId()로 pId 얻기

         2) OpenProcess()로 핸들 얻기


2. ReadProcessMemory()로 base address(0x00400000)부터 10바이트만큼 읽어서 버퍼에 저장

3. 버퍼 출력

※ x86 빌드, ASLR off



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
int main() {
    HANDLE handle1,openPro;
    int processId, buf[20= { 0 };
    int errCode;
    SIZE_T readSize;
    DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(),
        &handle1, 0, TRUE, DUPLICATE_SAME_ACCESS);
    //DuplicateHandle : 현재 프로세스의 진짜 핸들을 얻는 함수 . handel1에 핸들이 들어가게 됨
    processId = GetCurrentProcessId(); //GetCurrentProcessId : 리턴 값은 호출 프로세스의 프로세스 ID
    openPro = OpenProcess(PROCESS_ALL_ACCESS,FALSE, processId); 
//OpenProcess : 프로세스 핸들 얻어오는 함수
    ReadProcessMemory(handle1,(LPCVOID)0x400000,buf,10,&readSize);
    //ReadProcessMemory()로 base address(0x00400000)부터 10바이트만큼 읽어서 버퍼에 저장
    errCode = GetLastError();
    printf("%d\n", errCode);
    printf("Handle(GetCurrentProcess): %d\n", handle1);
    printf("Handle(OpenProcess): %d\n", openPro);
    printf("ProcessId(GetCurrentProcessId): %d\n", processId);
    printf("ReadProcessMemory read size : %d \n", readSize);
    printf("읽은 10바이트: ");
    for (int i = 0; i < 10; i++) {
        printf("%#02x ", buf);
    }
    return 0;
}
cs


GetCurrentProcess()

HANDLE WINAPI GetCurrentProcess(void);

현재 실행되고 있는 프로세스(위의 함수를 호출한 프로세스)의 핸들을 반환값을 통해 얻을수있다.

GetCurrentProcess() 함수로 구하는 핸들은 가짜(Pseudo) 핸들이다.

핸들 테이블에 등록되지 않은 핸들이다. 이 함수에서 반환되는 핸들은 현재 실행중인 프로세스를 참조하기 위한 용도로

정의해 놓은 상수가 반환된다. 



DuplicateHandle()

현재 프로세스의 진짜 핸들을 얻고 싶다면 이 함수를 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
BOOL WINAPI DuplicateHandle
( _In_   HANDLE hSourceProcessHandle, //복제할 핸들을 소유하는 프로세스
  _In_   HANDLE hSourceHandle, //복제할 핸들 _In_   
  HANDLE hTargetProcessHandle, //복제된 핸들을 소유할 프로세스
  _Out_  LPHANDLE lpTargetHandle, //복제된 핸들값을 저장한 변수의 주소 
  _In_   DWORD dwDesiredAccess, //복제할 핸들의 접근권한 
  _In_   BOOL bInheritHandle, //복제할 핸들의 상속여부 
  _In_   DWORD dwOptions //옵션 );
 
 
 
cs


사용법


1
2
3
4
5
HANDLE hProcess;
 
DuplicateHandle(GetCurrentProcess(),GetCurrentProcess(),GetCurrentProcess(),
 
&hProcess,0,TRUE,DUPLICATE_SAME_ACCESS);
cs


출처: http://hypen1117.tistory.com/entry/DuplicateHandle-현재-프로세스의-진짜-핸들을-얻는-함수 [For the]








솔직히...안티치트는 시작한지 오래되었다ㅎㅎ....

2018년도 초부터 시작했는데 그때에는 막 프로그래밍을 시작하고 고등학교 생활을 시작하던 시기여서 나에게 마음의 여유가 없었나 보다. 한달 정도 후에 연구회를 나왔고 2학기가 시작한 후 다시 들어가서 활동하게 되었다!

평소 리버싱과 같은 프로그램 분석하는 것을 흥미있어 했는데 안티치트에서 연구하는 것들이 나에게 잘 맞는거 같고 프로그래밍 실력에도 도움이 되는것 같아서 열심히 해볼 생각이다. 오늘은 늦었으니 이만--!

프로그램 짠것들은 내일부터 올려야지ㅎㅅㅎ


level 1


level 1의 비밀번호는 level1

우선 정보를 모으기 위해 ls -al을 해 보았더니 hint라는 파일이 있어서 cat명령어를 사용하여 출력해보았다.




이렇게 힌트가 떴다. setuid라고?

Set 이란 말은 "변경하다" 란 뜻을 가지고 있고, UID는 User ID의 약자이다.

설명하면 SetUID는 일시적으로 자신의 ID를 변경하는 것을 말한다.


자신의 암호를 변경하는 passwd 파일에 루트 권한의 SetUID가 걸려있어서

일반 사용자들이 그 passwd 파일을 실행하는 동안에는 "루트로의 일시적인 아이디 변경" 이 되는것이다.




SetUID가 걸린 파일을 찾는 명령어는

find / -user level2 -perm -4000 2> /dev/null 이다. 


2> /dev/null : 오류는 /dev/null 파일에 담으라는 뜻


/bin/ExcuteMe 라는 이름의 파일이 떠서 실행시켜보면



my-pass 는 현재 자신의 비밀번호를 볼 수 있는 명령어 이고

chmod는 디렉토리나 파일의 권한을 바꿔주는 명령어 이다.

아무런 제한이 없다면 저 두 명령어를 쓰는것이 젤 쉽겠지만 안된다고 하니 다른 방법을 찾아보자.


내가 약간 희한하게 푼 것같기는 한데 /etc/passwd 에 사용자의 비밀번호가 기록되어 있다고해서 찾아봤더니

많은 것들 중 level2의 쉘이 /bin/bash인 걸 알 수 있다.

그래서 level2의 권한으로 명령어를 하나 실행할 수 있을때 /bin/bash를 사용해서 level 2의 쉘을 열기로 했고

level2의 쉘을 따게 되었당!!!



level 2


level 2의 비밀번호는 hacker or cracker


hint에서는 텍스트 파일 도중 쉘 명령어를 실행 시킬 수 있다고 해서



 일단 level3의 setuid를 가진 파일을 찾아봤다.

find / -user level3 -perm -4000 2> /dev/null 명령어를 쳤더니 

/usr/bin/editor가 떠서 실행시켜 보았더니 vim파일이었다.




구글에서 파일 편집 도중 명령어를 사용하려면 !를 붙이고 실행시켜야 한다고 했다.

level3:x:3003:3003:Level 3:/home/level3:/bin/bash 

level3 또한 쉘의 종류가 bash여서 :! /bin/bash라고 했더니 level3을 들어가졌다.

/usr/bin/editor가 level3의 setuid를 갖고 있는 파일이었기 때문에 level3의 권한으로 level3의 쉘이 들어갈 수 있었다.


level3


level 3의 비밀번호는 can you fly?


힌트에는 autodig의 소스와 more hints 로 

1)동시에 여러 명령어를 사용하려면?

2) 문자열 형태로 명령어를 전달하려면?

이 있었다.

문제를 풀기 전에 저 more hints에 대한것을 찾아봤는데


- 동시에 여러 명령어를 사용하려면?

    ; - 앞의 명령어가 실패해도 다음 명령어가 실행

    && - 앞의 명령어가 성공했을 때 다음 명령어가 실행

    & - 앞의 명령어를 백그라운드로 돌리고 동시에 뒤의 명령어를 실행


- 문자열 형태로 명령어를 전달하려면?

쉘 스크립트 : 쉘 명령어가 담긴 파일

"asdf asdf" <-이렇게


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char **argv){
 
    char cmd[100];
 
    if( argc!=2 ){
        printf"Auto Digger Version 0.9\n" );
        printf"Usage : %s host\n", argv[0] );
        exit(0);
    }
 
    strcpy( cmd, "dig @" );
    strcat( cmd, argv[1] );
    strcat( cmd, " version.bind chaos txt");
 
    system( cmd );
 
 
cs


argc : 명령어 개수를 받는 변수
argv : 명령어 문자열을 저장하는 변수
이 소스의 핵심은 argv[1]로 넘어온 사용자가 적은 명령을 바로 실행시키는 것이다.

autodig라고해서 구글에 dig를 검색해 보았더니
dig는 Domain Information Groper의 약자로 네임서버로 부터 정보를 가져올 수 있는 툴이다.

dig 명령은 DNS 네임서버 구성과 도메인 설정이 완료된 후, 일반 사용자의 입장에서 설정한 도메인네임에 대한 DNS 질의응답이 정상적으로 이루어지는지를 확인 점검하는 경우에 많이 사용한다고 한다.

[root@web ~]# dig [@server] [domain] [query type]

server : 확인하고자할 네임서버를 지정하는 곳이며 지정하지 않을경우 리눅스 
/etc/resolv.conf에 등록된 네임서버를 이용하여 루트 서버를 조회하게 됩니다.
라고 해서 /etc/resolv.conf에 들어갔더니 192.168.203.2가 나왔는데 솔직히 이건 필요없고
 


이 문제의 핵심은 소스를해석해서 argv[1]로 넘어온 사용자가 적은 명령을 바로 실행시킨다는것.[system( cmd );]
그래서 이 문제를 해결하는 방법이 총 3개 있다.
1. /bin/autodig "192.168.203.2 && /bin/bash ;" 



2. /bin/autodig "192.168.203.2 && /bin/bash &&"


3. autodig의 소스를 복사해서 ./test라는 파일을 만들어서 "192.168.203.2 && /bin/bash"했더니 저렇게 나온다. 저 소스가 결과적으로 실행하는 명령어를 출력하게 만들어 놓음. /bin/bash는 뒤에 인자가 붙지 않으면 쉘을 실행시키고 뒤에 인자가 붙으면 그 파일을 쉘 스크립트로 실행한다. 그래서 저 뒤에 붙는 version.bind을 쉘스크립트로 만들어서 /bin/bash를 실행시키게 한다. 1번과 2번은 version.bind가 실행되지 않도록 /bin/bash까지만 실행하도록 만들어 놓은 것 이다.

<소스를 복사해서 실제로 소스가하는 일은 무엇인지 알아보았다.> 
[level3@ftz tmp]$ ./test "192.168.203.2 && /bin/bash"
===================
dig @192.168.203.2 && /bin/bash version.bind chaos txt
===================
[level3@ftz tmp]$ /bin/autodig "192.168.203.2 && /bin/bash"

























'@C언어 : Layer7' 카테고리의 다른 글

2018년에 한것..(?)  (0) 2018.12.24
PLT,GOT와 동적 라이브러리  (0) 2018.10.23
Git 사용 보고서  (1) 2018.06.08
너의 타자소리가 들려  (0) 2018.05.23
구조체 예제  (0) 2018.05.15

Git 이란?

VCS(version Control System)로 버전 관리 시스템이다.


Git을 쓰는 이유

1. 여러 개발자들과 같이 작업을 할때 코드를 유용하게 공유 할 수 있다.

2. 변경된 내용이나 오류를 업데이트 하거나 수정하기 쉽다.

3. 변경된 내용이 왜 변경되었는지 기록이 가능하다.


Branch 란?

브랜치란 독립적으로 어떤 작업을 진행하기 위한 개념이다.

각각의 독립적인 브랜치는 다른 브랜치의 영향을 받지 않기 때문에 여러 작업을 동시에 진행할 수 있다.

이런 브랜치들이 다른 브랜치들과 병합함으로써 모든 작업내용을 갖고있는 하나의 브랜치로도 만들 수 있다.


Git 사용법>


<git에 올릴 파일 생성 & git에 파일 올리기>


poodoong@layer7:~$ git init  : git을 사용할 수 있도록 초기화

Initialized empty Git repository in /home/poodoong/.git/


poodoong@layer7:~$ ls -al   :  모든 파일 보기

total 44

drwxr-xr-x  5 poodoong poodoong 4096  6월  7 23:31 .

drwxr-xr-x 44 root     root     4096  6월  7 21:32 ..

-rw-------  1 poodoong poodoong  313  6월  5 23:07 .bash_history

-rw-r--r--  1 poodoong poodoong  220  6월  4 19:27 .bash_logout

-rw-r--r--  1 poodoong poodoong 3771  6월  4 19:27 .bashrc

drwx------  2 poodoong poodoong 4096  6월  4 19:29 .cache

drwxrwxr-x  7 poodoong poodoong 4096  6월  7 23:31 .git

-rw-r--r--  1 poodoong poodoong 2181  6월  4 19:27 .kshrc

-rw-r--r--  1 poodoong poodoong  655  6월  4 19:27 .profile

-rw-------  1 poodoong poodoong  940  6월  5 20:46 .viminfo

drwxrwxr-x  3 poodoong poodoong 4096  6월  5 20:46 test


poodoong@layer7:~$ ls  : 모든 디렉토리 보기

test


poodoong@layer7:~/test$ vim README.c  : test디렉토리에 README라는 c언어 파일 만들기


poodoong@layer7:~/test$ gcc -o README README.c  : 컴파일 돌리기(만약 아무 에러가 없다면 아무것도 출력되지 않음)


poodoong@layer7:~/test$ git config --global user.email "kimwhayoung0115@naver.com"  : git 사용자 설정
poodoong@layer7:~/test$ git config --global user.name "Glume.f" 

poodoong@layer7:~/test$ git add * : 파일 추가하기

poodoong@layer7:~/test$ git commit -m "Initial Commit"  : 기록 저장(분기점 생성)
[master (root-commit) 5875b2a] Initial Commit
 5 files changed, 12 insertions(+)
 create mode 100755 README
 create mode 100644 README.c
 create mode 100644 README.md
 create mode 100644 Yogroot.c
 create mode 100755 mainaa

poodoong@layer7:~/test$ git push origin master --force  :  서버에 올리기(--force는 강제로 덮어쓰기)
Username for 'https://github.com': kimwhayoung0115@naver.com
Password for 'https://kimwhayoung0115@naver.com@github.com':



<git에 올린 파일 지우기>

poodoong@layer7:~/test$ ls -l  : 현재 test 디렉토리에 있는 파일 모두 출력
total 36
-rwxrwxr-x 1 poodoong poodoong 8608  6월  7 23:40 README
-rw-rw-r-- 1 poodoong poodoong   72  6월  7 23:39 README.c
-rw-rw-r-- 1 poodoong poodoong   13  6월  4 19:36 README.md
-rw-rw-r-- 1 poodoong poodoong   71  6월  5 20:46 Yogroot.c
-rwxrwxr-x 1 poodoong poodoong 8608  6월  5 20:46 mainaa

poodoong@layer7:~/test$ rm mainaa  : mainaa 삭제
poodoong@layer7:~/test$ git add mainaa : mainaa을 삭제했다는 것을 git한테 직접 알림

poodoong@layer7:~/test$ rm README README.md Yogroot.c  : 필요 없는 것들을 더 삭제해 본다

poodoong@layer7:~/test$ git add * : (오류) add * 은 현재 존재하는 파일만 git 기록에 추가하기 때문에 삭제된 디렉토리의 정보를 올리려면 git add -A를 써야한다. mainaa를 삭제하고 add *를 썼음에도 불구하고 잘 삭제되었던 이유는 우리가 git한테 직접 삭제사실을 알렸기 때문

poodoong@layer7:~/test$ git commit -m "remove file"  : 파일 삭제 이유를 알리고 기록에 추가
[master 266939b] remove file
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100755 mainaa

poodoong@layer7:~/test$ git add -A : 이 명령어는 모든 파일의 변경된 이력을 기록에 추가

poodoong@layer7:~/test$ git commit -m "remove file"
[master e1d1253] remove file
 3 files changed, 6 deletions(-)
 delete mode 100755 README
 delete mode 100644 README.md
 delete mode 100644 Yogroot.c

poodoong@layer7:~/test$ git push origin master : 서버에 올리기
Username for 'https://github.com': kimwhayoung0115@naver.com
Password for 'https://kimwhayoung0115@naver.com@github.com': 

삭제하지 않은 README.c 파일만 남은 것을 확인 할 수 있다.







'@C언어 : Layer7' 카테고리의 다른 글

PLT,GOT와 동적 라이브러리  (0) 2018.10.23
HackerSchool FTZ 풀이  (0) 2018.06.15
너의 타자소리가 들려  (0) 2018.05.23
구조체 예제  (0) 2018.05.15
구조체  (0) 2018.05.13


너의 타자소리가 들려 - by 화영


1. 작품 개요


-개발동기

평소 영어 타자 실력이 좋지 못해서 컴퓨터에 기본적으로 깔려있는 한컴 타자연습에 들어가서  연습을 하려고 했더니 프로그램이 오류가 나면서 작동이 중지 됐었다. C언어 프로젝트의 주제를 고민하다가 이 기회를 이용하여 직접 타자연습 프로그램을 만들게 되었다.


-개발환경

visual studio 2017


-제작과정

키보드 연습, 긴글 연습을 먼저 만들고 시작하면을 디자인 했다.

17~19일에 키보드 연습을 만들었고 , 

19~22일을 긴글 연습을 만드는데 사용했다.

나머지는 오류를 수정하고 기능추가와 보고서와 ppt를 만들었다.


2. 작품 소개


-사용 헤더 및 라이브러리


#include<stdio.h>

#include<stdlib.h>

#include<time.h>

#include<windows.h>


#define _CRT_SECURE_NO_WARNINGS

#define COLOR_YELLOW SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14)

#define COLOR_WHITE SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7)


1) 시간 측정을 위해 time.h 라이브러리 사용

2) 글자 색 변환을 위해 windows.h 라이브러리 사용

3) SetConsoleTextAttribute(GetStdHandle

(STD_OUTPUT_HANDLE), 14) 글자를 노란색으로 변환

4) #define COLOR_WHITE SetConsoleTextAttribute(GetStdHandle

(STD_OUTPUT_HANDLE), 7) 글자를 흰색으로 변환



-사용자 지정 함수


void start(); ->시작 화면 출력


void level_one();  //키보드

void print_keyboard(char key); ->키보드 랜덤 출력

char random_key(); ->랜덤숫자 지정

int input_key(char key, int *arr); ->입력받는 함수

void keyboard_turn(int *arr); ->키보드를 출력하고 출력횟수 제한


void level_two();  //긴글

char *ReadAllFile(char *fileName); ->파일을 읽고 버퍼에 저장

void Input_filename(char *Inbuf, int len); ->파일 이름 입력 확인

void print_contents(GameInfo Game); ->지정 길이 만큼 내용 출력

void input_contents(GameInfo *Game); ->출력된 내용 입력받기

void print_score(GameInfo Game); ->타수와 정확도 측정.출력


-구조체


typedef struct _Engfile {      

char file_Letitgo[30];

char file_Partof[30];

char file_Awolenew[30];

char file_Healthe[30];

char file_Sugar[30];

}Engfile;

->긴글 연습에 필요한 파일 이름을 저장시켜놓은 구조체

(입력받은 파일이름을 비교하기 위해)


typedef struct _Gameinfo {
char *contents; //내용 들어있는 contents 포인터
int offset; //contents에서 입력할 글자 가리키기
int start_time; //입력 시작 시간
//현재속도 = (타수-백스페이스*2) / 경과시간(초) * 60초
//      한컴타자는 백스페이스 * 3
int end_time; //입력 끝난 시간
int elapsedTime; // 입력 끝난 시간 - 입력 시작 시간
int correct; //입력 글자 중 맞은 개수
int wrong; //입력 글자중 틀린 개수
}GameInfo;
->긴글 연습 한번을 실행 시킬때 필요한 내용을 담아놓은 구조체



-실행 화면


<시작화면>




<키보드 연습(1)>



<키보드 연습(2)>



<긴 글 연습(1)>



<긴 글 연습(2)>



3. 개발하면서 느낀점


내가 목표한 프로그램을 다 만들었다는 거에 큰 성취감을 느꼈고 지금까지 배워왔던 모든 C언어 지식들을 총 동원하여 만들어서 다시한번 명령어들의 쓰임새와 알고있던 것들이 정리되는 느낌이었다.

'@C언어 : Layer7' 카테고리의 다른 글

HackerSchool FTZ 풀이  (0) 2018.06.15
Git 사용 보고서  (1) 2018.06.08
구조체 예제  (0) 2018.05.15
구조체  (0) 2018.05.13
메모리 구조  (0) 2018.05.12

+ Recent posts