Global API Hooking-ntdll!ZwResumeThread() API
프로세스를 생성하는 API에 대해서 생각해봅시다.
프로세스를 생성하는 API는 kernel32!CreateProcess() API가 가장 유명합니다.
CreateProcess() API의 디버깅을 위하여 다음과 같이 간단한 프로그램을 만들어보겠습니다.
이 코드를 빌드해서 cptest.exe를 생성합니다. 이 파일을 디버깅해보면 프로세스 생성과 관련된 API들의 호출 흐름을 할 수 있습니다.
-코드
- #include "Windows.h"
- #include "tchar.h"
- void main()
- {
- STARTUPINFO si={0,};
- PROCESS_INFORMATION pi={0,};
- TCHAR szCmd[MAX_PATH]={0,};
- si.cb=sizeof(STARTUPINFO);
- _tcscpy(szCmd, L"notepad.exe"); //notepad.exe의 실행정보를 szCmd에 저장
- if(!CreateProcess(NULL,
- szCmd,NULL, //lpCommandLine
- NULL,FALSE,
- NORMAL_PRIORITY_CLASS,
- NULL,NULL,&si,&pi)
- )
- return;
- if(pi.hProcess !=NULL)
- CloseHandle(pi.hProcess);
- }
kernel32.CreateProcessW() 내부로 따라 들어가면(F7) 위와 같이 kernel32.CreateProcessInternalW() 호출 코드를 볼 수 있습니다. 그리고 notepad.exe가 그대로 넘어와서 세번 째 인자가 된다는 것을 볼 수강 있습니다.
CreateProcessInternalW() 내부를 살펴보도록 하겠습니다.
이렇게 긴 코드가 나옵니다. CreateProcessInternalW() 함수는 상당히 큰 함수라는 것을 알 수 있습니다. 아래로 쭉 스크롤 해보면
ntdll.ZwCreateUserProcess()를 호출하는 코드가 나옵니다.
이번 그림에서 아래쪽의 스택을 보면 위 그림처럼 인자를 알려주는 스택형태와 많이 다른 형태인 것을 알 수 있습니다. 2번째 파라미터(Arg2)는 어떤 구조체인데 왼쪽의 Hex dump창을 보면 구조체 멤버 중에 19F9EC주소에 0019FD30가 쓰여있는 것이 확인됩니다.
이 주소로 이동해보겠습니다.
밑에 보이시죠?? notepad.exe 문자열을 확인할 수가 있었습니다. 그럼 ntdll.ZwCreateUserProcess를 한번 실행시켜보겠습니다.
notepad.exe화면은 보이지 않지만 프로세스는 생성되었습니다. 즉 아직 EP(Entry Point)코드가 실행되지 않은 상태라고 볼 수 있습니다.
위의 ntdll.ZwCreateUserProcess을 실행시킨 후 계속 아래로 진행하면...............
ntdll.ZwResumeThread() API 호출 코드를 볼 수 있습니다. ntdll.ZwResumeThread()는 함수 이름 그대로 스레드를 Resume해줍니다. 이 스레드가 바로 자식 프로세스(notepad.exe)의 메인 스레드입니다. 따라서 이 API가 호출되면 비로소 자식 프로세스의 EP코드가 실행되는 것 입니다.
보시는 것처럼 notepad.exe가 EP코드가 실행되어 프로그램 창이 띄어진 것을 확인 할 수 있습니다.
지금까지의 CreateProcessW() API 호출 흐름을 정리해보도록 하겠습니다.
kernel32.CreateProcessW
kernel32.CreateProcessInternalW
ntdll.ZwCreateUserProcess //프로세스 생성됨(메인 스레드는 Suspend 상태
ntdll.ZwResumeThread //메인 스레드 Resume 시킴(프로세스가 실행됨)
자식 프로세스 생성에 있어서 가장 마지막에 호출되는 API가 바로 ntdll.ZwResumeThread() 입니다. 따라서 우리는 이 API를 후킹하여 자식 프로세스의 EP코드가 실행되기 바로직전에 제어를 가로챈 후 원하는 API를 후킹시킬 수 있습니다.
이 4개의 API (CreateProcessW, CreateProcessInternalW, ZwCreateUserprocess, ZwResumeThread) 중에서 어떤 걸 후킹해도 우리 목적에 맞는 글로벌 후킹이 가능합니다.
"리버싱 핵심원리" 에서는 ntdll.ZwResumeThread() API 후킹을 실습했기 때문에 그걸 토대로 저도 실습을 하겠습니다.
출처, 참고: 리버싱핵심원리-이승원 지음
0 개의 댓글