CreateRemoteThread를 이용한 DLL Injection
DLL Injection을 하는 방법중에 하나로 RemoteThread를 생성하여 타겟프로세스에 원하는 DLL을 로드 하는 방법이 있습니다. 이 방법은 가장 간단하면서도 널리 쓰이는 방법 중에 하나입니다. CreateRemoteThread
API를 이용하여 다른 프로세스에 쓰레드를 생성 할 수 있습니다.
CreateRemoteThread
를 MSDN에서 찾아보면 Remarks에 다음과 같은 내용을 볼 수 있습니다.
Terminal Services isolates each terminal session by design. Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process.
번역을 해보자면 터미널 서비스 프로스 들은 세션이 다르게 지정되어 실행되기 때문에 만약 타겟 프로세스가 다른 세션에서 실행중일 때 실패 한다는 이야기 입니다. Windows Vista 이후 즉 NT커널 6.x이후로 사용자 응용프로그램과 서비스 등이 실행되는데 세션이라는 개념이 도입되었습니다.
이렇게 널리 쓰이던 방식인 CreateRemoteThread
를 이용한 DLL Injection에 위기가 왔지만 사람들은 더 하위 함수인 NtCreateThreadEx
라는 함수를 사용하여 대체 할 수 있음을 알아내었습니다. 하지만 NtCreateThreadEx
는 문서화 되지 않은 함수로 언제 갑자기 호출이 불가해도 할말이 없는 API였습니다. 아무튼 Windows 7에서부터 CreateRemoteThread
로 다른 세션에 DLL Injection을 수행하면 0x08이라는 LastError를 리턴하면서 실패 하였지만 NtCreateThreadEx
함수를 통해 DLL Injection을 성공 시킬 수 있었습니다.
세월이 흘러 Windows 10이 출시되고 NtCreateThreadEx
로 DLL Injectino이 불가능 하다는 소리가 또 터져 나오기 시작했습니다. 해당글 들은 구글에서 검색만 해봐도 많은 글들을 볼 수 있었습니다. NtCreateThreadEx를 대체 하여 RtlCreateUserThread를 사용하라고 하고 있습니다.
직접 테스트를 해봤습니다.
다음은 DLL Injection을 수행하는 예시 코드입니다.
BOOL DLLInjectByRemoteThread(DWORD dwPID, LPCWSTR lpszDLLPath )
{
HMODULE hKernel32 = NULL;
HMODULE hNTDLL = NULL;
FARPROC lpfnLoadLibrary = NULL;
pfnNtCreateThreadEx fpNtCreateThread = NULL;
HANDLE hTargetProc = NULL;
LPVOID dllPathAlloc = NULL;
BOOL bSuccess = TRUE;
HANDLE hThread = NULL;
if (wcslen(lpszDLLPath) == 0)
{
bSuccess = FALSE;
goto EXIT;
}
if (!PathFileExists(lpszDLLPath))
{
bSuccess = FALSE;
OutputDebugString(_T("Error: DLL File is not exists"));
goto EXIT;
}
hTargetProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (!hTargetProc)
{
bSuccess = FALSE;
OutputDebugString(_T("Error: fail to open Target process "));
goto EXIT;
}
dllPathAlloc = VirtualAllocEx(hTargetProc, NULL, sizeof(WCHAR) * MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (dllPathAlloc == NULL)
{
OutputDebugString(_T("Error: VirtualAllocEx "));
bSuccess = FALSE;
goto EXIT;
}
if (!WriteProcessMemory(hTargetProc, dllPathAlloc, lpszDLLPath, sizeof(WCHAR) * wcslen(lpszDLLPath) +sizeof(WCHAR), NULL))
{
OutputDebugString(_T("Error: WriteProcessMemory "));
bSuccess = FALSE;
goto EXIT;
}
hKernel32=LoadLibrary(L"Kernel32.dll");
if (!hKernel32)
{
OutputDebugString(_T("Error: LoadLibrary Kernel32.dll "));
bSuccess = FALSE;
goto EXIT;
}
lpfnLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryW");
if (!lpfnLoadLibrary)
{
OutputDebugString(_T("Error: GetProcAddress LoadLibraryW "));
bSuccess = FALSE;
goto EXIT;
}
hThread = CreateRemoteThread(hTargetProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpfnLoadLibrary, dllPathAlloc, 0, NULL);
if (hThread == NULL)
{
OutputDebugString(_T("Error: CreateRemoteThread fail "));
DWORD dwError = GetLastError();
if (dwError == 5)
{
OutputDebugString(_T("Error: CreateRemoteThread GetLastError 5 "));
bSuccess = FALSE;
goto EXIT;
}
if (dwError == 8)
{
OutputDebugString(_T("Error: CreateRemoteThread GetLastError 8 "));
hNTDLL = LoadLibrary(L"ntdll.dll");
if (!hNTDLL)
{
bSuccess = FALSE;
goto EXIT;
}
CLIENT_ID cid;
pfnRtlCreateUserThread RtlCreateUserThread = (pfnRtlCreateUserThread)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlCreateUserThread");
if (RtlCreateUserThread)
{
NTSTATUS status = 0;
status = RtlCreateUserThread(hTargetProc, NULL, FALSE, 0, 0, 0, (LPTHREAD_START_ROUTINE)lpfnLoadLibrary, dllPathAlloc, &hThread, &cid);
if (NT_SUCCESS(status) && hThread != NULL)
{
OutputDebugString(_T("success RtlCreateUserThread"));
}
else
{
OutputDebugString(_T("fail RtlCreateUserThread"));
bSuccess = FALSE;
goto EXIT;
}
}
else
{
bSuccess = FALSE;
goto EXIT;
}
}
}
OutputDebugString(_T("success inject"));
if (hThread)
{
WaitForSingleObject(hThread, INFINITE);
}
EXIT:
if (dllPathAlloc)
{
VirtualFreeEx(hTargetProc, dllPathAlloc, 0, MEM_RELEASE);
}
if (hTargetProc)
{
CloseHandle(hTargetProc);
}
if (hThread)
{
CloseHandle(hThread);
}
if (hNTDLL)
{
FreeLibrary(hNTDLL);
}
if (hKernel32)
{
FreeLibrary(hKernel32);
}
return bSuccess;
}
일단 Windows 7 SP1 x64에서 테스트 해보았습니다.
첫번째 시도 CreateRemoteThread를 이용한 Injection에서는 GetLastError 8을 뱉으며 인젝션에 실패 하고 다시 RtlCreateRemoteThread로 시도하면서 다시 성공합니다. 즉 분명 Windows7에서는 CreateRemoteThread로 다른세션의 프로세스에 인젝션이 되지 않습니다.
다시 Windows 10 x64 1057버전에서 시행해 보았습니다.
Windows 10에서는 CreateRemoteThread함수를 통해 바로 인젝션이 됩니다.
물론 OS가 보호하거나 보호 드라이버 등으로 보호된 프로세스는 인젝션이 불가능 합니다. 하지만 MSDN의 설명과 다르게 최신 Windows10의 경우 인젝션이 예전 XP에서 처럼 CreateRemoteThread로 Injection이 가능 했습니다. 또한 NtCreateThread, RtlCreateUserThread를 통해서도 Injection이 가능했습니다. MS가 이러한 사항을 MSDN에 반영을 하지 않은 것인지, 버그 인지, 혼란만 가중 시키고 있습니다.
샘플소스
https://github.com/lucidmaj7/DLLInjection
참고
https://eram.tistory.com/entry/DLL-Injection-3-Injector-%EC%82%AC%EC%9A%A9
https://gwangyi.github.io/posts/dll-injection-by-createremotethread/
'개발 > Windows' 카테고리의 다른 글
세션0(session 0) 격리 윈도우 서비스에 미치는 영향 (0) | 2020.04.07 |
---|---|
Memory Mapped File (MMF) Global로 명명할 때 권한 / winapi (0) | 2020.04.03 |
Hyper-V 2세대 VM에서 Com Port 활성화 하기 / Windbg (0) | 2020.03.27 |
UINT32[4] IPv6를 스트링으로 변환하기 / MFC, C++ (0) | 2020.02.19 |
DeviceIoControl에서 Overlapped I/O 사용하기 (4) | 2020.02.18 |
댓글