相比 X86, X64的系统调用要兼容X86, 所有有些不一样
这里我写了一个X86小程序, 程序只是调用了 ReadProcessMemory 函数, 开始追踪
操作系统: Win7Sp1
核数: 2
1). 追踪 ReadProcessMemory 的系统调用 (使用EXE为 x86体系)
进入 kernel32.dll 的代码中发现, 只是一个跳转
跟随跳转来到了 kernelbase.dll, 其调用了 ntdll(32位).NtReadProcessMemory
继续跟随, 发现类似 X86 体系的系统调用流程
其中 EAX 等于系统服务号
EDX 等于函数参数起始地址
FS指向的是 TEB32 结构, TEB32[0xC0] == WOW32Reserved == 0x74C92320
之后进行了长跳, 这里 0x33 段描述符是 X86 转 X64 体系的特殊兼容段描述符, 并跳向了0x74C9271E
继续跟随发现变为X64寻址, 并进入了wow64cpu.dll
我们来分析这段代码, 用 IDA64 追踪查看 wow64cpu.dll, 在 CpuSimulate() 函数中
mov r8d, [esp] // R8D == WOW32Reserved 的返回地址
mov [r13+0BCh], r8d // 保存 WOW32Reserved 的返回地址 (猜测 R13 是个结构体, 保存在 TEB64.TlsSlots[0x1] 处)
mov [r13+0C8h], esp // 保存当前 R3 的ESP
mov rsp, [r12+1480h] // Rsp = TEB64.TlsSlots[0x0]
and qword ptr [r12+1480h], 0 TEB64.TlsSlots[0x0] &= 0x0 (清空 TEB64.TlsSlots[0x0])
mov r11d, edx // R11D == 函数参数起始地址
jmp qword ptr [r15+rcx*8] // R15 == TurboDispatchJumpAddressEnd()函数起始地址
// RCX == 0x0
// Jmp 到 TurboDispatchJumpAddressEnd() 函数
.text:0000000078B62749 public TurboDispatchJumpAddressEnd
.text:0000000078B62749 TurboDispatchJumpAddressEnd: ; CODE XREF: CpuSimulate+32Cj
.text:0000000078B62749 ; CpuSimulate+3E6j
.text:0000000078B62749 ; DATA XREF: ...
.text:0000000078B62749 41 89 B5 A4 00 00 00 mov [r13+0A4h], esi // 保存 ESI
.text:0000000078B62750 41 89 BD A0 00 00 00 mov [r13+0A0h], edi // 保存 EDI
.text:0000000078B62757 41 89 9D A8 00 00 00 mov [r13+0A8h], ebx // 保存 EBX
.text:0000000078B6275E 41 89 AD B8 00 00 00 mov [r13+0B8h], ebp // 保存 EBP
.text:0000000078B62765 9C pushfq // Push RFLAGS寄存器
.text:0000000078B62766 5B pop rbx // RBX = RFLAGS寄存器
.text:0000000078B62767 41 89 9D C4 00 00 00 mov [r13+0C4h], ebx // 保存 RFLAGS 寄存器
.text:0000000078B6276E 8B C8 mov ecx, eax // ECX = 系统服务号(X64 FASTCALL)
.text:0000000078B62770 FF 15 0A E9 FF FF call cs:Wow64SystemServiceEx // X64的系统调用(位于 wow64.dll)
.text:0000000078B62776 41 89 85 B4 00 00 00 mov [r13+0B4h], eax // 存储 Wow64SystemServiceEx 返回值
.text:0000000078B6277D E9 8F FE FF FF jmp loc_78B62611
// 分析返回流程
.text:0000000078B62611 loc_78B62611: ; CODE XREF: CpuSimulate+1CDj
.text:0000000078B62611 41 83 A5 D0 02 00 00 01 and dword ptr [r13+2D0h], 1
.text:0000000078B62619 0F 84 AF 00 00 00 jz loc_78B626CE
.text:0000000078B6261F 41 0F 28 85 70 01 00 00 movaps xmm0, xmmword ptr [r13+170h]
.text:0000000078B62627 41 0F 28 8D 80 01 00 00 movaps xmm1, xmmword ptr [r13+180h]
.text:0000000078B6262F 41 0F 28 95 90 01 00 00 movaps xmm2, xmmword ptr [r13+190h]
.text:0000000078B62637 41 0F 28 9D A0 01 00 00 movaps xmm3, xmmword ptr [r13+1A0h]
.text:0000000078B6263F 41 0F 28 A5 B0 01 00 00 movaps xmm4, xmmword ptr [r13+1B0h]
.text:0000000078B62647 41 0F 28 AD C0 01 00 00 movaps xmm5, xmmword ptr [r13+1C0h]
.text:0000000078B6264F 41 8B 8D B0 00 00 00 mov ecx, [r13+0B0h]
.text:0000000078B62656 41 8B 95 AC 00 00 00 mov edx, [r13+0ACh]
.text:0000000078B6265D 41 83 A5 D0 02 00 00 FE and dword ptr [r13+2D0h], 0FFFFFFFEh
.text:0000000078B62665 41 8B BD A0 00 00 00 mov edi, [r13+0A0h]
.text:0000000078B6266C 41 8B B5 A4 00 00 00 mov esi, [r13+0A4h]
.text:0000000078B62673 41 8B 9D A8 00 00 00 mov ebx, [r13+0A8h]
.text:0000000078B6267A 41 8B AD B8 00 00 00 mov ebp, [r13+0B8h]
.text:0000000078B62681 41 8B 85 B4 00 00 00 mov eax, [r13+0B4h]
.text:0000000078B62688 49 89 A4 24 80 14 00 00 mov [r12+1480h], rsp
.text:0000000078B62690 66 C7 44 24 08 23 00 mov [rsp+0B8h+var_B0], 23h
.text:0000000078B62697 66 C7 44 24 20 2B 00 mov word ptr [rsp+0B8h+var_98], 2Bh
.text:0000000078B6269E 45 8B 85 C4 00 00 00 mov r8d, [r13+0C4h]
.text:0000000078B626A5 41 81 A5 C4 00 00 00 FF+ and dword ptr [r13+0C4h], 0FFFFFEFFh
.text:0000000078B626B0 44 89 44 24 10 mov [rsp+0B8h+var_A8], r8d
.text:0000000078B626B5 45 8B 85 C8 00 00 00 mov r8d, [r13+0C8h]
.text:0000000078B626BC 4C 89 44 24 18 mov [rsp+0B8h+var_A0], r8
.text:0000000078B626C1 45 8B 85 BC 00 00 00 mov r8d, [r13+0BCh]
.text:0000000078B626C8 4C 89 04 24 mov [rsp+0B8h+var_B8], r8
.text:0000000078B626CC 48 CF iretq
.text:0000000078B626CE loc_78B626CE: (兼容模式从这返回) ; CODE XREF: CpuSimulate+69j
.text:0000000078B626CE 41 8B BD A0 00 00 00 mov edi, [r13+0A0h]
.text:0000000078B626D5 41 8B B5 A4 00 00 00 mov esi, [r13+0A4h]
.text:0000000078B626DC 41 8B 9D A8 00 00 00 mov ebx, [r13+0A8h]
.text:0000000078B626E3 41 8B AD B8 00 00 00 mov ebp, [r13+0B8h]
.text:0000000078B626EA 41 8B 85 B4 00 00 00 mov eax, [r13+0B4h]
.text:0000000078B626F1 49 89 A4 24 80 14 00 00 mov [r12+1480h], rsp
.text:0000000078B626F9 41 C7 46 04 23 00 00 00 mov dword ptr [r14+4], 23hr13
.text:0000000078B62701 41 B8 2B 00 00 00 mov r8d, 2Bh
.text:0000000078B62707 41 8E D0 mov ss, r8d
.text:0000000078B6270A 41 8B A5 C8 00 00 00 mov esp, [r13+0C8h]
.text:0000000078B62711 45 8B 8D BC 00 00 00 mov r9d, [r13+0BCh]
.text:0000000078B62718 45 89 0E mov [r14], r9d
.text:0000000078B6271B 41 FF 2E jmp fword ptr [r14] // 注意这个返回, 动态分析R14存储的是 0x23 : 0x772CFE92(之前保存的 WOW32Reserved 的返回地址)
// 0x23 这个段描述符和 0x33 相反, 是将X64寻址转为X86寻址
// 返回到了 ntdll(32位).NtReadProcessMemory 调用 WOW32Reserved 的下一个地址(WOW32Reserved 的返回地址)
// 分析 wow64.dll 的 Wow64SystemServiceEx() 函数
.text:0000000078BDCEB0 public Wow64SystemServiceEx
.text:0000000078BDCEB0 Wow64SystemServiceEx proc near ; CODE XREF: Wow64SystemService+6j
.text:0000000078BDCEB0 ; DATA XREF: .text:off_78C07E28o ...
.text:0000000078BDCEB0
.text:0000000078BDCEB0 var_898 = dword ptr -898h
.text:0000000078BDCEB0 var_888 = qword ptr -888h
.text:0000000078BDCEB0 var_880 = qword ptr -880h
.text:0000000078BDCEB0 var_878 = byte ptr -878h
.text:0000000078BDCEB0 var_870 = qword ptr -870h
.text:0000000078BDCEB0 var_FindServiceTableIndex= dword ptr -868h
.text:0000000078BDCEB0 var_SerivceTableFunIndex= dword ptr -864h
.text:0000000078BDCEB0 var_860 = dword ptr -860h
.text:0000000078BDCEB0 var_85C = byte ptr -85Ch
.text:0000000078BDCEB0 var_848 = qword ptr -848h
.text:0000000078BDCEB0 var_840 = qword ptr -840h
.text:0000000078BDCEB0 var_28 = qword ptr -28h
.text:0000000078BDCEB0 arg_10 = qword ptr 18h
.text:0000000078BDCEB0
.text:0000000078BDCEB0 4C 8B DC mov r11, rsp ; R11 == &(Wow64SystemServiceEx返回地址)
.text:0000000078BDCEB3 49 89 5B 18 mov [r11+18h], rbx ; 保存 RFLAGS 寄存器
.text:0000000078BDCEB7 56 push rsi
.text:0000000078BDCEB8 57 push rdi
.text:0000000078BDCEB9 41 54 push r12
.text:0000000078BDCEBB 48 81 EC A0 08 00 00 sub rsp, 8A0h
.text:0000000078BDCEC2 48 8B 05 37 C2 02 00 mov rax, cs:__security_cookie
.text:0000000078BDCEC9 48 33 C4 xor rax, rsp
.text:0000000078BDCECC 48 89 84 24 90 08 00 00 mov [rsp+890h], rax
.text:0000000078BDCED4 48 8B DA mov rbx, rdx ; Rbx == 函数参数起始地址
.text:0000000078BDCED7 44 8B C1 mov r8d, ecx ; R8d == 系统服务号
.text:0000000078BDCEDA 8B D1 mov edx, ecx ; edx == 系统服务号
.text:0000000078BDCEDC C1 EA 0C shr edx, 0Ch ; 系统服务号 >> 12位
.text:0000000078BDCEDF 83 E2 03 and edx, 3 ; Rdx = 获取查找哪张表的(从这里可以判断最多只有3张表)
.text:0000000078BDCEE2 41 81 E0 FF 0F 00 00 and r8d, 0FFFh ; R8 = 获取系统服务号低12位
.text:0000000078BDCEE9 4C 8D 0C 52 lea r9, [rdx+rdx*2]
.text:0000000078BDCEED 4D 03 C9 add r9, r9 ; R9 = 6*Rdx
.text:0000000078BDCEF0 4C 8D 15 09 DB 02 00 lea r10, ServiceTables
.text:0000000078BDCEF7 47 3B 44 CA 10 cmp r8d, [r10+r9*8+10h] ; 系统服务号 与 指定服务表的函数数量比较
.text:0000000078BDCEFC 0F 87 3F 01 00 00 ja loc_78BDD041 ; 越界则走向函数结束
.text:0000000078BDCF02 65 48 8B 34 25 30 00 00+ mov rsi, gs:30h ; Rsi = TEB64
.text:0000000078BDCF0B 48 89 74 24 38 mov [rsp+38h], rsi
.text:0000000078BDCF10 65 48 8B 3C 25 30 00 00+ mov rdi, gs:30h
.text:0000000078BDCF19 48 81 C7 00 20 00 00 add rdi, 2000h
.text:0000000078BDCF20 48 89 7C 24 30 mov [rsp+30h], rdi
.text:0000000078BDCF25 48 8B 86 98 14 00 00 mov rax, [rsi+1498h]
.text:0000000078BDCF2C 48 89 44 24 70 mov [rsp+70h], rax
.text:0000000078BDCF31 48 8D 44 24 78 lea rax, [rsp+78h]
.text:0000000078BDCF36 49 89 83 C8 F7 FF FF mov [r11-838h], rax
.text:0000000078BDCF3D 48 8D 44 24 78 lea rax, [rsp+78h]
.text:0000000078BDCF42 48 89 44 24 78 mov [rsp+78h], rax
.text:0000000078BDCF47 49 8D 43 D8 lea rax, [r11-28h]
.text:0000000078BDCF4B 49 89 83 D0 F7 FF FF mov [r11-830h], rax
.text:0000000078BDCF52 48 8D 44 24 70 lea rax, [rsp+8B8h+var_848]
.text:0000000078BDCF57 48 89 86 98 14 00 00 mov [rsi+1498h], rax
.text:0000000078BDCF5E 4B 8B 04 CA mov rax, [r10+r9*8] ; Rax = ServiceTable函数地址表
.text:0000000078BDCF62 4E 8B 24 C0 mov r12, [rax+r8*8] ; R12 = 获取指定服务号的函数地址
.text:0000000078BDCF66 89 54 24 50 mov [rsp+8B8h+var_FindServiceTableIndex], edx
.text:0000000078BDCF6A 44 89 44 24 54 mov [rsp+8B8h+var_SerivceTableFunIndex], r8d
.text:0000000078BDCF6F 8B 47 34 mov eax, [rdi+34h]
.text:0000000078BDCF72 89 46 68 mov [rsi+68h], eax
.text:0000000078BDCF75 48 8B 05 1C E0 02 00 mov rax, cs:pfnWow64LogSystemService
.text:0000000078BDCF7C 48 85 C0 test rax, rax
.text:0000000078BDCF7F 75 0E jnz short loc_78BDCF8F
.text:0000000078BDCF81 48 8B CB mov rcx, rbx ; Rcx = 函数参数起始地址
.text:0000000078BDCF84 41 FF D4 call r12 ; 调用对应的系统服务函数
.text:0000000078BDCF87 8B D8 mov ebx, eax
.text:0000000078BDCF89 89 44 24 20 mov [rsp+8B8h+var_898], eax
.text:0000000078BDCF8D EB 31 jmp short loc_78BDCFC0 // 返回
// 从上面可以看出相比X86, X64在Ring 3中也有一张 ServiceTables 用于查询3环对应的系统服务函数地址, 这里Ring 3调用的是 ReadProcessMemory() 函数
// 跟随动态调试进入会发现进入了 wow64.whNtReadVirtualMemory() 函数中, 我们接下来分析
.text:0000000078BEAC78 whNtReadVirtualMemory proc near ; DATA XREF: .data:0000000078C09480o
.text:0000000078BEAC78 ; .pdata:0000000078C0B948o
.text:0000000078BEAC78
.text:0000000078BEAC78 ReturnLength64_1= qword ptr -38h
.text:0000000078BEAC78 arg_0 = qword ptr 8
.text:0000000078BEAC78 ReturnLength64 = byte ptr 10h
.text:0000000078BEAC78 arg_10 = qword ptr 18h
.text:0000000078BEAC78
.text:0000000078BEAC78 48 89 5C 24 18 mov [rsp+arg_10], rbx ; Rbx = Rcx = 函数参数起始地址
.text:0000000078BEAC7D 56 push rsi
.text:0000000078BEAC7E 57 push rdi
.text:0000000078BEAC7F 41 54 push r12
.text:0000000078BEAC81 41 55 push r13
.text:0000000078BEAC83 41 56 push r14
.text:0000000078BEAC85 48 83 EC 30 sub rsp, 30h
.text:0000000078BEAC89
.text:0000000078BEAC89 loc_78BEAC89: ; DATA XREF: .text:0000000078C00DC8o
.text:0000000078BEAC89 4C 63 21 movsxd r12, dword ptr [rcx] ; ProcessHandle
.text:0000000078BEAC8C 8B 71 04 mov esi, [rcx+4] ; BaseAddress
.text:0000000078BEAC8F 8B 79 08 mov edi, [rcx+8] ; Buffer
.text:0000000078BEAC92 8B 59 0C mov ebx, [rcx+0Ch] ; BufferLength
.text:0000000078BEAC95 44 8B 69 10 mov r13d, [rcx+10h] ; ReturnLength
.text:0000000078BEAC99 49 8B D5 mov rdx, r13
.text:0000000078BEAC9C 48 8D 4C 24 68 lea rcx, [rsp+58h+ReturnLength64]
.text:0000000078BEACA1 E8 E6 04 FF FF call Wow64ShallowThunkSIZE_T32TO64 ; 将32位的ReturnLength切换位64位
.text:0000000078BEACA6 4C 8B F0 mov r14, rax ; R14 = ReturnLength64
.text:0000000078BEACA9 48 89 44 24 20 mov [rsp+58h+ReturnLength64_1], rax
.text:0000000078BEACAE 4C 8B CB mov r9, rbx ; BufferLength
.text:0000000078BEACB1 4C 8B C7 mov r8, rdi ; Buffer
.text:0000000078BEACB4 48 8B D6 mov rdx, rsi ; BaseAddress
.text:0000000078BEACB7 49 8B CC mov rcx, r12 ; ProcessHandle
.text:0000000078BEACBA FF 15 40 64 FE FF call cs:__imp_NtReadVirtualMemory ; 调用64位ntdll.dll的函数
.text:0000000078BEACC0 44 8B D8 mov r11d, eax
.text:0000000078BEACC3 4D 85 F6 test r14, r14
.text:0000000078BEACC6 75 07 jnz short loc_78BEACCF
.text:0000000078BEACC8 4C 21 74 24 60 and [rsp+58h+arg_0], r14
.text:0000000078BEACCD EB 15 jmp short loc_78BEACE4
.text:0000000078BEACCF ; ---------------------------------------------------------------------------
.text:0000000078BEACCF
.text:0000000078BEACCF loc_78BEACCF: ; CODE XREF: whNtReadVirtualMemory+4Ej
.text:0000000078BEACCF B8 FF FF FF FF mov eax, 0FFFFFFFFh
.text:0000000078BEACD4 49 39 06 cmp [r14], rax
.text:0000000078BEACD7 49 0F 42 06 cmovb rax, [r14]
.text:0000000078BEACDB 41 89 45 00 mov [r13+0], eax
.text:0000000078BEACDF 4C 89 6C 24 60 mov [rsp+58h+arg_0], r13
.text:0000000078BEACE4
.text:0000000078BEACE4 loc_78BEACE4: ; CODE XREF: whNtReadVirtualMemory+55j
.text:0000000078BEACE4 ; DATA XREF: .text:0000000078C00DC8o
.text:0000000078BEACE4 41 8B C3 mov eax, r11d
.text:0000000078BEACE7 48 8B 5C 24 70 mov rbx, [rsp+58h+arg_10]
.text:0000000078BEACEC 48 83 C4 30 add rsp, 30h
.text:0000000078BEACF0 41 5E pop r14
.text:0000000078BEACF2 41 5D pop r13
.text:0000000078BEACF4 41 5C pop r12
.text:0000000078BEACF6 5F pop rdi
.text:0000000078BEACF7 5E pop rsi
.text:0000000078BEACF8 C3 retn
.text:0000000078BEACF8 whNtReadVirtualMemory endp
// 注意这个函数中的 Wow64ShallowThunkSIZE_T32TO64() 函数, 将用户传入的 ReturnLength 变为64位, 随后调用了64位 ntdll.dll 的 NtReadVirtualMemory() 函数
// 这里利用了 syscall 指令进入了内核, 注意 EAX 等于系统服务号
// 从这里可以看出Ring 3的 ServiceTables 和 Ring 0 的 ServiceTables 用的 系统服务号 可能不一致
.text:0000000078EA1700 public NtReadVirtualMemory
.text:0000000078EA1700 NtReadVirtualMemory proc near ; CODE XREF: sub_78F222D0+24p
.text:0000000078EA1700 ; sub_78F28520+3F8p
.text:0000000078EA1700 ; DATA XREF: ...
.text:0000000078EA1700 mov r10, rcx ; NtReadVirtualMemory
.text:0000000078EA1703 mov eax, 3Ch
.text:0000000078EA1708 syscall
.text:0000000078EA170A retn
.text:0000000078EA170A NtReadVirtualMemory endp
总结:
相比X86体系, X64体系做了一系列的扩展, Ring3 有套系统服务表寻址, 查询Ring3 系统服务号对应的函数,之后调用 64位 ntdll.dll 进入内核
总体的调用流程:
32位的EXE调用 ReadProcessMemory --> 32位的kernel32.dll --> 32位的kernelbase.dll --> 32位的ntdll.dll -(0x3C服务号)-> 64位的wow64cpu.dll --> 64位的wow64.dll(根据服务号查询对应函数) --> 64位ntdll.dll -(0x3C服务号)-> 内核
阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开
发者可享99元/年,续费同价!
最后于 2020-6-3 21:51
被灵幻空间编辑
,原因: 排版问题