首页
社区
课程
招聘
[原创]CreateRemoteThread和CreateThread 流程逆向分析从R3到R0
2022-2-3 22:15 16745

[原创]CreateRemoteThread和CreateThread 流程逆向分析从R3到R0

2022-2-3 22:15
16745

目录

分析目标:

  1、分析Win10 64位CreateRemoteThread具体流程。
  2、分析为什么无法直接在系统进程中创建远程线程。


一、函数调用流程概述

通过分析整个函数调用流程可以得到这么几个结果:
  1、创建远程线程和普通线程没有本质的区别,其调用的函数都是相同的,唯一的区别就是在进程句柄上,进程句柄为-1是创建普通线程。调用的函数根据这个区别就可以对两种线程分别处理了。
  2、在用户模式下执行的代码主要还是为了在进入内核模式前准备所需的各种信息参数。在内核模式中的代码最为重要,是完成线程创建的关键代码。
  3、在CreateRemoteThead调用的整个流程中没有对系统进程进行权限的检查,所以可以认为权限检查的关键代码在OpenProcess中。如果需要在系统进程中创建远程线程就必须在获取进程句柄前进行提权处理。提权方案:

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
//在OpenProcess前调用即可
BOOL KtSetDebugPrivilege(HANDLE ProcessHandle, BOOL IsEnable)
{
    DWORD  LastError = 0;
    HANDLE TokenHandle = 0;
 
    if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
    {
        LastError = GetLastError();
        if (TokenHandle)
        {
            CloseHandle(TokenHandle);
        }
        return LastError;
    }
    TOKEN_PRIVILEGES TokenPrivileges;
    memset(&TokenPrivileges, 0, sizeof(TOKEN_PRIVILEGES));
    LUID v1;//权限类型,本地独有标识
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1))
    {
        LastError = GetLastError();
        CloseHandle(TokenHandle);
        return LastError;
    }
    TokenPrivileges.PrivilegeCount = 1;
    TokenPrivileges.Privileges[0].Luid = v1;
    if (IsEnable)
    {
        TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    }
    else
    {
        TokenPrivileges.Privileges[0].Attributes = 0;
    }
    AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
    LastError = GetLastError();
    CloseHandle(TokenHandle);
    return LastError;
}


 

二、函数分析详细报告:

1、用户模式Ring3:

函数调用流程分析:

  通过WinDbg查看函数CreateRemoteThead在用户模式下的调用流程,观察这个调用情况可以确定在用户模式下,这个函数涉及到了三个dll模块(KERNEL32、KERNELBASE、ntdll)。而CreateRemoteThead这个API在KERNEL32模块中真正的函数名是CreateRemoteThreadStub,通过这个KERNEL32中的CreateRemoteThreadStubAPI将参数转发到KERNELBASE模块中的CreateRemoteThreadEx中,然后在KERNELBASE中调用ntdll模块中的NtCreateThreadExAPI,进入内核。待内核处理结束后获取返回值,进行返回值的处理并返回结果。

 

