본문 바로가기

High Level Technique/Reversing

DLL Injection in kernel 6

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, 0x1FFFFFNULL, hProcess, pThreadProc, pRemoteBuf, FALSE, NULLNULLNULLNULL);
        if( hThread == NULL )
        {
            printf("MyCreateRemoteThread() : NtCreateThreadEx() failed!!! [%d]\n", GetLastError());
            return FALSE;
        }
    }
    else                    // Kernel Version 6.0 이전
    {
        hThread = CreateRemoteThread(hProcess, NULL0, pThreadProc, pRemoteBuf, 0NULL);
        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를 사용하게되면 해당 크기에 맞춰 사용하게 됩니다.



typedef SIZE_T (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 
); 






역시 우리 갓 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, NULLNULL);
        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, NULLfalse000, 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