PowerTool x64驱动模块逆向分析(持续更新)
比赛打完了,来继续搞了,因为那个主动防御正在写,所以想找找思路正好想到可以来逆向一下PT的驱动模块看看pt大大是怎么写的程序。
PT x64版本的驱动模块是这个kEvP64.sys。
0x0
先来看看DriverEntry
1 //IDA伪代码 2 __int64 __fastcall sub_3A010(struct _DRIVER_OBJECT *a1, __int64 a2) 3 { 4 char *v2; // rdi@1 5 signed __int64 i; // rcx@1 6 char *v4; // rdi@4 7 __int64 v5; // rsi@4 8 signed __int64 j; // rcx@4 9 _UNKNOWN *v7; // rdi@7 10 char *v8; // rsi@7 11 signed __int64 k; // rcx@7 12 __int64 result; // rax@11 13 unsigned int v11; // [sp+48h] [bp-A0h]@10 14 NTSTATUS v12; // [sp+48h] [bp-A0h]@12 15 NTSTATUS v13; // [sp+48h] [bp-A0h]@14 16 char v14; // [sp+ACh] [bp-3Ch]@1 17 char v15; // [sp+C0h] [bp-28h]@4 18 struct _DRIVER_OBJECT *DriverObject; // [sp+F0h] [bp+8h]@1 19 20 DriverObject = a1; 21 v2 = &v14; 22 for ( i = 4i64; i; --i ) 23 *v2++ = 0; 24 v4 = &v15; 25 v5 = a2; 26 for ( j = 16i64; j; --j ) 27 *v4++ = *(_BYTE *)v5++; 28 v7 = &unk_37C60; 29 v8 = &v15; 30 for ( k = 16i64; k; --k ) 31 { 32 *(_BYTE *)v7 = *v8++; 33 v7 = (char *)v7 + 1; 34 } 35 RtlGetVersion(&unk_37AE0); 36 v11 = sub_19A30(); 37 if ( (v11 & 0x80000000) == 0 ) 38 { 39 sub_39010(); 40 DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_29870; 41 RtlInitUnicodeString(&DeviceName, L"\\Device\\kEvP64"); 42 v12 = IoCreateDevice(DriverObject, 0, &DeviceName, 0x22u, 0x100u, 0, &DeviceObject); 43 if ( v12 >= 0 ) 44 { 45 DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_298F0; 46 DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_298F0; 47 DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_29940; 48 RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\kEvP64"); 49 v13 = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName); 50 if ( v13 >= 0 ) 51 { 52 FltRegisterFilter(DriverObject, &unk_300C0, &qword_37AA8); 53 *((_DWORD *)DriverObject->DriverSection + 26) |= 0x20u; 54 qword_37C28 = (__int64)DriverObject; 55 result = 0i64; 56 } 57 else 58 { 59 IoDeleteDevice(DeviceObject); 60 result = (unsigned int)v13; 61 } 62 } 63 else 64 { 65 result = (unsigned int)v12; 66 } 67 } 68 else 69 { 70 result = v11; 71 } 72 return result; 73 }
函数的26、27行把程序的注册表目录的字符串保存到了局部数组中,然后又存在了一个全局的缓冲区里,应该是一个全局数组。
然后是进行系统版本的判断,对于驱动模块来说判断系统很重要,否则很容易造成蓝屏。
使用RtlGetVersion获得一个有关系统信息的结构,WDK中对这个函数的描述如下
RtlGetVersion
The RtlGetVersion routine returns version information about the currently running operating system.
NTSTATUS
RtlGetVersion(
IN OUT PRTL_OSVERSIONINFOW lpVersionInformation
);
来看看pt是怎么对版本进行的判断,
首先是对IRQL进行的判断,代码如下
1 if ( (signed int)(unsigned __int8)sub_11030() > 1 ) 2 { 3 v0 = sub_11030(); 4 DbgPrint("EX: Pageable code called at IRQL %d\n", v0); 5 sub_11020(); 6 }
其中sub_11030的反汇编如下
刚开始没明白是什么意思,后来查了一下原来X64的IRQL储存在CR8里,这个以前还真的不知道,学到了。
判断了一下IRQL是否合理,然后就是具体的判断了。
1 v2 = dword_37964; 2 v4 = dword_37968; 3 v3 = (unsigned __int16)word_37A74; 4 DbgPrint( 5 "[kEvP64]Windows %d.%d, SP%d.%d, build %d\n", 6 (unsigned int)dword_37964, 7 (unsigned int)dword_37968, 8 (unsigned __int16)word_37A74); 9 dword_37908 = 0; 10 dword_378E0 = 0; 11 if ( v2 == 5 && v4 == 1 ) 12 { 13 dword_378FC = 51; 14 if ( !v3 ) 15 return 3221225659i64; 16 if ( v3 == 1 ) 17 return 3221225659i64; 18 if ( v3 != 2 && v3 != 3 ) 19 return 3221225659i64; 20 return 0i64; 21 } 22 if ( v2 == 5 && v4 == 2 ) 23 { 24 dword_378FC = 52; 25 if ( v3 && v3 != 1 && v3 != 2 ) 26 return 3221225659i64; 27 return 0i64; 28 } 29 if ( v2 == 6 && !v4 ) 30 { 31 dword_378FC = 60; 32 if ( v3 ) 33 { 34 if ( v3 == 1 ) 35 { 36 dword_37C40 = 16; 37 dword_37C50 = 40; 38 } 39 else 40 { 41 if ( v3 != 2 ) 42 return 3221225659i64; 43 dword_37C40 = 16; 44 dword_37C50 = 40; 45 } 46 } 47 else 48 { 49 dword_37C40 = 120; 50 dword_37C50 = 144; 51 } 52 dword_37C58 = 20; 53 dword_37C70 = 352; 54 dword_37C54 = 876; 55 dword_378E4 = 11; 56 dword_37A90 = 216; 57 dword_378F8 = 16; 58 dword_378F0 = 632; 59 dword_37AC0 = 24; 60 dword_37C44 = 104; 61 dword_37C48 = 992; 62 dword_37BF4 = 856; 63 dword_37930 = 115; 64 dword_378F4 = 340; 65 dword_37AB0 = 904; 66 dword_37C4C = 896; 67 dword_37920 = 904; 68 dword_37AC4 = 896; 69 dword_37BF8 = 339; 70 dword_37888 = 1056; 71 dword_37908 = 48; 72 dword_377CC = 580; 73 dword_37894 = 48; 74 dword_377C4 = 41; 75 dword_377B0 = 32; 76 dword_377B4 = 370; 77 dword_377B8 = 79; 78 dword_377BC = 80; 79 dword_377C0 = 22; 80 dword_3788C = 440; 81 dword_37890 = 712; 82 dword_37898 = 2416; 83 dword_3789C = 2424; 84 dword_378A0 = 56; 85 return 0i64; 86 } 87 if ( v2 == 6 && v4 == 1 ) 88 { 89 dword_378FC = 61; 90 if ( v3 && v3 != 1 ) 91 return 3221225659i64; 92 dword_37C58 = 20; 93 dword_37C70 = 512; 94 dword_37C54 = 1084; 95 dword_378E4 = 11; 96 dword_37A90 = 376; 97 dword_378F8 = 16; 98 dword_37C40 = 16; 99 dword_37C50 = 40; 100 dword_378F0 = 800; 101 dword_37AC0 = 24; 102 dword_37C44 = 112; 103 dword_37C48 = 1040; 104 dword_37BF4 = 904; 105 dword_37C30 = 1048; 106 dword_37C14 = 912; 107 dword_37930 = 123; 108 dword_378F4 = 356; 109 dword_37BF8 = 502; 110 dword_37888 = 1056; 111 dword_37AB0 = 960; 112 dword_37C4C = 952; 113 dword_37920 = 952; 114 dword_37AC4 = 944; 115 dword_37908 = 48; 116 dword_377CC = 620; 117 dword_37894 = 48; 118 dword_377C4 = 41; 119 dword_377B0 = 32; 120 dword_377B4 = 379; 121 dword_377B8 = 79; 122 dword_377BC = 80; 123 dword_377C0 = 22; 124 dword_3788C = 600; 125 dword_37890 = 744; 126 dword_37898 = 1352; 127 dword_3789C = 1360; 128 dword_378A0 = 64; 129 DbgPrint("[kEvP64]Initialized version-specific data for Windows 7 SP%d\n", v3); 130 return 0i64; 131 } 132 if ( v2 == 6 && v4 == 2 ) 133 { 134 dword_378FC = 62; 135 dword_37C58 = 20; 136 dword_37C70 = 1032; 137 dword_37C54 = -1; 138 dword_378E4 = -1; 139 dword_37A90 = 456; 140 dword_378F8 = 16; 141 dword_37C40 = 16; 142 dword_37C50 = 40; 143 dword_378F0 = 1048; 144 dword_37AC0 = 24; 145 dword_37908 = 48; 146 dword_37C10 = 19; 147 dword_378E0 = 20; 148 dword_37C44 = 184; 149 dword_37C48 = 1008; 150 dword_37BF4 = 880; 151 dword_37C30 = 1008; 152 dword_37C14 = 880; 153 dword_37930 = 195; 154 dword_378F4 = 388; 155 dword_37BF8 = 562; 156 dword_37888 = 1024; 157 dword_37AB0 = 928; 158 dword_37C4C = 920; 159 dword_37920 = 928; 160 dword_37AC4 = 920; 161 dword_377CC = 644; 162 dword_37894 = 48; 163 dword_377C4 = 42; 164 dword_377B0 = 33; 165 dword_377B4 = 402; 166 dword_377B8 = 80; 167 dword_377BC = 81; 168 dword_377C0 = 23; 169 dword_3788C = 920; 170 dword_37890 = 784; 171 dword_37898 = 328; 172 dword_3789C = 336; 173 dword_378A0 = 72; 174 DbgPrint("[kEvP64]Initialized version-specific data for Windows 8 SP%d\n", v3); 175 return 0i64; 176 } 177 if ( v2 == 6 && v4 == 3 ) 178 { 179 dword_378FC = 63; 180 dword_37C54 = -1; 181 dword_378E4 = -1; 182 dword_37C58 = 20; 183 dword_378F8 = 16; 184 dword_37C70 = 1032; 185 dword_37A90 = 728; 186 dword_37C40 = 16; 187 dword_37C50 = 40; 188 dword_378F0 = 1048; 189 dword_37AC0 = 24; 190 dword_37908 = 48; 191 dword_37C10 = 16; 192 dword_378E0 = 17; 193 dword_37C44 = 184; 194 dword_37C48 = 1656; 195 dword_37BF4 = 1528; 196 dword_37C30 = 1656; 197 dword_37C14 = 1528; 198 dword_37930 = 195; 199 dword_378F4 = 388; 200 dword_37BF8 = 562; 201 dword_37888 = 760; 202 dword_37AB0 = 1576; 203 dword_37C4C = 1568; 204 dword_37920 = 1576; 205 dword_37AC4 = 1568; 206 dword_377CC = 644; 207 dword_37894 = 48; 208 dword_377C4 = 43; 209 dword_377B0 = 34; 210 dword_377B4 = 408; 211 dword_377B8 = 81; 212 dword_377BC = 82; 213 dword_377C0 = 24; 214 dword_3788C = 920; 215 dword_37890 = 784; 216 dword_37898 = 328; 217 dword_3789C = 336; 218 dword_378A0 = 72; 219 DbgPrint("[kEvP64]Initialized version-specific data for Windows 8.1 SP%d\n", v3); 220 return 0i64; 221 } 222 if ( v2 == 10 && !v4 ) 223 { 224 dword_378FC = 100; 225 dword_37C54 = -1; 226 dword_378E4 = -1; 227 dword_37C58 = 20; 228 dword_378F8 = 16; 229 dword_37C70 = 1048; 230 dword_37A90 = 736; 231 dword_378F0 = 1064; 232 dword_37C40 = 16; 233 dword_37C50 = 40; 234 dword_37AC0 = 24; 235 dword_37908 = 48; 236 dword_37C10 = 16; 237 dword_378E0 = 17; 238 dword_37C44 = 184; 239 dword_37C48 = 1664; 240 dword_37BF4 = 1536; 241 dword_37C30 = 1664; 242 dword_37C14 = 1536; 243 dword_37930 = 195; 244 dword_378F4 = 388; 245 dword_37BF8 = 562; 246 dword_37888 = 760; 247 dword_37AB0 = 1584; 248 dword_37C4C = 1576; 249 dword_37920 = 1584; 250 dword_37AC4 = 1576; 251 dword_377CC = 644; 252 dword_37894 = 48; 253 dword_377C4 = 44; 254 dword_377B0 = 35; 255 dword_377B4 = 416; 256 dword_377B8 = 82; 257 dword_377BC = 83; 258 dword_377C0 = 25; 259 dword_3788C = 936; 260 dword_37890 = 784; 261 dword_37898 = 328; 262 dword_3789C = 336; 263 dword_378A0 = 72; 264 return 0i64; 265 } 266 if ( v2 == 10 && v4 || v2 > 0xA ) 267 { 268 dword_378FC = -1; 269 result = 3221225659i64; 270 } 271 else 272 { 273 result = 3221225659i64; 274 } 275 return result; 276 }
其中v2,v3,v4都是结构体中的成员,就是上面用RtlGetVersion获取到的结构体。
类似于return 3221225659i64;这种是NTSTATUS值,0就是STATUS_SUCESS,下面还可以看到
(v11 & 0x80000000) == 0
这种写法就是NT_SUCESS()宏
来具体看下这种判断过程是怎么个意思
dword_37960 = 284; v2 = RtlGetVersion(&dword_37960);
这个是指定使用了RTL_OSVERSIONINFOEXW结构,因为RtlGetVersion这个函数其实可以支持两种格式的输出。
根据反汇编的结果还原了一下C的源码,应该是根据不同的系统版本设置了全局变量不同的值,但是目前还不知道这些变量的作用,判断系统方法比较简单,根据dwMajorVersion判断主版本号,dwMinorVersion判断副版本号,再根据需要去判断wServicePackMajor的值就可以实现了。
1 RTL_OSVERSIONINFOEXW Struct={284}; 2 ULONG Version; 3 NTSTATUS CheckVersion(void) 4 { 5 ULONG MajorVersion; 6 ULONG MinorVersion; 7 ULONG ServicePackMajor; 8 ULONG IRQL; 9 ULONG result; 10 RtlGetVersion(&Struct); 11 if(KeGetCurrentirql()>PASSIVE_LEVEL) 12 { 13 IRQL=KeGetCurrentirql(); 14 DbgPrint("EX: Pageable code called at IRQL %d\n", IRQL); 15 _asm{int 0x2c}; 16 } 17 MajorVersion=Struct.dwMajorVersion; 18 MinorVersion=Struct.dwMinorVersion; 19 ServicePackMajor=Struct.wServicePackMajor; 20 DbgPrint( 21 "[kEvP64]Windows %d.%d, SP%d.%d, build %d\n",Struct.dwMajorVersion,Struct.dwMinorVersion,Struct.wServicePackMajor); 22 if(MajorVersion==5&&MinorVersion==1) 23 { 24 //WINDOWS_XP 25 Version=51; 26 if(!ServicePackMajor) 27 return 3221225659; 28 if(ServicePackMajor==1) 29 return 3221225659; 30 if(ServicePackMajor!=2&&ServicePackMajor!=3) 31 return 3221225659; 32 return STATUS_SUCCESS; 33 } 34 if(MajorVersion==5&&MinorVersion==2) 35 { 36 //WINDOWS_2003 37 Version=52; 38 if(ServicePackMajor&&ServicePackMajor!=1&&ServicePackMajor!=2) 39 return 3221225659; 40 return STATUS_SUCCESS; 41 } 42 if(MajorVersion==6&&!MinorVersion) 43 { 44 //WINDOWS_2003 45 Version=60; 46 if(ServicePackMajor) 47 { 48 if(ServicePackMajor==1) 49 { 50 dword_37C40 = 16; 51 dword_37C50 = 40; 52 } 53 else 54 { 55 if(ServicePackMajor!=2) 56 return 3221225659; 57 dword_37C40 = 16; 58 dword_37C50 = 40; 59 } 60 } 61 else 62 { 63 dword_37C40 = 120; 64 dword_37C50 = 144; 65 } 66 dword_37C58 = 20; 67 dword_37C70 = 352; 68 dword_37C54 = 876; 69 dword_378E4 = 11; 70 dword_37A90 = 216; 71 dword_378F8 = 16; 72 dword_378F0 = 632; 73 dword_37AC0 = 24; 74 dword_37C44 = 104; 75 dword_37C48 = 992; 76 dword_37BF4 = 856; 77 dword_37930 = 115; 78 dword_378F4 = 340; 79 dword_37AB0 = 904; 80 dword_37C4C = 896; 81 dword_37920 = 904; 82 dword_37AC4 = 896; 83 dword_37BF8 = 339; 84 dword_37888 = 1056; 85 dword_37908 = 48; 86 dword_377CC = 580; 87 dword_37894 = 48; 88 dword_377C4 = 41; 89 dword_377B0 = 32; 90 dword_377B4 = 370; 91 dword_377B8 = 79; 92 dword_377BC = 80; 93 dword_377C0 = 22; 94 dword_3788C = 440; 95 dword_37890 = 712; 96 dword_37898 = 2416; 97 dword_3789C = 2424; 98 dword_378A0 = 56; 99 return STATUS_SUCCESS; 100 } 101 if(MajorVersion==6&&MinorVersion==1) 102 { 103 //WINDOWS_7 104 Version=61; 105 if(ServicePackMajor&&ServicePackMajor!=1) 106 return 3221225659; 107 dword_37C58 = 20; 108 dword_37C70 = 512; 109 dword_37C54 = 1084; 110 dword_378E4 = 11; 111 dword_37A90 = 376; 112 dword_378F8 = 16; 113 dword_37C40 = 16; 114 dword_37C50 = 40; 115 dword_378F0 = 800; 116 dword_37AC0 = 24; 117 dword_37C44 = 112; 118 dword_37C48 = 1040; 119 dword_37BF4 = 904; 120 dword_37C30 = 1048; 121 dword_37C14 = 912; 122 dword_37930 = 123; 123 dword_378F4 = 356; 124 dword_37BF8 = 502; 125 dword_37888 = 1056; 126 dword_37AB0 = 960; 127 dword_37C4C = 952; 128 dword_37920 = 952; 129 dword_37AC4 = 944; 130 dword_37908 = 48; 131 dword_377CC = 620; 132 dword_37894 = 48; 133 dword_377C4 = 41; 134 dword_377B0 = 32; 135 dword_377B4 = 379; 136 dword_377B8 = 79; 137 dword_377BC = 80; 138 dword_377C0 = 22; 139 dword_3788C = 600; 140 dword_37890 = 744; 141 dword_37898 = 1352; 142 dword_3789C = 1360; 143 dword_378A0 = 64; 144 DbgPrint("[kEvP64]Initialized version-specific data for Windows 7 SP%d\n", ServicePackMajor); 145 return STATUS_SUCCESS; 146 147 } 148 if(MajorVersion==6&&MinorVersion==2) 149 { 150 //WINDOWS_8 151 dword_378FC = 62; 152 dword_37C58 = 20; 153 dword_37C70 = 1032; 154 dword_37C54 = -1; 155 dword_378E4 = -1; 156 dword_37A90 = 456; 157 dword_378F8 = 16; 158 dword_37C40 = 16; 159 dword_37C50 = 40; 160 dword_378F0 = 1048; 161 dword_37AC0 = 24; 162 dword_37908 = 48; 163 dword_37C10 = 19; 164 dword_378E0 = 20; 165 dword_37C44 = 184; 166 dword_37C48 = 1008; 167 dword_37BF4 = 880; 168 dword_37C30 = 1008; 169 dword_37C14 = 880; 170 dword_37930 = 195; 171 dword_378F4 = 388; 172 dword_37BF8 = 562; 173 dword_37888 = 1024; 174 dword_37AB0 = 928; 175 dword_37C4C = 920; 176 dword_37920 = 928; 177 dword_37AC4 = 920; 178 dword_377CC = 644; 179 dword_37894 = 48; 180 dword_377C4 = 42; 181 dword_377B0 = 33; 182 dword_377B4 = 402; 183 dword_377B8 = 80; 184 dword_377BC = 81; 185 dword_377C0 = 23; 186 dword_3788C = 920; 187 dword_37890 = 784; 188 dword_37898 = 328; 189 dword_3789C = 336; 190 dword_378A0 = 72; 191 DbgPrint("[kEvP64]Initialized version-specific data for Windows 8 SP%d\n", ServicePackMajor); 192 return STATUS_SUCCESS; 193 } 194 if ( MajorVersion == 6 && MinorVersion == 3 ) 195 { 196 //WINDOWS_8.1 197 dword_378FC = 63; 198 dword_37C54 = -1; 199 dword_378E4 = -1; 200 dword_37C58 = 20; 201 dword_378F8 = 16; 202 dword_37C70 = 1032; 203 dword_37A90 = 728; 204 dword_37C40 = 16; 205 dword_37C50 = 40; 206 dword_378F0 = 1048; 207 dword_37AC0 = 24; 208 dword_37908 = 48; 209 dword_37C10 = 16; 210 dword_378E0 = 17; 211 dword_37C44 = 184; 212 dword_37C48 = 1656; 213 dword_37BF4 = 1528; 214 dword_37C30 = 1656; 215 dword_37C14 = 1528; 216 dword_37930 = 195; 217 dword_378F4 = 388; 218 dword_37BF8 = 562; 219 dword_37888 = 760; 220 dword_37AB0 = 1576; 221 dword_37C4C = 1568; 222 dword_37920 = 1576; 223 dword_37AC4 = 1568; 224 dword_377CC = 644; 225 dword_37894 = 48; 226 dword_377C4 = 43; 227 dword_377B0 = 34; 228 dword_377B4 = 408; 229 dword_377B8 = 81; 230 dword_377BC = 82; 231 dword_377C0 = 24; 232 dword_3788C = 920; 233 dword_37890 = 784; 234 dword_37898 = 328; 235 dword_3789C = 336; 236 dword_378A0 = 72; 237 DbgPrint("[kEvP64]Initialized version-specific data for Windows 8.1 SP%d\n", ServicePackMajor); 238 return STATUS_SUCCESS; 239 } 240 if ( MajorVersion == 10 && !MinorVersion ) 241 { 242 //WINDOWS_10 243 dword_378FC = 100; 244 dword_37C54 = -1; 245 dword_378E4 = -1; 246 dword_37C58 = 20; 247 dword_378F8 = 16; 248 dword_37C70 = 1048; 249 dword_37A90 = 736; 250 dword_378F0 = 1064; 251 dword_37C40 = 16; 252 dword_37C50 = 40; 253 dword_37AC0 = 24; 254 dword_37908 = 48; 255 dword_37C10 = 16; 256 dword_378E0 = 17; 257 dword_37C44 = 184; 258 dword_37C48 = 1664; 259 dword_37BF4 = 1536; 260 dword_37C30 = 1664; 261 dword_37C14 = 1536; 262 dword_37930 = 195; 263 dword_378F4 = 388; 264 dword_37BF8 = 562; 265 dword_37888 = 760; 266 dword_37AB0 = 1584; 267 dword_37C4C = 1576; 268 dword_37920 = 1584; 269 dword_37AC4 = 1576; 270 dword_377CC = 644; 271 dword_37894 = 48; 272 dword_377C4 = 44; 273 dword_377B0 = 35; 274 dword_377B4 = 416; 275 dword_377B8 = 82; 276 dword_377BC = 83; 277 dword_377C0 = 25; 278 dword_3788C = 936; 279 dword_37890 = 784; 280 dword_37898 = 328; 281 dword_3789C = 336; 282 dword_378A0 = 72; 283 return STATUS_SUCCESS; 284 } 285 if ( MajorVersion == 10 && MajorVersion || MinorVersion > 0xA ) 286 { 287 dword_378FC = -1; 288 result = 3221225659; 289 } 290 else 291 { 292 result = 3221225659; 293 } 294 return result; 295 }
因为代码比较长,我默认折叠了,想看的可以看下。
我们继续往下看了,接下来主要做了如下几件事:
- 创建一个设备
- 创建符号链接
- 设置设备分发函数
- 注册一个过滤驱动
首先作者实现了一个获取导出函数地址的函数,我想这是因为作者想使用一些已经导出但是没有在WDK文档中的函数吧。我这里取名为GetFuncAddress了。我用C重写了一下这个函数,如下
//根据反汇编写的 PVOID GetFuncAddress(WCHAR *Name) { ULONG IRQL; UNICODE_STRING UnicodeFindName; WCHAR *BufPointer=Name; if(KeGetCurrentirql()>1) { IRQL=KeGetCurrentirql(); DbgPrint("EX: Pageable code called at IRQL %d\n", IRQL); _asm{int 0x2C}; } RtlInitUnicodeString(&UnicodeFindName,BufPointer); return MmGetSystemRoutineAddress(&UnicodeFindName); }
然后作者用这个函数获取一些函数的地址(先判断了一下系统的版本),函数的列表如下
"ExfUnblockPushLock" "ObGetObjectType" "ObDereferenceObject" "PsAcquireProcessExitSynchronization" "PsIsProtectedProcess" "PsReleaseProcessExitSynchronization" "PsResumeProcess" "PsSuspendProcess" "KeSetAffinityThread"
这些部分都在第39行的sub_39010();函数中,程序判断完系统版本后马上就执行了这个函数。
这个函数的伪代码如下
1 __int64 sub_39010() 2 { 3 unsigned __int8 v0; // al@2 4 __int64 result; // rax@15 5 6 if ( (signed int)(unsigned __int8)sub_11030() > 1 ) 7 { 8 v0 = sub_11030(); 9 DbgPrint("EX: Pageable code called at IRQL %d\n", v0); 10 sub_11020(); 11 } 12 if ( (unsigned int)dword_378FC >= 0x3E ) 13 qword_37AB8 = (__int64)sub_392B0(L"ExfUnblockPushLock"); 14 qword_37928 = (__int64)sub_392B0(L"ObGetObjectType"); 15 qword_378E8 = (__int64)sub_392B0(L"ObDereferenceObject"); 16 qword_378D8 = (__int64)sub_392B0(L"PsAcquireProcessExitSynchronization"); 17 qword_37A98 = (__int64)sub_392B0(L"PsIsProtectedProcess"); 18 qword_37C38 = (__int64)sub_392B0(L"PsReleaseProcessExitSynchronization"); 19 qword_37900 = (__int64)sub_392B0(L"PsResumeProcess"); 20 qword_37C20 = (__int64)sub_392B0(L"PsSuspendProcess"); 21 qword_378B8 = (__int64)sub_392B0(L"KeSetAffinityThread"); 22 sub_26AD0((__int64)qword_39A00, (__int64)"KfdClassify2"); 23 sub_26AD0((__int64)qword_39A00, (__int64)"GetCalloutEntry "); 24 sub_26AD0((__int64)qword_39A00, (__int64)"InitDefaultCallout "); 25 qword_377F8 = sub_26AD0((__int64)qword_39A70, (__int64)"KeInsertQueueApc"); 26 if ( !qword_377F8 ) 27 qword_377F8 = (__int64)sub_392B0(L"KeInsertQueueApc"); 28 qword_37800 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetCreateProcessNotifyRoutine"); 29 if ( !qword_37800 ) 30 qword_37800 = (__int64)sub_392B0(L"PsSetCreateProcessNotifyRoutine"); 31 qword_37808 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetCreateThreadNotifyRoutine"); 32 if ( !qword_37808 ) 33 qword_37808 = (__int64)sub_392B0(L"PsSetCreateThreadNotifyRoutine"); 34 qword_37810 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetLoadImageNotifyRoutine"); 35 if ( !qword_37810 ) 36 qword_37810 = (__int64)sub_392B0(L"PsSetLoadImageNotifyRoutine"); 37 qword_37818 = sub_26AD0((__int64)qword_39A70, (__int64)"CmUnRegisterCallback"); 38 if ( !qword_37818 ) 39 qword_37818 = (__int64)sub_392B0(L"CmUnRegisterCallback"); 40 result = sub_26AD0((__int64)qword_39A70, (__int64)"IoRegisterShutdownNotification"); 41 qword_37820 = result; 42 if ( !result ) 43 { 44 result = (__int64)sub_392B0(L"IoRegisterShutdownNotification"); 45 qword_37820 = result; 46 } 47 return result; 48 }
这个函数首先判断了一下IRQL这个在之前就已经分析过了,然后判断了一下系统版本,通过验证之后用自己实现的GetFuncAddress函数来获取这些函数的地址,并把地址储存在全局变量中sub_392B0就是GetFuncAddress(),然后又调用了sub_26AD0()这个函数,这个函数中又调用了这个函数
1 //IDA伪代码,被sub_39010()调用 2 PVOID __fastcall sub_260D0(unsigned int a1) 3 { 4 PVOID result; // rax@3 5 int v2; // [sp+20h] [bp-28h]@4 6 int v3; // [sp+24h] [bp-24h]@4 7 SIZE_T NumberOfBytes; // [sp+28h] [bp-20h]@1 8 PVOID P; // [sp+30h] [bp-18h]@2 9 unsigned int v6; // [sp+50h] [bp+8h]@1 10 11 v6 = a1; 12 for ( LODWORD(NumberOfBytes) = 256; ; LODWORD(NumberOfBytes) = v3 + 256 ) 13 { 14 P = ExAllocatePool(0, (unsigned int)NumberOfBytes); 15 if ( !P ) 16 return 0i64; 17 v3 = 0; 18 v2 = ZwQuerySystemInformation(v6, P, (unsigned int)NumberOfBytes, &v3); 19 if ( v2 == -1073741820 ) 20 { 21 ExFreePoolWithTag(P, 0); 22 P = 0i64; 23 if ( v3 ) 24 continue; 25 } 26 break; 27 } 28 if ( v2 >= 0 ) 29 { 30 result = P; 31 } 32 else 33 { 34 sub_199F0(P); 35 result = 0i64; 36 } 37 return result; 38 }
这是调用ZwQuerySystemInformation()函数的11号功能也就是SystemModuleInformation功能,这个函数经常在安全类程序中调用,作为一个常规的枚举模块的方法。用循环是为了让ZwQuerySystemInformation返回需要的合适的大小。这个函数返回的结构如下
1 typedef struct _SYSTEM_MODULE_INFORMATION { 2 ULONG Count; 3 SYSTEM_MODULE_INFORMATION_ENTRY Module[1]; 4 } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
Count为个数,有多少Count就有多少的SYSTEM_MODULE_INFORMATION_ENTRY,其中这个数组中的项的结构如下:
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY { HANDLE Section; PVOID MappedBase; PVOID Base; ULONG Size; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT PathLength; CHAR ImageName[256]; } SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
注意这个调用返回的第一个模块一定是ntoskrnl.exe模块,很多人就是通过这个调用去获取内核模块的信息的。
1 __int64 __fastcall sub_21DB0(const char *a1) 2 { 3 __int64 result; // rax@2 4 UNICODE_STRING String1; // [sp+20h] [bp-108h]@3 5 UNICODE_STRING String2; // [sp+30h] [bp-F8h]@12 6 NTSTATUS v4; // [sp+40h] [bp-E8h]@1 7 PCWSTR v5; // [sp+48h] [bp-E0h]@1 8 __int64 v6; // [sp+50h] [bp-D8h]@1 9 __int64 v7; // [sp+58h] [bp-D0h]@1 10 __int64 v8; // [sp+60h] [bp-C8h]@1 11 __int64 v9; // [sp+68h] [bp-C0h]@1 12 __int64 v10; // [sp+70h] [bp-B8h]@1 13 unsigned int i; // [sp+78h] [bp-B0h]@1 14 UNICODE_STRING v12; // [sp+80h] [bp-A8h]@1 15 UNICODE_STRING v13; // [sp+90h] [bp-98h]@19 16 STRING v14; // [sp+A0h] [bp-88h]@3 17 STRING v15; // [sp+B0h] [bp-78h]@8 18 PCWSTR v16; // [sp+C0h] [bp-68h]@1 19 __int64 v17; // [sp+C8h] [bp-60h]@1 20 __int64 v18; // [sp+D0h] [bp-58h]@1 21 __int64 v19; // [sp+D8h] [bp-50h]@1 22 UNICODE_STRING UnicodeString; // [sp+E0h] [bp-48h]@8 23 int j; // [sp+F0h] [bp-38h]@1 24 PVOID v22; // [sp+F8h] [bp-30h]@1 25 UNICODE_STRING DestinationString; // [sp+100h] [bp-28h]@1 26 __int64 v24; // [sp+110h] [bp-18h]@1 27 PCSZ SourceString; // [sp+130h] [bp+8h]@1 28 29 SourceString = a1; 30 v24 = 0i64; 31 v4 = 0; 32 i = 0; 33 j = 0; 34 v5 = L"hal.dll"; 35 v6 = (__int64)L"halacpi.dll"; 36 v7 = (__int64)L"halapic.dll"; 37 v8 = (__int64)L"halmps.dll"; 38 v9 = (__int64)L"halaacpi.dll"; 39 v10 = (__int64)L"halmacpi.dll"; 40 v16 = L"ntoskrnl.exe"; 41 v17 = (__int64)L"ntkrnlpa.exe"; 42 v18 = (__int64)L"ntkrnlmp.exe"; 43 v19 = (__int64)L"ntkrpamp.exe"; 44 v22 = 0i64; 45 RtlInitUnicodeString(&DestinationString, L"hal.dll"); 46 RtlInitUnicodeString(&v12, L"ntoskrnl.exe"); 47 v22 = sub_260D0(11u); 48 if ( v22 ) 49 { 50 RtlInitAnsiString(&v14, SourceString); 51 v4 = RtlAnsiStringToUnicodeString(&String1, &v14, 1u); 52 if ( v4 >= 0 ) 53 { 54 for ( i = 0; ; ++i ) 55 { 56 if ( i < *(_DWORD *)v22 ) 57 { 58 RtlInitAnsiString(&v15, (PCSZ)v22 + 296 * i + *((_WORD *)v22 + 148 * i + 23) + 48); 59 v4 = RtlAnsiStringToUnicodeString(&UnicodeString, &v15, 1u); 60 if ( v4 < 0 ) 61 continue; 62 if ( RtlEqualUnicodeString(&String1, &DestinationString, 0) ) 63 { 64 for ( j = 0; j < 6; ++j ) 65 { 66 RtlInitUnicodeString(&String2, (&v5)[4 * j]); 67 if ( RtlEqualUnicodeString(&UnicodeString, &String2, 0) ) 68 { 69 v24 = *((_QWORD *)v22 + 37 * i + 3); 70 break; 71 } 72 } 73 } 74 else if ( RtlEqualUnicodeString(&String1, &v12, 0) ) 75 { 76 for ( j = 0; j < 4; ++j ) 77 { 78 RtlInitUnicodeString(&v13, (&v16)[4 * j]); 79 if ( RtlEqualUnicodeString(&UnicodeString, &v13, 0) ) 80 { 81 v24 = *((_QWORD *)v22 + 37 * i + 3); 82 break; 83 } 84 } 85 } 86 else if ( RtlEqualUnicodeString(&String1, &UnicodeString, 0) ) 87 { 88 v24 = *((_QWORD *)v22 + 37 * i + 3); 89 } 90 RtlFreeUnicodeString(&UnicodeString); 91 if ( !v24 ) 92 continue; 93 } 94 break; 95 } 96 RtlFreeUnicodeString(&String1); 97 sub_199F0(v22); 98 result = v24; 99 } 100 else 101 { 102 result = 0i64; 103 } 104 } 105 else 106 { 107 result = 0i64; 108 } 109 return result; 110 }
这个是遍历刚才得到的结果,就是刚才得到的SYSTEM_MODULE_INFORMATION结构。如果要得到的目标模块是nt内核模块或是hal.dll的话就特殊处理
0x1
接下来就是进入正题了,来看看PT的分发例程,在看分发例程之前先总结下,如下图
图中的真正的功能函数就是我们要分析的重点,PT的大部分功能都在这里完成。
0x2
我们来看switch语句,这个语句由最后的一个 DbgPrint("[kEvP64] Unknown IOCTL: 0x%X (%04X,%04X)\r\n", v37, (v37 & 0xFFFF0000) >> 16, (v37 >> 2) & 0xFFF);就可以看出是DeviceIoControl函数来发送的一些自定义的IOCTL。但是我们没有办法知道具体的含义,因为这些都是自定义的,在C中定义IOCTL也只是用一个宏来完成。
来看下这里
1 //IDA伪代码 2 Irp = a2; 3 v35 = -1073741808; 4 v27 = 0i64; 5 v28 = sub_11100((__int64)a2);
注意第5行的sub_11100,参数a2是Irp指针,它的反汇编如下图
只是一个简单的移位操作是吧。我猜这个应该就是IoGetCurrentIrpStackLocation()这个函数,可见IRP中是存有当前IRP栈的指针的。首先是对比Irp栈的总层数是不是大于当前层数,然后就是取出当前栈的指针(这个指针在Irp结构中)返回了。
而我们常用的操作就是
PIO_STACK_LOCATION irpSp; IoControlCode=irpSp->Parameters.DeviceIoControl.IoControlCode; switch(IoControlCode) { …… }