WindowsAPI是如何从3环进入0环的

在windows上,所有进程都需要调用一个模块——ntdll.dll

csrss.exe只调用ntdll.dll这一个模块(nativeProcess)

分析ReadProcessMemory如何从R3进入R0

代码:

1
2
3
4
5
6
7
8
9
#include <Windows.h>
int main()
{
__asm int 3;
HANDLE tmp = GetCurrentProcess();
ReadProcessMemory(tmp, NULL, NULL, NULL, NULL);
system("pause");
return 0;
}

因为有int3断点,直接运行让Windbg接收异常信号即可,如果没有符号,可以使用.reload强制重新加载符号

image-20201126161935196

单步到ReadProcessMemory的CALL处

image-20201126163700403

步入,发现还在Kernel32中,在这里如果全是问号,说明没有符号,可以使用.reload强制重新加载符号

image-20201126164153289

jmp过去发现又是一个jmp

image-20201126164319426

继续跟,来到了KernelBase的ReadProcessMemory中,在这又调用了一个ntdll中的NtReadVirtualMemory

image-20201126164618464

继续步入这个call,发现来到了这样一个地方

第一行,把0x115给了EAX,这个115并不是没有意义的,他叫服务号或者调用号

image-20201126164730324

第二行,把SharedUserData结构体中偏移0x300的值(SystemCallStub)mov给了edx,

1
edx, offset SharedUserData!SystemCallStub (7ffe0300)

使用dt _KUSER_SHARED_DATA 7ffe0000来查看一下这个结构体

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
1: kd> dt _KUSER_SHARED_DATA 7ffe0000
ntdll!_KUSER_SHARED_DATA
+0x000 TickCountLowDeprecated : 0
+0x004 TickCountMultiplier : 0xf99a027
+0x008 InterruptTime : _KSYSTEM_TIME
+0x014 SystemTime : _KSYSTEM_TIME
+0x020 TimeZoneBias : _KSYSTEM_TIME
+0x02c ImageNumberLow : 0x14c
+0x02e ImageNumberHigh : 0x14c
+0x030 NtSystemRoot : [260] "C:\Windows"
+0x238 MaxStackTraceDepth : 0
+0x23c CryptoExponent : 0
+0x240 TimeZoneId : 0
+0x244 LargePageMinimum : 0x200000
+0x248 Reserved2 : [7] 0
+0x264 NtProductType : 1 ( NtProductWinNt )
+0x268 ProductTypeIsValid : 0x1 ''
+0x26c NtMajorVersion : 6
+0x270 NtMinorVersion : 1
+0x274 ProcessorFeatures : [64] ""
+0x2b4 Reserved1 : 0x7ffeffff
+0x2b8 Reserved3 : 0x80000000
+0x2bc TimeSlip : 0
+0x2c0 AlternativeArchitecture : 0 ( StandardDesign )
+0x2c4 AltArchitecturePad : [1] 0
+0x2c8 SystemExpirationDate : _LARGE_INTEGER 0x0
+0x2d0 SuiteMask : 0x110
+0x2d4 KdDebuggerEnabled : 0x3 ''
+0x2d5 NXSupportPolicy : 0x2 ''
+0x2d8 ActiveConsoleId : 1
+0x2dc DismountCount : 0
+0x2e0 ComPlusPackage : 0xffffffff
+0x2e4 LastSystemRITEventTickCount : 0x5d74b
+0x2e8 NumberOfPhysicalPages : 0x7ff7e
+0x2ec SafeBootMode : 0 ''
+0x2ed TscQpcData : 0 ''
+0x2ed TscQpcEnabled : 0y0
+0x2ed TscQpcSpareFlag : 0y0
+0x2ed TscQpcShift : 0y000000 (0)
+0x2ee TscQpcPad : [2] ""
+0x2f0 SharedDataFlags : 0xc
+0x2f0 DbgErrorPortPresent : 0y0
+0x2f0 DbgElevationEnabled : 0y0
+0x2f0 DbgVirtEnabled : 0y1
+0x2f0 DbgInstallerDetectEnabled : 0y1
+0x2f0 DbgSystemDllRelocated : 0y0
+0x2f0 DbgDynProcessorEnabled : 0y0
+0x2f0 DbgSEHValidationEnabled : 0y0
+0x2f0 SpareBits : 0y0000000000000000000000000 (0)
+0x2f4 DataFlagsPad : [1] 0
+0x2f8 TestRetInstruction : 0xc3
+0x300 SystemCall : 0x772470b0
+0x304 SystemCallReturn : 0x772470b4
+0x308 SystemCallPad : [3] 0
+0x320 TickCount : _KSYSTEM_TIME
+0x320 TickCountQuad : 0x6002
+0x320 ReservedTickCountOverlay : [3] 0x6002
+0x32c TickCountPad : [1] 0
+0x330 Cookie : 0x1247913
+0x334 CookiePad : [1] 0
+0x338 ConsoleSessionForegroundProcessId : 0n3724
+0x340 Wow64SharedInformation : [16] 0
+0x380 UserModeGlobalLogger : [16] 0
+0x3a0 ImageFileExecutionOptions : 0
+0x3a4 LangGenerationCount : 1
+0x3a8 Reserved5 : 0
+0x3b0 InterruptTimeBias : 0
+0x3b8 TscQpcBias : 0
+0x3c0 ActiveProcessorCount : 2
+0x3c4 ActiveGroupCount : 1
+0x3c6 Reserved4 : 0
+0x3c8 AitSamplingValue : 0
+0x3cc AppCompatFlag : 1
+0x3d0 SystemDllNativeRelocation : 0xff340000
+0x3d8 SystemDllWowRelocation : 0
+0x3dc XStatePad : [1] 0
+0x3e0 XState : _XSTATE_CONFIGURATION

