Windows 7 (& Vista) 에 맞는 InjectDll.exe 를 개발하고 소스 코드를 살펴보겠습니다.
이전 포스트에서 이어지는 내용입니다.
* 참고!
모든 소스 코드는 MS Visual C++ 2008 Express Edition SP1 으로 개발 되었으며, Windows 7 (32bit) & XP SP3 (32bit) 환경에서 테스트 되었습니다.
지난 내용 정리
시작하기 전에 먼저 지난 시간에 소개해 드린 내용을 짧게 정리해 보겠습니다.
Windows 7 (or Vista) 에서는 Session 관리 정책이 변경됨에 따라서 kernel32!CreateRemoteThread() API 내부 구현이 변경되었습니다.
☞ 참고 : Session in Windows 7
그 결과 CreateRemoteThread() 를 사용한 DLL Injection 기술이 Windows 7 (or Vista) 의 서비스 프로세스(Session 0) 에게는 정상적으로 동작하지 않습니다.
Kernel32!CreateRemoteThread() 를 디버깅 한 결과 원인은 API 내부에서 리모트 스레드를 생성할 때 suspend 모드로 생성하는데, 만약 리모트 프로세스가 Session 0 라면 resume 시키지 않고 그냥 에러를 리턴하기 때문이었습니다.
* 참고!
리모트 스레드를 생성할 때 일단 suspend 모드로 생성시킨 후 resume 시키는 구현 방법은 예전 XP 때부터 사용되던 방식입니다.
Kernel32!CreateRemoteThread() API 내부에서 호출되는 ntdll!ZwCreateThreadEx() API 의 파라미터를 조작 하거나 에러 조건 분기를 강제로 변경하면 정상적으로 리모트 스레드가 생성되면서 DLL Injection 이 성공하는 것을 확인 하였습니다.
InjectDll.exe
Windows 7 에서는 kernel32!CreateRemoteThread() 를 호출하는 것보다는 ntdll!ZwCreateThreadEx() API 를 직접 호출하는 것이 더 좋은 방법임을 알게 되었습니다.
* 주의!
ntdll!ZwCreateThreadEx() API 는 Vista 이후부터 추가된 API 이므로 XP 이하에서는 계속 CreateRemoteThread() 를 사용해야 합니다.
새로 알게 된 지식을 바탕으로 InjectDll.exe 프로그램을 새롭게 만들었습니다.
새롭게 프로그래밍한 InjectDll() 함수를 살펴보겠습니다.
typedef DWORD (WINAPI *PFNTCREATETHREADEX)
(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID Unknown
);
BOOL IsVistaOrLater()
{
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
// 커널 버전이 6 이상인지 확인!
if( osvi.dwMajorVersion >= 6 )
return TRUE;
return FALSE;
}
BOOL MyCreateRemoteThread
(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
{
HANDLE hThread = NULL;
FARPROC pFunc = NULL;
// OS 가 Vista 이상인지 확인!
if( IsVistaOrLater() ) // Vista, 7, Server2008
{
pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx");
if( pFunc == NULL )
{
printf("GetProcAddress(\"NtCreateThreadEx\") failed!!! [%d]\n",
GetLastError());
return FALSE;
}
// NtCreateThreadEx() 호출
((PFNTCREATETHREADEX)pFunc)(&hThread,
0x1FFFFF,
NULL,
hProcess,
pThreadProc,
pRemoteBuf,
FALSE,
NULL,
NULL,
NULL,
NULL);
if( hThread == NULL )
{
printf("NtCreateThreadEx() failed!!! [%d]\n", GetLastError());
return FALSE;
}
}
else // 2000, XP, Server2003
{
hThread = CreateRemoteThread(hProcess,
NULL,
0,
pThreadProc,
pRemoteBuf,
0,
NULL);
if( hThread == NULL )
{
printf("CreateRemoteThread() failed!!! [%d]\n", GetLastError());
return FALSE;
}
}
if( WAIT_FAILED == WaitForSingleObject(hThread, INFINITE) )
{
printf("WaitForSingleObject() failed!!! [%d]\n", GetLastError());
return FALSE;
}
return TRUE;
}
BOOL InjectDll(DWORD dwPID, char *szDllName)
{
HANDLE hProcess = NULL;
LPVOID pRemoteBuf = NULL;
FARPROC pThreadProc = NULL;
DWORD dwBufSize = strlen(szDllName)+1;
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
printf("OpenProcess(%d) failed!!! [%d]\n",
dwPID, GetLastError());
return FALSE;
}
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
dwBufSize, NULL);
pThreadProc = GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"LoadLibraryA");
if( !MyCreateRemoteThread(hProcess, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf) )
{
printf("CreateRemoteThread() failed!!! [%d]\n", GetLastError());
return FALSE;
}
VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
CloseHandle(hProcess);
return TRUE;
}
InjectDll() 함수의 변경된 사항은 바로 kernel32!CreateRemoteThread() 를 직접 호출하지 않고MyCreateRemoteThread() 사용자 함수를 호출 한다는 것입니다.
MyCreateRemoteThread() 함수 내부에서 OS 버전을 구해서 Vista 이상이라면 ntdll!NtCreateThreadEx() 를 호출하고, XP 이하라면 kernel32!CreateRemoteThread() 를 호출하도록 하였습니다.
간단한 코드이므로 쉽게 이해하실 수 있으실 겁니다.
* 참고!
유저 모드에서 ntdll.dll 라이브러리의 NtCreateThreadEx() 와 ZwCreateThreadEx() API 는 사실 같은 함수입니다. (두 함수의 시작 주소는 동일합니다.) 커널 모드(ntoskrnl.exe) 에서는 두 함수가 서로 틀려집니다. 향후 커널 모드 디버깅을 설명할 때 다시 설명 드리겠습니다. 일단 유저 모드에서는 NtXXX() 와 ZwXXX() 는 같다는 것만 기억해 주시기 바랍니다.
테스트
dummy.dll 파일은 지난 시간에 사용된 것과 동일한 파일로써 인젝션이 성공하면 디버그 로그를 출력하는 기능을 가지고 있습니다.
테스트를 위해서 적당한 서비스 프로세스를 골라 봅니다.
<Fig. 1>
그리고 아래와 같이 InjectDll.exe 를 실행시켜 주세요.
<Fig. 2>
Process Explorer 로 svchost.exe (PID:612) 를 확인해 보시면 dummy.dll 이 인젝션 되어 있는 걸 확인 하실 수 있습니다.
<Fig. 3>
이제 서비스 프로세스(Session 0) 에도 무리 없이 DLL Injection 을 할 수 있게 되었습니다.
* 참고!
ntdll!NtCreateThreadEx() API 는 undocument API 입니다. 따라서 MicroSoft 에서 사용을 권장하지 않을뿐더러 시스템의 안정성을 보장할 수 없습니다. 제가 테스트 해본 바로는 잘 동작하였지만 언제든지 MS 에서 패치를 시킬 가능성도 있습니다. 따라서 업무용으로 이 방법을 따라 하시는 분들께서는 꼭 이러한 사항을 염두에 두시기 바랍니다.
이상으로 Windows 7 에서 DLL Injection 하는 방법에 대한 설명을 마치도록 하겠습니다.
ReverseCore
위 글이 도움이 되셨다면 추천(VIEW ON) 부탁 드려요~
'study' 카테고리의 다른 글
| Code Injection 기법 (1) (8) | 2010/06/22 |
|---|---|
| Advanced Global API Hooking – IE 접속 제어 (4) (27) | 2010/05/17 |
| Advanced Global API Hooking – IE 접속 제어 (3) (10) | 2010/05/07 |
| Advanced Global API Hooking – IE 접속 제어 (2) (10) | 2010/04/25 |
| Advanced Global API Hooking – IE 접속 제어 (1) (21) | 2010/03/29 |
| DLL Injection in Windows 7 (3) (32) | 2010/02/23 |
| DLL Injection in Windows 7 (2) (15) | 2010/02/21 |
| DLL Injection in Windows 7 (1) (8) | 2010/02/20 |
| Session in Windows 7 (14) | 2010/02/14 |
| 실행 파일에서 ".reloc" 섹션 제거하기 (11) | 2010/01/27 |
| ASLR(Address Space Layout Randomization) (20) | 2010/01/27 |
7,
CreateRemoteThread,
dll,
DLL Injection,
it,
NtCreateThreadEx,
OllyDbg,
Reverse Code Engineering,
Reversing,
Service,
Session,
Session 0 Isolation,
VISTA,
windows,
리버스 엔지니어링,
리버싱,
서비스,
세션,
소프트웨어 역공학
Trackback Address :: http://www.reversecore.com/trackback/75
- Tracked from ♡바다.. 의 일상이야기 2010/08/10 12:02 삭제
Subject: CopyPwd.exe 파일을 Windows Server 2008 에서도 사용해보자!!
오랫만에 포스팅을 하는 듯 합니다. 서버 관리자 분들이면 꽤나 많이 사용하시는 소프트웨어가 CopyPwd.exe 파일입니다. 파일명에서도 보듯이 사용자의 패스워드를 해쉬값으로 변환해서 txt 파일로 저장 및 복원해주는 서버 관리자 분들에게는 없어서는 안될 아주 좋은 Command 창 소프트웨어 입니다. CopyPwd.exe 제조사 방문하기 하지만!! 제가 이번에 Windows Server 2008 을 설치하면서 난관에 봉착했습니다. 그것은 이 Cop..
InjectDll.cpp
dummy.dll
InjectDll.exe