DLL Injection in kernel 6
kernel version 6 이후부터 CreateRemoteThread()가 사실상 사용이 불가능한데, 이 API를 대신해서 ZwCreateThreadEx() API를 사용하면 Injection이 된다고 합니다.
Windows 10 x64 환경에서 소스코드를 작성하여 실행해 보도록 하겠습니다.
<Injectorx64.cpp>
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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #include <windows.h> #include <stdio.h> #include <tchar.h> BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { _tprintf(L"OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if( !LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid) ) // receives LUID of privilege { _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if( bEnablePrivilege ) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ) { _tprintf(L"The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } 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); if( osvi.dwMajorVersion >= 6 ) return TRUE; return FALSE; } BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf) { HANDLE hThread = NULL; FARPROC pFunc = NULL; if( IsVistaOrLater() ) // Kernel Version 6.0 이후 { pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); if( pFunc == NULL ) { printf("MyCreateRemoteThread() : GetProcAddress(\"NtCreateThreadEx\") failed!!! [%d]\n", GetLastError()); return FALSE; } ((PFNTCREATETHREADEX)pFunc)(&hThread, 0x1FFFFF, NULL, hProcess, pThreadProc, pRemoteBuf, FALSE, NULL, NULL, NULL, NULL); if( hThread == NULL ) { printf("MyCreateRemoteThread() : NtCreateThreadEx() failed!!! [%d]\n", GetLastError()); return FALSE; } } else // Kernel Version 6.0 이전 { hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); if( hThread == NULL ) { printf("MyCreateRemoteThread() : CreateRemoteThread() failed!!! [%d]\n", GetLastError()); return FALSE; } } if( WAIT_FAILED == WaitForSingleObject(hThread, INFINITE) ) { printf("MyCreateRemoteThread() : 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("[ERROR] 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("[ERROR] MyCreateRemoteThread() failed!!!\n"); return FALSE; } VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hProcess); return TRUE; } int main(int argc, char *argv[]) { // adjust privilege SetPrivilege(SE_DEBUG_NAME, TRUE); // InjectDll.exe <PID> <dll_path> if( argc != 3 ) { printf("usage : %s <PID> <dll_path>\n", argv[0]); return 1; } if( !InjectDll((DWORD)atoi(argv[1]), argv[2]) ) { printf("InjectDll() failed!!!\n"); return 1; } printf("InjectDll() succeeded!!!\n"); return 0; } | cs |
위 사진을 보면 NtCreateThreadEx()가 실행이 되지 않습니다.
그래서 Windows 7 x86 환경에서 시도해 봤습니다.
Windows7 에서는 Injection이 제대로 되었습니다.
이상하게 Windows10에서 실행을 했을 때는 되지 않았을까요??
NtCreateRemoteThreadEx()가 undocument API로 Microsoft에서 직접 호출을 권장하지 않고, 시스템의 안정성 또한 보장할 수 없습니다. MS에서 패치를 진행하지 않았나 싶습니다.
구글검색을 통해서 한참 동안 찾아본 결과 http://alkasis.tistory.com/8 에서 해결방안을 써두셨습니다.
기본 런타임 검사 부분을 기본값으로 하라고 했는데 저는 애초에 기본값으로 되어있었습니다. 다른 방법을 찾아봐야 할 것 같네요.
64비트 프로그램을 디버깅해서 DLL을 찾아본 결과 NtCreateThread는 존재하지 않았고 RtlCreateUserThread는 존재했습니다.
이를 가지고 Injector를 만들면 될 것 같습니다.
수정: ntcreatethread가 ntdll에 존재합니다.
RtlCreateUserThread가 NtCreateThread의 wapper이기 때문에 결국 들어갈때도 NtCreateThread를 사용할것입니다.
한참동안 찾아보다가 64bit에서 크기가 다르다는 것을 깨닫고 SIZE_T를 사용해야 한다는 것을 알게되었습니다. 갓ERAM
위 글은 ntcreatethread에 대한 내용은 아니지만 64비트에 관한 내용이고, SIZE_T를 사용하게되면 해당 크기에 맞춰 사용하게 됩니다.
역시 우리 갓 ERAM이 Injector를 만들었습니다. RtlCreateUserThread를 이용해야 합니다.
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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | #include<stdio.h> #include<Windows.h> #include<tchar.h> #include<sal.h> #define DLL_PATH argv[2] typedef struct _CLIENT_ID { PVOID UniqueProcess; PVOID UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef NTSTATUS(WINAPI *PROC_RtlCreateUserThread)( HANDLE ProcessHandle, PSECURITY_DESCRIPTOR SecurityDescriptor, BOOLEAN CreateSuspended, ULONG StackZeroBits, SIZE_T StackReserve, SIZE_T StackCommit, PTHREAD_START_ROUTINE StartAddress, PVOID Parameter, PHANDLE ThreadHandle, PCLIENT_ID ClientId ); BOOL SetPrivilege(_In_z_ const wchar_t* privilege, _In_ bool enable) { HANDLE token = INVALID_HANDLE_VALUE; if (TRUE != OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &token)) { if (ERROR_NO_TOKEN == GetLastError()) { if (ImpersonateSelf(SecurityImpersonation) != TRUE) { return FALSE; } if (TRUE != OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &token)) { return FALSE; } } else { return FALSE; } } TOKEN_PRIVILEGES tp = { 0 }; LUID luid = { 0 }; DWORD cb = sizeof(TOKEN_PRIVILEGES); bool ret = false; do { if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { break; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (enable) { tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; } else { tp.Privileges[0].Attributes = 0; } AdjustTokenPrivileges(token, FALSE, &tp, cb, NULL, NULL); if (GetLastError() != ERROR_SUCCESS) { break; } ret = true; } while (false); CloseHandle(token); return ret; } HANDLE AdvancedOpenProcess(_In_ DWORD pid) { HANDLE ret = NULL; if (TRUE != SetPrivilege(L"SeDebugPrivilege", true)) return ret; do { ret = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (NULL == ret) break; if (TRUE != SetPrivilege(L"SeDebugPrivilege", false)) { CloseHandle(ret); ret = NULL; break; } } while (false); return ret; } BOOL myRtlCreateUserThread(_In_ HANDLE process_handle, _In_ TCHAR *buffer, _In_ SIZE_T buffer_size) { HMODULE ntdll = NULL; HMODULE kernel32 = NULL; HANDLE thread_handle =NULL; CLIENT_ID cid; PROC_RtlCreateUserThread RtlCreateUserThread = NULL; PTHREAD_START_ROUTINE start_address = NULL; __try { ntdll = LoadLibrary(L"ntdll.dll"); if (NULL == ntdll) { _tprintf(_T("LoadLibrary(ntdll.dll) Func err gle : 0x%08X"), GetLastError()); return false; } RtlCreateUserThread = (PROC_RtlCreateUserThread)GetProcAddress(ntdll, "RtlCreateUserThread"); if (NULL == RtlCreateUserThread) { _tprintf(_T("GetProcAddress(RtlCreateUserThread) Func err gle : 0x%08X"), GetLastError()); return false; } kernel32 = LoadLibrary(L"kernel32.dll"); if (NULL == kernel32) { _tprintf(_T("LoadLibrary(kernel32.dll) Func err gle : 0x%08X"), GetLastError()); return false; } start_address = (PTHREAD_START_ROUTINE)GetProcAddress(kernel32, "LoadLibraryW"); if (NULL == start_address) { _tprintf(_T("GetProcAddress(LoadLibraryW) Func err gle : 0x%08X"), GetLastError()); return false; } NTSTATUS status = RtlCreateUserThread(process_handle, NULL, false, 0, 0, 0, start_address, buffer, &thread_handle, &cid); if (status > 0) { _tprintf(L"RtlCreateUserThread failed (0x%08x) status : %x\n", GetLastError(), status); return false; } status = WaitForSingleObject(thread_handle, INFINITE); if (status == WAIT_FAILED) { _tprintf(L"WaitForSingleObject failed (0x%08x) status : %x\n", GetLastError(), status); return false; } } __finally { if(kernel32 != NULL) FreeLibrary(kernel32); if(ntdll != NULL) FreeLibrary(ntdll); if(thread_handle != NULL) CloseHandle(thread_handle); } return true; } BOOL InjectThread(_In_ DWORD pid, _In_ const TCHAR* dll_path) { HANDLE process_handle = NULL; SIZE_T buffer_size = 0; TCHAR *buffer = NULL; SIZE_T byte_written = 0; __try { process_handle = AdvancedOpenProcess(pid); if (NULL == process_handle) { _tprintf(_T("OpenProcess Func err gle : 0x%08X"), GetLastError()); return false; } buffer_size = _tcslen(dll_path) * sizeof(TCHAR) + 1; buffer = (TCHAR*)VirtualAllocEx(process_handle, NULL, buffer_size, MEM_COMMIT, PAGE_READWRITE); if (NULL == buffer) { _tprintf(_T("VirtualAllocEx Func err gle : 0x%08X"), GetLastError()); return false; } if (TRUE != WriteProcessMemory(process_handle, buffer, dll_path, buffer_size, &byte_written)) { _tprintf(_T("WriteProcessMemory Func err gle : 0x%08X"), GetLastError()); return false; } if (TRUE != myRtlCreateUserThread(process_handle, buffer, buffer_size)) { _tprintf(_T("myRtlCreateUserThread Func err gle : 0x%08X"), GetLastError()); return false; } } __finally { if (buffer != NULL) VirtualFreeEx(process_handle, buffer, buffer_size, MEM_COMMIT); if (process_handle != NULL) CloseHandle(process_handle); } return true; } int _tmain(int argc, TCHAR* argv[]) { if (argc != 3) { _tprintf(_T("Usage: <PID> <DLL_PATH>\n")); return 0; } if (InjectThread(_ttoi(argv[1]) , DLL_PATH)) _tprintf(_T("Injection \"%s\" Success\n"), DLL_PATH); else _tprintf(_T("Injection \"%s\" Failed\n"), DLL_PATH); return 0; } | cs |
'High Level Technique > Reversing' 카테고리의 다른 글
Thread Environment Block (TEB) (0) | 2016.08.02 |
---|---|
Thread Local Storage CallBack (TLS) (0) | 2016.08.01 |
Session in Kernel 6 (0) | 2016.07.26 |
Address Space Layout Randomization (ASLR) (0) | 2016.07.26 |
WinDBG명령어와 분석방법 (0) | 2016.07.26 |