可以看到+0x300 SystemCall : 0x772470b0

u一下这个地址可以看到这是个函数

image-20201126170848934

跟进去下面的CALL就能来到这个地方

image-20201126171134675

接下来就是调用sysenter进入0环

所以大致的流程为:

image-20201126173853379

接下来分析一下KernalBase的详细行为,因为在上面跟踪的过程中可以看到Kernel32基本啥也没干,只是jmp到KernelBaseReadProcessMemory函数。

对KernelBase的分析

这里使用OpenProcess作为例子,搜索OpenProcess来到对应汇编处

image-20201126175407125

详细分析:

image-20201126233911713

在这行代码中,把EAX赋给了OBJECT_ATTRIBUTESAttributes成员

1
.text:0DCEB374 mov [ebp+ObjectAttributes.Attributes], eax

此结构体完整定义为:

1
2
3
4
5
6
7
8
9
10
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;

Attributes指定对象句柄属性的标志的位掩码标志:

1
2
3
4
5
6
7
8
9
10
11
#define OBJ_INHERIT                         0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
#define OBJ_IGNORE_IMPERSONATED_DEVICEMAP 0x00000800L
#define OBJ_DONT_REPARSE 0x00001000L
#define OBJ_VALID_ATTRIBUTES 0x00001FF2L

对ntdll的分析

KernelBaseOpenProcess在最后jmp到了ntdll中的NTOpenProcess,所以接下来要对ntdll中的NTOpenProcess进行分析

IDA加载ntdll后会发现,搜索不到NTOpenProcess

image-20201126235519449

这是为啥呢?接下来去导出表中搜索,发现可以找到

image-20201126235709261

双击过去就可以看到,实际上ntdll中调用的是ZwOpenProcess

image-20201126235850310

这里的.text:77F05D98 mov eax, 0BFh ; NtOpenProcessToken 在上面说过,是调用号

下面的.text:77F05D9D mov edx, 7FFE0300h上面也说过,是用来获取KiFastSystemCall的

然后就是.text:77F05DA2 call dword ptr [edx]CALL KiFastSystemCall

所以我们继续跟KiFastSystemCall这个函数

image-20201127000941009

在KiFastSystemCall这个函数这个函数中把R3的ESP给了EDX,然后调用sysemter进入0环

进入0环前的堆栈图:

image-20201127003112056

分析sysenter

准备工作

在从R3进入R0的时候,会改变这么几样东西

1
2
3
4
cs
ss
EIP
ESP

那么这几个东西保存在哪呢?