流程图1

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
0:000> wt
Tracing KERNEL32!CreateRemoteThreadStub to return address 00007ff7`3f741220
   11     0 0] KERNEL32!CreateRemoteThreadStub
   32     0 1]   KERNELBASE!CreateRemoteThreadEx
   25     0 2]     KERNELBASE!BaseFormatObjectAttributes
   67    25 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     ntdll!NtDuplicateObject
>> More than one level popped 1 -> 1
   77    31 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     ntdll!NtQueryInformationProcess
>> More than one level popped 1 -> 1
   94    37 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     ntdll!NtQueryInformationProcess
>> More than one level popped 1 -> 1
  139    43 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     ntdll!NtCreateThreadEx
>> More than one level popped 1 -> 1
  158    49 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     ntdll!NtClose
>> More than one level popped 1 -> 1
  167    55 1]   KERNELBASE!CreateRemoteThreadEx
    6     0 2]     KERNELBASE!_security_check_cookie
  174    61 1]   KERNELBASE!CreateRemoteThreadEx
   14   235 0] KERNEL32!CreateRemoteThreadStub
 
249 instructions were executed in 248 events (0 from other threads)
 
Function Name                               Invocations MinInst MaxInst AvgInst
KERNEL32!CreateRemoteThreadStub                       1      14      14      14
KERNELBASE!BaseFormatObjectAttributes                 1      25      25      25
KERNELBASE!CreateRemoteThreadEx                       1     174     174     174
KERNELBASE!_security_check_cookie                     1       6       6       6
ntdll!NtClose                                         1       6       6       6
ntdll!NtCreateThreadEx                                1       6       6       6
ntdll!NtDuplicateObject                               1       6       6       6
ntdll!NtQueryInformationProcess                       2       6       6       6
 
5 system calls were executed
 
Calls  System Call
    1  ntdll!NtClose
    1  ntdll!NtCreateThreadEx
    1  ntdll!NtDuplicateObject
    2  ntdll!NtQueryInformationProcess

KERNEL32模块分析:

  用户调用CreateRemoteThreadAPI后,会进入KERNEL32模块中的CreateRemoteThreadStub,参数相同,而CreateRemoteThreadStub则会对CreateRemoteThread的参数进行一些处理,由原来的7个参数扩展到了8个参数,并且对dwCreationFlags参数进行了一些安全处理,规避了系统规定参数外的无效参数。然后将参数转发到KERNELBASE模块。

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
.text:000000018003AB20 ; HANDLE __stdcall CreateRemoteThreadStub(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
.text:000000018003AB20                 public CreateRemoteThreadStub
.text:000000018003AB20 CreateRemoteThreadStub proc near        ; DATA XREF: .rdata:0000000180085477↓o
.text:000000018003AB20                                         ; .rdata:off_18009A208↓o ...
.text:000000018003AB20
.text:000000018003AB20 lpParameter     = qword ptr  28h
.text:000000018003AB20 dwCreationFlags = dword ptr  30h
.text:000000018003AB20 lpThreadId      = qword ptr  38h
.text:000000018003AB20
.text:000000018003AB20                 mov     r11, rsp            ; r11存储原来的rsp用来传参
.text:000000018003AB23                 sub     rsp, 48h            ; 栈抬高,用来存储参数
.text:000000018003AB27                 mov     r10d, [rsp+48h+dwCreationFlags]   
.text:000000018003AB2C                 mov     rax, [rsp+48h+lpThreadId]
.text:000000018003AB34                 and     r10d, 10004h   
.text:000000018003AB3B                 mov     [r11-10h], rax            ;    参数8 = lpThreadId
.text:000000018003AB3F                 and     qword ptr [r11-18h], 0    ;    参数7(扩展参数) = 0
.text:000000018003AB44                 mov     rax, [rsp+48h+lpParameter]
.text:000000018003AB49                 mov     [r11-20h], r10d            ;    参数6 = dwCreationFlags & 0x10004h
.text:000000018003AB4D                 mov     [r11-28h], rax            ;    参数5 = lpParameter
.text:000000018003AB51                 call    cs:__imp_CreateRemoteThreadEx
.text:000000018003AB58                 nop     dword ptr [rax+rax+00h]
.text:000000018003AB5D                 add     rsp, 48h
.text:000000018003AB61                 retn
.text:000000018003AB61 ; ---------------------------------------------------------------------------
.text:000000018003AB62                 db 0CCh
.text:000000018003AB62 CreateRemoteThreadStub endp

KERNELBASE模块分析:

  这个API主要是为了给内核模式准备所需的参数的,所以这是执行CreateRemoteThread在用户模式下的最后一个API,然后就会调用ntdll模块中的NtCreateThreadEx进入内核模式执行内核代码,待内核代码执行完成后,将执行结果返回给用户模式,CreateRemoteThread就会获取执行结果,并将结果处理完毕后一层一层的返回给调用者。

函数流程图:

流程图3

函数分析:

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
HANDLE __stdcall CreateRemoteThreadEx(HANDLE a_hProcess, LPSECURITY_ATTRIBUTES a_lpThreadAttributes, SIZE_T a_dwStackSize, LPTHREAD_START_ROUTINE a_lpStartAddress, LPVOID a_lpParameter, DWORD a_dwCreationFlags, LPPROC_THREAD_ATTRIBUTE_LIST a_lpAttributeList, LPDWORD a_lpThreadId)
{
  int nRet; // eax
  DWORD v_nFlag; // er15
  char bFlag; // si
  NTSTATUS NtRet6; // eax
  NTSTATUS NtRet5; // ebx
  int v_bIsSuspendThread2; // edx
  SIZE_T v_dwStackSize1; // rax
  HMODULE v_hModule_csrsrv; // rax
  __int64 (__fastcall *v_lpfuncCsrCreateRemoteThread)(HANDLE, CLIENT_ID *); // rax
  NTSTATUS NtRet2; // eax
  NTSTATUS NtRet4; // eax
  NTSTATUS NtRet3; // ecx
  NTSTATUS NtRet1; // ebx
  int v23; // [rsp+A0h] [rbp-548h]
  char v_bIsSuspendThread1; // [rsp+B0h] [rbp-538h]
  char v_bflag; // [rsp+B1h] [rbp-537h]
  HANDLE v_ThreadHandle; // [rsp+B8h] [rbp-530h] BYREF
  __int64 v27; // [rsp+C0h] [rbp-528h] BYREF
  HANDLE v_hProcessHandle; // [rsp+C8h] [rbp-520h] BYREF
  PVOID Context[2]; // [rsp+D0h] [rbp-518h] BYREF
  PACTIVATION_CONTEXT_STACK Stack; // [rsp+E0h] [rbp-508h] BYREF
  int v31; // [rsp+E8h] [rbp-500h] BYREF
  PTEB Teb; // [rsp+F0h] [rbp-4F8h] BYREF
  ULONG SuspendCount; // [rsp+F8h] [rbp-4F0h] BYREF
  SIZE_T v_dwStackSize; // [rsp+100h] [rbp-4E8h]
  LPVOID v_lpParameter; // [rsp+108h] [rbp-4E0h]
  LPTHREAD_START_ROUTINE v_lpStartAddress; // [rsp+110h] [rbp-4D8h]
  __int64 v_ThreadAttributes; // [rsp+118h] [rbp-4D0h] BYREF
  CLIENT_ID v_tagClientID; // [rsp+120h] [rbp-4C8h] BYREF
  unsigned __int64 Cookie; // [rsp+130h] [rbp-4B8h] BYREF
  PROCESS_BASIC_INFORMATION v_tagProcessInformation; // [rsp+138h] [rbp-4B0h] BYREF
  SECTION_IMAGE_INFORMATION v_tagSectionImageinformation; // [rsp+170h] [rbp-478h] BYREF
  __int64 v42; // [rsp+1B0h] [rbp-438h] BYREF
  __int64 v_aryAttributeList[122]; // [rsp+1E0h] [rbp-408h] BYREF
  void *retaddr; // [rsp+5E8h] [rbp+0h]
 
//=========================================================================================
 
  v_lpStartAddress = a_lpStartAddress;
  v_dwStackSize = a_dwStackSize;
  v_lpParameter = a_lpParameter;
  v_bflag = 0;
  *(_OWORD *)Context = 0i64;
  Stack = 0i64;
  v_tagClientID = 0i64;
  v_bIsSuspendThread1 = 0;
  v_ThreadHandle = 0i64;
  if ( (a_dwCreationFlags & 0xFFFEFFFB) != 0 // 检查a_dwCreationFlags是否被传入了无效参数,如果参数无效返回错误 0xC000000D(STATUS_INVALID_PARAMETER)
  {
    NtRet3 = -1073741811;
FUNC_END_ERROR:
    BaseSetLastNTError(NtRet3);
    return 0i64;
  }
  nRet = BaseFormatObjectAttributes(&v42, a_lpThreadAttributes, 0i64, &v_ThreadAttributes);// 通过lpThreadAttributes参数格式化v_ThreadAttributes参数,为三环进0环做准备
  if ( nRet < 0 )                               // 检查返回值是否有效,小于0返回错误
    goto LABEL_49;
  v_aryAttributeList[1] = 65539i64;
  v_aryAttributeList[2] = 16i64;
  v_aryAttributeList[4] = 0i64;
  v_aryAttributeList[3] = (__int64)&v_tagClientID;
  v_aryAttributeList[5] = 65540i64;
  v_aryAttributeList[6] = 8i64;
  v_aryAttributeList[8] = 0i64;
  v_aryAttributeList[7] = (__int64)&Teb;
  LODWORD(v27) = 2;
  if ( a_lpAttributeList )                      // 判断是否有扩展参数列表,如果有扩展参数列表就使用BasepConvertWin32AttributeList进行转换
  {
    v23 = 30;
    nRet = BasepConvertWin32AttributeList(
             (int *)a_lpAttributeList,
             1,
             (__int64)&v31,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             0i64,
             (__int64)v_aryAttributeList,
             (__int64)&v27,
             v23);
    if ( nRet < 0 )                             // 检查是否转换成功,如果nRet<0返回失败
    {
LABEL_49:
      NtRet3 = nRet;
      goto FUNC_END_ERROR;
    }
  }
  v_aryAttributeList[0] = 32i64 * (unsigned int)v27 + 8;// 给index=0的位置赋值, 如果没有扩展参数该值为: 2*32 + 8 = 72
  v_nFlag = a_dwCreationFlags & 0x10000;        // 给标志赋值: INHERIT_PARENT_AFFINITY(继承)=1
                                                //            其他 = 0
  v_hProcessHandle = 0i64;
  bFlag = 1;
  if ( a_hProcess != (HANDLE)-1i64 )            // 判断句柄是否是-1
                                                // #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
  {
    if ( NtDuplicateObject(                     // 将进程句柄复制一份,为内核准备参数,并将句柄的引用数+1
           (HANDLE)0xFFFFFFFFFFFFFFFFi64,
           a_hProcess,
           (HANDLE)0xFFFFFFFFFFFFFFFFi64,
           &v_hProcessHandle,
           0x402u,
           0,
           0) >= 0 )
      a_hProcess = v_hProcessHandle;            // 如果句柄复制成功,传入内核
    NtRet1 = NtQueryInformationProcess(a_hProcess, ProcessBasicInformation, &v_tagProcessInformation, 0x30u, 0i64);// 检索指向可用于确定指定进程是否正在调试的 PEB 结构的指针,以及系统用于标识指定进程的唯一值。
    if ( NtRet1 >= 0 && (HANDLE)v_tagProcessInformation.UniqueProcessId != NtCurrentTeb()->ClientId.UniqueProcess )// 判断句柄是否属于当前的进程(用来区分是否是远程线程创建)
    {
      bFlag = 0;
      NtRet1 = NtQueryInformationProcess(
                 a_hProcess,
                 ProcessImageInformation,
                 &v_tagSectionImageinformation,
                 0x40u,
                 0i64);                         // 调用函数获取hProcess的SECTION_IMAGE_INFORMATION结构
      if ( NtRet1 >= 0 && v_tagSectionImageinformation.SubSystemType - 2 > 1 )
        NtRet1 = -1073741823;                   // 如果返回值正确,但是SubSystemType类型错误,返回0xC0000001‬(STATUS_UNSUCCESSFUL)
    }
    if ( NtRet1 < 0 )                           // 如果返回错误,关闭句柄,返回错误信息
    {
      if ( v_hProcessHandle )
        NtClose(v_hProcessHandle);
      NtRet3 = NtRet1;
      goto FUNC_END_ERROR;
    }
  }
  if ( bFlag )
  {
    NtRet6 = RtlQueryInformationActivationContext(1u, 0i64, 0i64, 1u, Context, 0x10ui64, 0i64);
    NtRet5 = NtRet6;
    if ( NtRet6 < 0 )
    {                                           // 函数返回值错误,释放资源并返回
      DbgPrint_0(
        "SXS: %s - Failing thread create because RtlQueryInformationActivationContext() failed with status %08lx\n",
        "CreateRemoteThreadEx",
        (unsigned int)NtRet6);
      goto RELEASE_RET;
    }
    if ( !g_bFlag && !NtCurrentTeb()->SubProcessTag && (!Context[0] || ((__int64)Context[1] & 1) != 0) )
    {
      v_bIsSuspendThread1 = 0;
      goto LABEL_11;
    }
    v_bIsSuspendThread1 = 1;
  }
  if ( !v_bIsSuspendThread1 )
  {
LABEL_11:
    v_bIsSuspendThread2 = 0;
    if ( (a_dwCreationFlags & 4) == 0 )
      goto LABEL_17;
  }
  v_bIsSuspendThread2 = 1;
LABEL_17:
  v31 = v_bIsSuspendThread2;
  v_dwStackSize1 = v_dwStackSize;
  if ( v_nFlag )
    v_dwStackSize1 = 0i64;
  NtRet5 = NtCreateThreadEx(                    // 进入内核创建线程
             &v_ThreadHandle,                   // _Out_ PHANDLE ThreadHandle,
             0x1FFFFFi64,                       // _In_ ACCESS_MASK DesiredAccess,  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL
             v_ThreadAttributes,                // _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
             a_hProcess,                        // _In_ HANDLE ProcessHandle,
             v_lpStartAddress,                  // _In_ PVOID StartRoutine,
             v_lpParameter,                     // _In_opt_ PVOID Argument,
             v_bIsSuspendThread2,               // _In_ ULONG CreateFlags,   挂起线程或者正常 THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001
             0i64,                              // _In_opt_ ULONG_PTR ZeroBits,
             v_dwStackSize1,                    // _In_opt_ SIZE_T StackSize,
             v_dwStackSize & -(__int64)(v_nFlag != 0),// _In_opt_ SIZE_T MaximumStackSize,  如果是通过父进程创建最大栈大小不变,否则为0
             v_aryAttributeList);               // _In_opt_ PVOID AttributeList
  if ( NtRet5 < 0 )
    goto RELEASE_RET;
  if ( !v_bIsSuspendThread1 )
    goto LABEL_21;
  if ( NtCurrentTeb()->SubProcessTag )
    Teb->SubProcessTag = NtCurrentTeb()->SubProcessTag;
  if ( Context[0] && ((__int64)Context[1] & 1) == 0 )
  {
    NtRet2 = RtlAllocateActivationContextStack(&Stack);// 分配可激活的线程上下文栈
    NtRet5 = NtRet2;
    if ( NtRet2 < 0 )
    {
      DbgPrint_0(
        "SXS: %s - Failing thread create because RtlAllocateActivationContextStack() failed with status %08lx\n",
        "CreateRemoteThreadEx",
        (unsigned int)NtRet2);
      goto RELEASE_RET;
    }
    Teb->ActivationContextStackPointer = Stack;
    NtRet4 = RtlActivateActivationContextEx(1u, Teb, Context[0], &Cookie);// 激活线程上下文
    NtRet5 = NtRet4;
    if ( NtRet4 < 0 )
    {
      DbgPrint_0(
        "SXS: %s - Failing thread create because RtlActivateActivationContextEx() failed with status %08lx\n",
        "CreateRemoteThreadEx",
        (unsigned int)NtRet4);
      goto RELEASE_RET;
    }
    v_bflag = 1;
  }
  if ( !g_bFlag
    || (v_hModule_csrsrv = GetModuleHandleA("csrsrv"),// 获取模块句柄
        (v_lpfuncCsrCreateRemoteThread = (__int64 (__fastcall *)(HANDLE, CLIENT_ID *))GetProcAddressForCaller(// 获取函数地址
                                                                                        v_hModule_csrsrv,
                                                                                        "CsrCreateRemoteThread",
                                                                                        retaddr)) == 0i64)
    || (NtRet5 = v_lpfuncCsrCreateRemoteThread(v_ThreadHandle, &v_tagClientID), NtRet5 >= 0) )// 调用函数,将线程句柄传入获取ClientID
                                                // CsrCreateRemoteThread例程为不属于当前NT进程的NT线程创建CSR线程对象
  {
LABEL_21:
    if ( a_lpThreadId )
      *a_lpThreadId = (DWORD)v_tagClientID.UniqueThread;// 获取线程ID
    if ( v_bIsSuspendThread1 && (a_dwCreationFlags & 4) == 0 )// CREATE_SUSPENDED 0x00000004
                                                // 创建线程时,挂起标志为1,并且标志参数表示不挂起,恢复线程状态,修改线程挂起数量
      NtResumeThread(v_ThreadHandle, &SuspendCount);
  }
RELEASE_RET:
  if ( Context[0] )
    RtlReleaseActivationContext(Context[0]);    // 释放线程上下文
  if ( v_hProcessHandle )                       // 关闭进程句柄
    NtClose(v_hProcessHandle);
  if ( NtRet5 < 0 )
  {
    if ( v_bflag && Context[0] )
      RtlReleaseActivationContext(Context[0]);
    if ( Stack )
      RtlFreeActivationContextStack(Stack);     // 释放栈空间
    if ( v_ThreadHandle )
    {
      NtTerminateThread(v_ThreadHandle, NtRet5);// 终止线程
      NtClose(v_ThreadHandle);
    }
    BaseSetLastNTError(NtRet5);                 // 设置NT错误信息
    v_ThreadHandle = 0i64;
  }
  return v_ThreadHandle;                        // 返回线程句柄
}

相关结构体及定义:

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
//0x40 bytes (sizeof)  未公共 https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1909%2019H2%20(November%202019%20Update)/_SECTION_IMAGE_INFORMATION
typedef struct _SECTION_IMAGE_INFORMATION
{
    VOID* TransferAddress;                                                  //0x0
    ULONG ZeroBits;                                                         //0x8
    ULONGLONG MaximumStackSize;                                             //0x10
    ULONGLONG CommittedStackSize;                                           //0x18
    ULONG SubSystemType;                                                    //0x20
    union
    {
        struct
        {
            USHORT SubSystemMinorVersion;                                   //0x24
            USHORT SubSystemMajorVersion;                                   //0x26
        };
        ULONG SubSystemVersion;                                             //0x24
    };
    union
    {
        struct
        {
            USHORT MajorOperatingSystemVersion;                             //0x28
            USHORT MinorOperatingSystemVersion;                             //0x2a
        };
        ULONG OperatingSystemVersion;                                       //0x28
    };
    USHORT ImageCharacteristics;                                            //0x2c
    USHORT DllCharacteristics;                                              //0x2e
    USHORT Machine;                                                         //0x30
    UCHAR ImageContainsCode;                                                //0x32
    union
    {
        UCHAR ImageFlags;                                                   //0x33
        struct
        {
            UCHAR ComPlusNativeReady:1;                                     //0x33
            UCHAR ComPlusILOnly:1;                                          //0x33
            UCHAR ImageDynamicallyRelocated:1;                              //0x33
            UCHAR ImageMappedFlat:1;                                        //0x33
            UCHAR BaseBelow4gb:1;                                           //0x33
            UCHAR ComPlusPrefer32bit:1;                                     //0x33
            UCHAR Reserved:2;                                               //0x33
        };
    };
    ULONG LoaderFlags;                                                      //0x34
    ULONG ImageFileSize;                                                    //0x38
    ULONG CheckSum;                                                         //0x3c
}SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;
 
//公开
typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG_PTR AffinityMask;
    KPRIORITY BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
 
 
// Subsystem Values in Microsoft SDK and avaiable in <winnt.h> header file.
 
#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
 
 
 
 
// begin_rev
#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001
#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002 // ?
#define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004
#define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010 // ?
#define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020 // ?
#define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080
// end_rev
#if (PHNT_VERSION >= PHNT_VISTA)
NTSYSCALLAPI
NTSTATUS
NTAPI
NtCreateThreadEx(
    _Out_ PHANDLE ThreadHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_ HANDLE ProcessHandle,
    _In_ PVOID StartRoutine, // PUSER_THREAD_START_ROUTINE
    _In_opt_ PVOID Argument,
    _In_ ULONG CreateFlags, // THREAD_CREATE_FLAGS_*
    _In_ SIZE_T ZeroBits,
    _In_ SIZE_T StackSize,
    _In_ SIZE_T MaximumStackSize,
    _In_opt_ PPS_ATTRIBUTE_LIST AttributeList
    );
#endif

NTDLL模块分析:

  从以下代码可以看出,在进入内核前需要判断标志位(地址:ds:7FFE0308h),如果最低位为1则使用syscall指令进入内核,否则使用int 2Eh中断进入内核。至于这个地址具体是干什么的待日后分析。从程序的执行流程可以知道,我们进入内核使用的是syscall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:000000018009D840 NtCreateThreadEx proc near              ; CODE XREF: RtlpCreateUserThreadEx+137↑p
.text:000000018009D840                                         ; DATA XREF: .rdata:0000000180118F60↓o ...
.text:000000018009D840                 mov     r10, rcx        ; NtCreateThreadEx
.text:000000018009D843                 mov     eax, 0BDh
.text:000000018009D848                 test    byte ptr ds:7FFE0308h, 1
.text:000000018009D850
.text:000000018009D850 IF_BEGIN:
.text:000000018009D850                 jnz     short IF_END
.text:000000018009D852                 syscall                 ; Low latency system call
.text:000000018009D854                 retn
.text:000000018009D855 ; ---------------------------------------------------------------------------
.text:000000018009D855
.text:000000018009D855 IF_END:                                 ; CODE XREF: NtCreateThreadEx:IF_BEGIN↑j
.text:000000018009D855                 int     2Eh             ; DOS 2+ internal - EXECUTE COMMAND       
.text:000000018009D855                                         ; DS:SI -> counted CR-terminated command string
.text:000000018009D857                 retn
.text:000000018009D857 NtCreateThreadEx endp

2、内核模式Ring0:

  当程序进入内核模式后,使用三环的调试器就会跑飞,完全不知道程序执行到了哪里,这时就需要使用WinDbg使用内核模式进行调试了。知道程序在内核中一定会进入NT模块下的NtCreateThreadEx内核函数,所以只需要在这里下进程断点等待调用就行。调用后就会得到该内核函数的调用栈,知道他进入内核都经过了哪些系统调用。盲猜nt!KiSystemServiceCopyEnd一定就是系统的分发函数了。
  以下就是该函数的调用栈:

流程图4

 

   可以清楚的看出从用户模式到内核模式的全过程:
流程图2
  在nt模块下查找KiSystemServiceCopyEnd这个内核函数时发现,这根本不是一个函数名,而是一个地址标签,是在一个叫nt!KiSystemCall64的内核函数中的地址标签。再看这个函数的调用方式是一个call寄存器的方式,也就说明rax寄存器是一个存放NtCreateThreadEx地址的寄存器,该值需要在KiSystemCall64中提前计算好才能使用,调用后就会成功进入目标内核函数。
流程图3
   在调用rax之前还有一段代码看起来像是在拷贝函数的栈空间,而且拷贝了14个,盲猜系统内核函数最大的参数数也就是14个参数了,这也就清楚为什么这个地址叫系统服务拷贝了。

 

流程图6

NtCreateThreadEx分析:

  这个函数因为内核功底基本没有,所以只能大概分析。首先检查参数判断是否是有效参数,然后判断线程句柄是否在一个正确的区间内,如果不在就修改句柄值。之后就是准备线程的上下文环境,通过句柄和tag获取线程对象。最后就是调用PspCreateThread开始正式创建线程。待结果返回后释放资源。 总的来说这个函数就是在内核模式为创建进程准备需要用到的参数,没有实际的功能。真正的创建是在PspCreateThread中,从该函数的前两个字母前缀就可以知道这个内核函数属性进程相关的内核函数。

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
__int64 __fastcall NtCreateThreadEx(PHANDLE a_hThread, ACCESS_MASK a_DesiredAccess, LPVOID a_pObjectAttributes, HANDLE a_hProcess, LPVOID a_lpStartAddress, LPVOID a_lpParameter, DWORD a_dwCreateSuspended, __int64 a_StackZeroBits, __int64 a_SizeOfStackCommit, __int64 a_SizeOfStackReserve, __int64 a_AttributeList)
{
  __int64 v_PreviousMode; // rdx
  __int64 NtRet; // rax
  __int64 v_XStateCompactionMask; // rdi
  unsigned int v_ContextFlags; // er14
  unsigned __int64 v17; // rax
  void *v18; // rsp
  __int64 v19; // r10
  unsigned int NtRet2; // ebx
  __int64 v_hThread; // rcx
  unsigned int v_Context; // [rsp+70h] [rbp+0h] BYREF
  int v_DesiredAccess; // [rsp+74h] [rbp+4h]
  PVOID Object; // [rsp+78h] [rbp+8h] BYREF
  __int64 v_pContextEx; // [rsp+80h] [rbp+10h] BYREF
  LPVOID v_lpParameter; // [rsp+88h] [rbp+18h]
  LPVOID v_pObjectAttributes; // [rsp+90h] [rbp+20h]
  char v28[80]; // [rsp+A0h] [rbp+30h] BYREF
  __int64 v_pCreateProcessContext[66]; // [rsp+F0h] [rbp+80h] BYREF
  char v30; // [rsp+300h] [rbp+290h] BYREF
  int v31; // [rsp+301h] [rbp+291h]
  __int16 v32; // [rsp+305h] [rbp+295h]
  char v33; // [rsp+307h] [rbp+297h]
  __int64 v34; // [rsp+308h] [rbp+298h]
  __int64 v35; // [rsp+310h] [rbp+2A0h]
  __int64 v36; // [rsp+318h] [rbp+2A8h]
 
  v_pObjectAttributes = a_pObjectAttributes;
  v_DesiredAccess = a_DesiredAccess;
  v_lpParameter = a_lpParameter;
  memset(v28, 0, 0x48ui64);
  Object = 0i64;
  v31 = 0;
  v32 = 0;
  v33 = 0;
  v_pContextEx = 0i64;
  v_Context = 0;
  if ( (a_dwCreateSuspended & 0xFFFFFF80) != 0 )// 检查参数是否有效
    return 0xC00000F5i64;
  if ( KeGetCurrentThread()->PreviousMode )     // 通过PreviousMode值修正线程句柄
  {
    v_hThread = (__int64)a_hThread;
    if ( (unsigned __int64)a_hThread >= 0x7FFFFFFF0000i64 )
      v_hThread = 0x7FFFFFFF0000i64;
    *(_QWORD *)v_hThread = *(_QWORD *)v_hThread;    //这句不是废话,条件赋值
  }
  v30 = 0;
  v35 = a_SizeOfStackCommit;
  v36 = a_SizeOfStackReserve;
  v34 = a_StackZeroBits;
  memset(v_pCreateProcessContext, 0, 0x208ui64);
  if ( !a_AttributeList
    || (LOBYTE(v_PreviousMode) = KeGetCurrentThread()->PreviousMode,
        NtRet = PspBuildCreateProcessContext(
                  a_AttributeList,
                  v_PreviousMode,
                  1i64,
                  v_pCreateProcessContext),     // 创建CreateProcessContext结构体
        (int)NtRet >= 0) )
  {
    NtRet = ObpReferenceObjectByHandleWithTag((ULONG_PTR)a_hProcess, 1917023056u, (__int64)&Object, 0i64, 0i64);// 通过带标记的句柄获取引用的对象
    if ( (int)NtRet >= 0 )
    {
      v_XStateCompactionMask = (*((_DWORD *)Object + 0x275) & 0x4000) != 0 ? 0x800 : 0;// 获取所需的标志
      v_ContextFlags = (*((_DWORD *)Object + 0x275) & 0x4000) != 0 ? 0x10004B : 0x10000B;
      ObfDereferenceObjectWithTag(Object, 1917023056u);// 通过tag标志引用对象,增加引用计数
      RtlGetExtendedContextLength2(v_ContextFlags, &v_Context, (unsigned int)v_XStateCompactionMask);// v_Context 是Context的大小
      v17 = v_Context + 15i64;
      if ( v17 <= v_Context )
        v17 = 0xFFFFFFFFFFFFFF0i64;
      v18 = alloca(v17 & 0xFFFFFFFFFFFFFFF0ui64);
      memset(&v_Context, 0, v_Context);
      RtlInitializeExtendedContext2(&v_Context, v_ContextFlags, &v_pContextEx, v_XStateCompactionMask);
      PspCreateUserContext(                     // 为线程执行的目标函数分配上下文环境
        (unsigned int)&v_Context,
        1,
        PspUserThreadStart,
        (_DWORD)a_lpStartAddress,
        (__int64)v_lpParameter);
      NtRet2 = PspCreateThread(                 // 调用PspCreateThread创建线程  创建重要部分
                 a_hThread,
                 v_DesiredAccess,
                 (__int64)v_pObjectAttributes,
                 (ULONG_PTR)a_hProcess,
                 0i64,
                 (__int64)v_pCreateProcessContext,
                 v_pCreateProcessContext[2],
                 (__int64)&v_Context,
                 (__int64)v28,
                 a_dwCreateSuspended,
                 (__int64)a_lpStartAddress,
                 v19,
                 (__int64)&v30);
      PspDeleteCreateProcessContext((__int64)v_pCreateProcessContext);// 释放CreateProcessContext结构体
      NtRet = NtRet2;
    }
  }
  return NtRet;
}

PspCreateThread猜想:

  在内核中进入了创建线程的关键节点,内核代码也越来越费解了,因此只能对这个内核函数进行一些合理的猜想假设。通过函数名获取了一些信息,可以知道操作线程需要上锁进行安全保护,线程中还有临界区保护。线程的存储结构是一个图,在分配线程后需要插入图完成创建。该内核函数调用了一个疑似处理远程线程的内核函数,但分析参数可以确定该参数不是一个变量而是而是一个常量,是通过上层函数传入的0得来的。所以可以暂时确定他没有对远程线程进行有效处理。如果需要确定还得继续学习内核知识。目前可以猜测在内核模式是不区分远程线程的,远程线程只是在用户模式的概念。

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
__int64 __fastcall PspCreateThread(PHANDLE a_hThread, ACCESS_MASK a_DesiredAccess, __int64 a_pObjectAttributes, ULONG_PTR a_hProcess, _KPROCESS *a5, __int64 a_pCreateProcessContext, __int64 a7, __int64 a8, __int64 a9, DWORD a_dwCreateSuspended, __int64 a_lpStartAddress, __int64 a12, __int64 a13)
{
  __int64 v14; // r8
  struct _KTHREAD *v_pKThread; // r15
  _KPROCESS *v_hProcess; // r13
  char v_PreviousMode; // al
  _KPROCESS *v_KProcess; // rbx
  int nRet3; // edi
  __int64 NtRet; // rax
  __int64 v21; // rdx
  __int64 v22; // r13
  unsigned int nRet; // eax
  struct _EX_RUNDOWN_REF *v_pRundownProtection; // rcx
  unsigned int nRet2; // ebx
  int v26; // eax
  char v_PreviousMode2; // [rsp+60h] [rbp-A0h]
  _DWORD v28[3]; // [rsp+64h] [rbp-9Ch] BYREF
  ACCESS_MASK v_DesiredAccess; // [rsp+70h] [rbp-90h]
  __int64 v_lpStartAddress; // [rsp+78h] [rbp-88h]
  PADAPTER_OBJECT DmaAdapter; // [rsp+80h] [rbp-80h] BYREF
  PVOID Object; // [rsp+88h] [rbp-78h] BYREF
  __int64 v_pCreateProcessContext; // [rsp+90h] [rbp-70h]
  __int64 v34; // [rsp+98h] [rbp-68h]
  __int64 v35; // [rsp+A0h] [rbp-60h]
  __int64 v36; // [rsp+A8h] [rbp-58h]
  __int64 v_pObjectAttributes; // [rsp+B0h] [rbp-50h]
  __int64 v38; // [rsp+B8h] [rbp-48h]
  HANDLE v_hThread; // [rsp+C0h] [rbp-40h]
  __int64 v40; // [rsp+C8h] [rbp-38h] BYREF
  __int128 v41; // [rsp+D0h] [rbp-30h]
  struct _ACCESS_STATE AccessState; // [rsp+E0h] [rbp-20h] BYREF
 
  v28[0] = 0;
  v_pCreateProcessContext = a_pCreateProcessContext;
  v38 = a7;
  v36 = a9;
  v_lpStartAddress = a_lpStartAddress;
  v34 = a12;
  v_pObjectAttributes = a_pObjectAttributes;
  v_DesiredAccess = a_DesiredAccess;
  v_hThread = a_hThread;
  v35 = a13;
  *(_QWORD *)&v28[1] = a8;
  memset(&AccessState, 0, 0x190ui64);
  v41 = 0i64;
  v_pKThread = KeGetCurrentThread();
  v_hProcess = v_pKThread->ApcState.Process;
  if ( a8 )
    v_PreviousMode = v_pKThread->PreviousMode;
  else
    v_PreviousMode = 0;
  DmaAdapter = 0i64;
  v_KProcess = 0i64;
  v_PreviousMode2 = v_PreviousMode;
  nRet3 = 0xC0000008;
  Object = 0i64;
  if ( a_hProcess )
  {
    NtRet = ObpReferenceObjectByHandleWithTag(a_hProcess, 0x72437350u, (__int64)&Object, 0i64, 0i64);// 通过进程句柄增加对象引用数
    v_KProcess = (_KPROCESS *)Object;
    goto LABEL_5;
  }
  if ( *(_QWORD *)&v28[1] )
  {
    NtRet = 0xC0000008i64;
LABEL_5:
    if ( (int)NtRet < 0 )
      return NtRet;
    goto LABEL_6;
  }
  ObfReferenceObjectWithTag(a5, 0x72437350u);   // 增加对象引用数
  v_KProcess = a5;
LABEL_6:
  if ( v_KProcess != v_hProcess && !PspIsProcessReadyForRemoteThread((ULONG_PTR)v_KProcess) )// 疑似处理远程线程,实际上该参数不是变量,感觉没有任何作用
    return 0xC0000001i64;
  v21 = v_lpStartAddress;
  LOBYTE(v14) = 1;
  if ( v_lpStartAddress && *(_QWORD *)&v28[1] )
  {
    v21 = v_lpStartAddress & -(__int64)((v_KProcess->SecureState.SecureHandle & 1) != 0);
    v_lpStartAddress = v21;
  }
  if ( !v_pCreateProcessContext
    && !v21
    && (((__int64)v_KProcess[2].ReadyListHead.Blink & 1) != 0
     || ((__int64)v_hProcess[2].ReadyListHead.Blink & 1) != 0
     || (HIDWORD(v_KProcess[2].ReadyListHead.Blink) & 0x4000) != 0
     || (HIDWORD(v_hProcess[2].ReadyListHead.Blink) & 0x4000) != 0) )
  {
    return 0xC0000022i64;
  }
  if ( v_PreviousMode2 )
  {
    v26 = HIDWORD(v_KProcess[2].Header.WaitListHead.Flink);
    if ( _bittest(&v26, 0xCu) || (v26 & 1) != 0 )
    {
LABEL_31:
      ObfDereferenceObjectWithTag(v_KProcess, 0x72437350u);// 减少对象引用数量,返回错误码
      return (unsigned int)nRet3;
    }
  }
  if ( (HIDWORD(v_KProcess[2].Header.WaitListHead.Flink) & 1) != 0
    && !*(_QWORD *)&v_KProcess[2].Affinity.Count
    && *(_QWORD *)&v28[1] )
  {
    nRet3 = 0xC0000022;
    goto LABEL_31;
  }
  PspMapThreadCreationFlags(a_dwCreateSuspended, v28, v14);// 猜测线程的存储结构是图
  --v_pKThread->KernelApcDisable;
  if ( !ExAcquireRundownProtection_0((PEX_RUNDOWN_REF)&v_KProcess[1].ProfileListHead.Blink) )// 获取执行保护,猜测是个锁
  {
    KeLeaveCriticalRegionThread(v_pKThread);    // 离开线程临界区
    nRet3 = 0xC000010A;
    goto LABEL_31;
  }
  ObfReferenceObjectWithTag(v_KProcess, 0x72437350u);// 增加对象的引用数
  v22 = v_pCreateProcessContext;
  v40 = v36;
  nRet3 = PspAllocateThread(                    // 分配一个新线程结构
            (ULONG_PTR)v_KProcess,
            v_pObjectAttributes,
            v_PreviousMode2,
            v_pCreateProcessContext,
            *(__int64 *)&v28[1],
            (__int64)&v40,
            v_lpStartAddress,
            v34,
            (__int64)v28,
            (__int64)&DmaAdapter,
            v35,
            (__int64)&AccessState);
  if ( nRet3 < 0 )
  {
    ExReleaseRundownProtection_0((PEX_RUNDOWN_REF)&v_KProcess[1].ProfileListHead.Blink);// 释放执行保护, 释放锁
    KeLeaveCriticalRegionThread(v_pKThread);    // 离开线程临界区
    goto LABEL_31;
  }
  ObfDereferenceObjectWithTag(v_KProcess, 0x72437350u);// 减少对象引用数
  nRet = PspInsertThread(                       // 将创建的线程插入到图中
           DmaAdapter,
           v_KProcess,
           v36,
           v28,
           v_DesiredAccess,
           v35,
           v22,
           v34,
           &AccessState,
           (__int64)v_hThread,
           v38);
  v_pRundownProtection = (struct _EX_RUNDOWN_REF *)&v_KProcess[1].ProfileListHead.Blink;
  nRet2 = nRet;
  ExReleaseRundownProtection_0(v_pRundownProtection);// 释放执行保护, 释放锁
  KeLeaveCriticalRegionThread(v_pKThread);      // 离开线程临界区
  HalPutDmaAdapter(DmaAdapter);
  return nRet2;
}

3、验证普通线程和远程线程在内核中没有区别:

  如果要验证普通进程和远程在内核中没有区别,就必须知道Kernel32模块是如何处理的,于是将IDA定位到Kernel32模块的CreateThreadStubAPI,然而让我震惊的一幕出现了:

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
.text:000000018001B5A0 ; HANDLE __stdcall CreateThreadStub(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
.text:000000018001B5A0                 public CreateThreadStub
.text:000000018001B5A0 CreateThreadStub proc near              ; DATA XREF: .rdata:000000018008468F↓o
.text:000000018001B5A0                                         ; .rdata:off_18009A208↓o ...
.text:000000018001B5A0
.text:000000018001B5A0 dwCreationFlags = dword ptr  28h
.text:000000018001B5A0 lpThreadId      = qword ptr  30h
.text:000000018001B5A0
.text:000000018001B5A0                 mov     r11, rsp
.text:000000018001B5A3                 sub     rsp, 48h
.text:000000018001B5A7                 mov     r10d, [rsp+48h+dwCreationFlags]
.text:000000018001B5AC                 mov     rax, [rsp+48h+lpThreadId]
.text:000000018001B5B1                 and     r10d, 10004h
.text:000000018001B5B8                 mov     [r11-10h], rax
.text:000000018001B5BC                 and     qword ptr [r11-18h], 0
.text:000000018001B5C1                 mov     [r11-20h], r10d
.text:000000018001B5C5                 mov     [r11-28h], r9
.text:000000018001B5C9                 mov     r9, r8          ; lpStartAddress
.text:000000018001B5CC                 mov     r8, rdx         ; dwStackSize
.text:000000018001B5CF                 mov     rdx, rcx        ; lpThreadAttributes
.text:000000018001B5D2                 or      rcx, 0FFFFFFFFFFFFFFFFh ; hProcess    进程句柄居然是-1
.text:000000018001B5D6                 call    cs:__imp_CreateRemoteThreadEx        ; 与远程线程调用同一个API
.text:000000018001B5DD                 nop     dword ptr [rax+rax+00h]
.text:000000018001B5E2                 add     rsp, 48h
.text:000000018001B5E6                 retn
.text:000000018001B5E6 ; ---------------------------------------------------------------------------
.text:000000018001B5E7                 db 0CCh
.text:000000018001B5E7 CreateThreadStub endp

流程图7
我的猜想是这样的:
流程图8
结果它是这样的:
流程图9
  这么看来前面有些代码的分析就会出现不小的偏差,创建远程线程与创建普通线程最本质的区别是进入KERNEL32!CreateRemoteThreadStub时的进程句柄有明显的区别,远程线程的进程句柄是正常的,而普通线程的进程句柄是-1。浏览上面的分析发现,不管是在用户模式还是在内核模式都对远程线程与普通线程的创建进行了区别处理。微软取名字的水平真会误导人啊。具体是怎么处理的还需要进一步的学习内核基础知识。

有趣的小实验:

  当我们发现创建远程线程与普通线程这一点区别后,是否可以这么做?使用CreateRemoteThread去创建普通线程,这其实很简单,只需要将进程句柄传为-1即可。说干就干,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <Windows.h>
#include <stdio.h>
 
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    printf("ThreadProc\r\n");
    return 0;
}
 
int main()
{   
    //进程句柄传-1就表示创建普通线程。
    HANDLE hThread = CreateRemoteThread((HANDLE)-1, NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);
    if (hThread == NULL)
    {
        printf("CreateRemoteThread Error:%d", GetLastError());
        return 0;
    }
 
    printf("CreateRemoteThread Success!\r\n");
    CloseHandle(hThread);
    return 0;
}

运行结果如下:
流程图10
果然可以!

温馨提示:

  本文的分析完全是个人在科锐学习期间的项目,不具有权威性。由于是刚开始接触内核编程不久,对内核的对象、句柄、权限一无所知。大部分分析结果可能与实际情况有着不小的偏差,待以后补充完相关知识再做修改,文中的错误也希望各路内核大佬多多指出,必定会做出改正。
  分析Windows内核实在是没有足够的资料进行学习,这里也希望大佬们指点下学习方法。


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞10
打赏
分享
最新回复 (8)
雪    币: 2061
活跃值: (1431)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
youxiaxy 2022-2-4 18:04
2
1

虽然搞有些小游戏的辅助的时候经常createremoteThread(-1,xxxxxx)不过还真没细究过内部的实现。涨知识了。感谢分享

最后于 2022-2-4 18:05 被youxiaxy编辑 ,原因:
雪    币: 4110
活跃值: (1495)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Boring勇哥 2022-2-6 02:09
3
1
-1是一个预定义的伪句柄,代表当前进程的句柄,常用宏函数NtCurrentProcess()和win32 api函数GetCurrentProcess()来引用它。类似的还有GetCurrentThread()等
雪    币: 35
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
编程两年半 2022-2-25 10:27
4
0
问一下,是科锐的线下班吗?
雪    币: 129
活跃值: (318)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xed 2022-3-22 20:17
5
0
不错,。标记一下~~~
雪    币: 1382
活跃值: (3100)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 2022-3-22 21:00
6
0
好,mark
雪    币: 1343
活跃值: (4048)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Oxygen1a1 2022-9-5 15:06
7
0
感谢po主,不过有些错误,有少量误导人的注释
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_广小告 2022-10-8 13:44
8
0
可以关注下 CR3 的变化 线程在哪使用主要取决于CR3 看看 线程创建的时候用的是 当前进程的CR3 还是 目标进程的CR3 
雪    币: 89
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
sanqiu 2022-11-28 16:17
9
0
创建线程的精髓就在PspCreateThread里面,你却一笔带过了
游客
登录 | 注册 方可回帖
返回