在intel白皮书中有这样一段描述,可以看到CS、ESP、ESP的值都存储在msr寄存器中

  • IA32_SYSENTER_CS (MSR address 174H) — The lower 16 bits of this MSR are the segment selector for the
    privilege level 0 code segment. This value is also used to determine the segment selector of the privilege level
    0 stack segment (see the Operation section). This value cannot indicate a null selector.
  • IA32_SYSENTER_EIP (MSR address 176H) — The value of this MSR is loaded into RIP (thus, this value
    references the first instruction of the selected operating procedure or routine). In protected mode, only
    bits 31:0 are loaded.
  • IA32_SYSENTER_ESP (MSR address 175H) — The value of this MSR is loaded into RSP (thus, this value
    contains the stack pointer for the privilege level 0 stack). This value cannot represent a non-canonical address.
    In protected mode, only bits 31:0 are loaded.

那么ss在哪呢?其实CS+8的位置就是ss

首先查看cs的msr,可以看到低位为8

image-20201127170441801

再查看gdtr,8的位置就是这

image-20201127170701637

那么他的+8就是ss

image-20201127170800371

接下看查看EIP,确定sysenter执行会跳到R0的哪个地方

image-20201127171030187

u一下这个83e3f0c0可以看到KiFastCallEntry这个函数

image-20201127171722055

所以sysenter执行后会跳转到KiFastCallEntry这个函数

在分析这个函数之前,需要先了解一个结构_KTRAP_FRAME,这个结构很重要。

image-20201127180732342

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
0: kd> dt _KTRAP_FRAME
ntdll!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B

注意,这四个值是虚拟8086进行系统调用的时候才会用来保存环境的,没有使用直接跳过

1
2
3
4
+0x07c V86Es            : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B

所以起始位置在

1
+0x078 HardwareSegSs    : Uint4B

为了保存从R3进入R0的寄存器,专门设计了这个结构,并且这个结构与调试也有关系,如果在R0里出现了异常,同样也是这个结构保存寄存器信息。在R3下也有这样一个类似的结构_CONTEXT,保存线程上下文环境

image-20201127181212516

因为sysenter指令不是每个cpu都支持,如果不支持sysenter的cpu要进行系统调用,就会通过int 0x2e中断进入

当通过中断进入R0时,系统会自动保存这几个值

1
2
3
4
5
6
7
+0x060 Ebp              : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B

然后esp就指向了

1
+0x064 ErrCode          : Uint4B

开始分析

了解这个之后,就可以使用ida分析KiFastCallEntry

在IDA中搜索可以搜出来两个,一个_KiFastCallEntry一个_KiFastCallEntry2
image-20201127175121321

为什么有两个呢?

_KiFastCallEntry2就是通过中断调用的

现在要分析的是通过sysenter调用的_KiFastCallEntry

汇编:

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
.text:0043E0C0                 mov     ecx, 23h        
.text:0043E0C5 push 30h
.text:0043E0C7 pop fs
.text:0043E0C9 mov ds, ecx
.text:0043E0CB mov es, ecx
.text:0043E0CD mov ecx, large fs:40h
.text:0043E0D4 mov esp, [ecx+4]
.text:0043E0D7 push 23h
.text:0043E0D9 push edx
.text:0043E0DA pushf
.text:0043E0DB
.text:0043E0DB loc_43E0DB: ; CODE XREF: _KiFastCallEntry2+23↑j
.text:0043E0DB push 2
.text:0043E0DD add edx, 8
.text:0043E0E0 popf
.text:0043E0E1 or [esp+0Ch+var_B], 2
.text:0043E0E6 push 1Bh
.text:0043E0E8 push dword ptr ds:0FFDF0304h
.text:0043E0EE push 0
.text:0043E0F0 push ebp
.text:0043E0F1 push ebx
.text:0043E0F2 push esi
.text:0043E0F3 push edi
.text:0043E0F4 mov ebx, large fs:1Ch
.text:0043E0FB push 3Bh
.text:0043E0FD mov esi, [ebx+124h]
.text:0043E103 push dword ptr [ebx]
.text:0043E105 mov dword ptr [ebx], 0FFFFFFFFh
.text:0043E10B mov ebp, [esi+28h]
.text:0043E10E push 1
.text:0043E110 sub esp, 48h
.text:0043E113 sub ebp, 29Ch
.text:0043E119 mov byte ptr [esi+13Ah], 1
.text:0043E120 cmp ebp, esp
.text:0043E122 jnz short loc_43E0BB
.text:0043E124 and dword ptr [ebp+2Ch], 0
.text:0043E128 test byte ptr [esi+3], 0DFh
.text:0043E12C mov [esi+128h], ebp
.text:0043E132 jnz Dr_FastCallDrSave
.text:0043E138
.text:0043E138 loc_43E138: ; CODE XREF: Dr_FastCallDrSave+D↑j
.text:0043E138 ; Dr_FastCallDrSave+79↑j
.text:0043E138 mov ebx, [ebp+60h]
.text:0043E13B mov edi, [ebp+68h]
.text:0043E13E mov [ebp+0Ch], edx
.text:0043E141 mov dword ptr [ebp+8], 0BADB0D00h
.text:0043E148 mov [ebp+0], ebx
.text:0043E14B mov [ebp+4], edi
.text:0043E14E sti
.text:0043E14F
.text:0043E14F loc_43E14F: ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:0043E14F ; _KiSystemService+7F↑j
.text:0043E14F mov edi, eax
.text:0043E151 shr edi, 8
.text:0043E154 and edi, 10h
.text:0043E157 mov ecx, edi
.text:0043E159 add edi, [esi+0BCh]
.text:0043E15F mov ebx, eax
.text:0043E161 and eax, 0FFFh
.text:0043E166 cmp eax, [edi+8]
.text:0043E169 jnb _KiBBTUnexpectedRange
.text:0043E16F cmp ecx, 10h
.text:0043E172 jnz short loc_43E18E
.text:0043E174 mov ecx, [esi+88h]
.text:0043E17A xor esi, esi
.text:0043E17C
.text:0043E17C loc_43E17C: ; DATA XREF: _KiTrap0E+156↓o
.text:0043E17C or esi, [ecx+0F70h]
.text:0043E182 jz short loc_43E18E
.text:0043E184 push edx
.text:0043E185 push eax
.text:0043E186 call ds:_KeGdiFlushUserBatch
.text:0043E18C pop eax
.text:0043E18D pop edx
.text:0043E18E
.text:0043E18E loc_43E18E: ; CODE XREF: _KiFastCallEntry+B2↑j
.text:0043E18E ; _KiFastCallEntry+C2↑j
.text:0043E18E inc large dword ptr fs:6B0h
.text:0043E195 mov esi, edx
.text:0043E197 xor ecx, ecx
.text:0043E199 mov edx, [edi+0Ch]
.text:0043E19C mov edi, [edi]
.text:0043E19E mov cl, [eax+edx]
.text:0043E1A1 mov edx, [edi+eax*4]
.text:0043E1A4 sub esp, ecx
.text:0043E1A6 shr ecx, 2
.text:0043E1A9 mov edi, esp
.text:0043E1AB cmp esi, ds:_MmUserProbeAddress
.text:0043E1B1 jnb loc_43E3E5
.text:0043E1B7
.text:0043E1B7 loc_43E1B7: ; CODE XREF: _KiFastCallEntry+329↓j
.text:0043E1B7 ; DATA XREF: _KiTrap0E:loc_441448↓o
.text:0043E1B7 rep movsd
.text:0043E1B9 test byte ptr [ebp+6Ch], 1
.text:0043E1BD jz short loc_43E1D5
.text:0043E1BF mov ecx, large fs:124h
.text:0043E1C6 mov edi, [esp+7Ch+var_7C]
.text:0043E1C9 mov [ecx+13Ch], ebx
.text:0043E1CF mov [ecx+12Ch], edi
.text:0043E1D5
.text:0043E1D5 loc_43E1D5: ; CODE XREF: _KiFastCallEntry+FD↑j
.text:0043E1D5 mov ebx, edx
.text:0043E1D7 test byte ptr ds:dword_537908, 40h
.text:0043E1DE setnz byte ptr [ebp+12h]
.text:0043E1E2 jnz loc_43E574
.text:0043E1E8
.text:0043E1E8 loc_43E1E8: ; CODE XREF: _KiFastCallEntry+4BB↓j
.text:0043E1E8 call ebx
.text:0043E1EA
.text:0043E1EA loc_43E1EA: ; CODE XREF: _KiFastCallEntry+334↓j
.text:0043E1EA ; DATA XREF: _KiTrap0E+18C↓o
.text:0043E1EA test byte ptr [ebp+6Ch], 1
.text:0043E1EE jz short loc_43E224
.text:0043E1F0 mov esi, eax
.text:0043E1F2 call ds:__imp__KeGetCurrentIRQL@0 ; KeGetCurrentIRQL()
.text:0043E1F8 or al, al
.text:0043E1FA jnz loc_43E53B
.text:0043E200 mov eax, esi
.text:0043E202 mov ecx, large fs:124h
.text:0043E209 test byte ptr [ecx+134h], 0FFh
.text:0043E210 jnz loc_43E559
.text:0043E216 mov edx, [ecx+84h]
.text:0043E21C or edx, edx
.text:0043E21E jnz loc_43E559
.text:0043E224
.text:0043E224 loc_43E224: ; CODE XREF: _KiFastCallEntry+12E↑j
.text:0043E224 ; _KiCallbackReturn+8D↓j
.text:0043E224 ; DATA XREF: ...
.text:0043E224 mov esp, ebp
.text:0043E226 cmp byte ptr [ebp+12h], 0
.text:0043E22A jnz loc_43E580
.text:0043E230
.text:0043E230 loc_43E230: ; CODE XREF: _KiBBTUnexpectedRange+3C↑j
.text:0043E230 ; _KiBBTUnexpectedRange+47↑j ...
.text:0043E230 mov ecx, large fs:124h
.text:0043E237 mov edx, [ebp+3Ch]
.text:0043E23A mov [ecx+128h], edx
.text:0043E23A _KiFastCallEntry endp

由于函数太长无法一次性截图,所以分段解析

第一部分准备环境:

image-20201127214337645

在第一部分fs被赋值为了0x30,而0x30对应的结构为KPCR,所以需要在IDA中添加这个结构

来到Structures窗口按INSERT

输入_KPCR确定

image-20201127220312399

如果里面默认有这个结构体就不会给你添加,灰色说明是导出的

image-20201127220406128

接下来选中fs按t键,搜索KPCR 双击即可

image-20201127221057922

image-20201127230718118

如需还原,按R即可

下面一行则对应_KTSS的结构

image-20201127231851929

同样按t添加_KTSS结构,这就验证了《x86内核-保护模式-段》中说的R0的寄存器从TSS中来

那么这又出现了一个新的问题,sysenter中msr.176的位置存在一个ESP,但是他在R0并没有使用

因为微软把msr.176的ESP定义为临时ESP,因为每一个线程的堆栈都是不一样的,如果用的都是同一个ESP,将会增加维护难度

image-20201127232040235

如果搜索不出_KTSS,重复刚刚INSERT的步骤即可

另外补充几个IDA关于结构体的操作

在IDA中这种蓝色的都是未被展开的结构体,可以按CTRL+ NUMPAD+(小键盘+号)展开, NUMPAD-(小键盘-号)收回

image-20201127232434134

按INSERT如果结构体存在只是帮你展开,没有才会添加

这里我通过INSERT添加了一个新的结构体,结构体的size为0

image-20201127233323554

对着这个结构按d即可为结构体添加一字节成员

image-20201127233443365

继续按d则会让这个成员变为2、4、8字节

对着名字按N即可修改名字

image-20201127233649437

在结构体的底部继续按d即可添加新的成员

image-20201127233902303

接下来到这一部分

image-20201127235714659

push 0x23是保存R3的SS

push EDX是保存R3的ESP,这里不明白可以看我这个进入R0前堆栈图

image-20201127003112056

接下来到这一行

image-20201128000321695

先把这里按右键转为byte(字节)

image-20201128002050136

现在ESP的位置在EFLAG的位置,所以是用EFLAG+1,需要注意的是,这里的加1是指加1字节,所以是到了第八位TF位

image-20201128001220955

然后用第八位去与上2:0010

也就是IF位(可屏蔽中断)

image-20201128003555873

到了这里,_KTRAP_FRAME结构体的这一部分已经填充完毕

1
2
3
4
5
6
7
8
9
10
11
+0x050 SegFs            : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B

接下来到

image-20201128005816305

设置对应的结构体偏移量

image-20201128005640772

下一行

image-20201128011843672

根据结构体可以知道,他取的是线程初始化堆栈的位置,原始esp指向哪个位置

image-20201128011601988

同样按t设置结构

image-20201128012016001

后面的push 1没什么难点,直接上图

image-20201128012253448

在push 1之后ESP已经来到了0x48这个位置

1
2
3
4
5
6
7
8
9
10
11
12
13
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B

所以下一行,直接一个

image-20201128132224790

来到结构的头部 +0x000 DbgEbp : Uint4B

下一行

image-20201128132729354

接下来做了一个很有意思的动作cmp ebp,esp

image-20201128132338824

如果不相等则跳转到06号中断

image-20201128132639025

所以这两个值必须相等,具体可以在WinDbg上查看

看首先先查看InitialStack的地址0x807efed0

image-20201128133818750

再dg 28查看任务段地址,然后查看_KTSS段结构中的ESP0x807efcb0

image-20201128134008027

这两个地址很显然在一个页内

接下来三行

image-20201128140156771

如果存在调试器,则会跳转到这

image-20201128140429414

接下来到下一部分

image-20201128144106335

_KTHREAD结构中的0xbc的位置有一个表,这个就是ssdt表

image-20201128144258846

第一部分总结:

image-20201128161709889

image-20201128161756741

接下来跟一下这个_KiBBTUnexpectedRange跳转里的_PsConvertToGuiThread这个CALL

image-20201128162029066

image-20201128162223597

完整分析:

image-20201128171543287

SSDT表与SSSDT表的区别:

image-20201128170137230

用NTOpenProcess的服务号作为例子

image-20201128162352281

首先这个调用号没有大于0x1000,在ssdt表里寻找

dd KeServiceDescriptorTable 查看ssdt表

image-20201128162852816

如要查看sssdt表使用dd KeServiceDescriptorTableShadow

image-20201128163011725

NTOpenProcess的参数有4个也就是16字节

使用WinDbg查看参数表,这是0x10,也就是16

image-20201128163406893

接下来查看函数表,因为表里存放的是函数指针,每个指针为4字节,所以要乘四

image-20201128163610124

再u一下这个指针,就是NTOpenProcess函数

image-20201128163747794

如果是带ui的怎么找呢?

使用IDA打开user32.dll搜索NTuser系列函数,随便点开一个,可以看到调用号为11DA

image-20201128173013790

11DA在右移8位再与上0x10肯定等于0x10

11DA >>8 AND 0x10 = 10

所以要在第二项找

image-20201128173348125

那么现在就知道了

函数表:94e96000

参数个数表:94e9702c

然后使用WinDbg找一个带ui的程序,如explorer.exe

image-20201128174004360

使用WinDbg附加然后断下

image-20201128174154591

然后查看参数,16个字节

image-20201128174404750

查看函数

image-20201128174602236

第二部分

image-20201129004435753

image-20201129004519576

image-20201129004631727

image-20201129004714821

总结

image-20201129010101888

什么是IRQL

中断请求等级(interrupt request level - IRQL)

软件等级的RIQL有三个

PASSIVE_LEVEL用户级别

APC_LEVELAPC异步调用

DISPATCH_LEVEL延时过程调用,定时器

R3调用到R0的都是PASSIVE_LEVEL用户级别

线程的切换是在APC_LEVELAPC异步调用等级下

如果当前处于DISPATCH_LEVEL延时过程调用等级下,线程是无法切走的,如果把时钟也关掉,那么就只有更高等级的RIQL能打断

在同等级下不能抢占,只能顺序,队列优先级

由于DISPATCH_LEVEL在软件层面中断等级最高,不能使用分页内存

因为如果使用分页内存,刚好这块分页内存被交换到了磁盘上,在执行DPC的时候就会产生缺页异常,由于缺页异常的处理函数也是DPC等级,不能抢占,无法接管,导致无法修复缺页,导致蓝屏。

x86_SSDT_HOOK

如何开始?

  1. 获取KeServiceDescriptorTable,他在x86的系统中是导出的,但是KeServiceDescriptorTableShadow在x86中也没有导出,而x64中两者都没有导出。

image-20201129160136661

虽然KeServiceDescriptorTableShadow在x86中也没有被导出但是可以通过偏移求得,可以看到KeServiceDescriptorTable的地址为0056A9C0,KeServiceDescriptorTableShadow的地址为0056AA00。两者之间的偏移为0x40

image-20201129162511063

然后再找一个函数测试HOOK,这里我使用ZwSetEvent函数作为例子,调用号为0x143。

image-20201129172236359

References:

《牛逼的火哥》

《Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference, M-U》

IRQL(多线程中断请求级别)