好友
阅读权限20
听众
最后登录1970-1-1
|
本帖最后由 oxygen1a1 于 2022-11-15 16:19 编辑
0x1 前言
我们学过x64的系统调用,是非常简单的,直接syscall,找SSDT表,然后复制参数,调用就行了。
和x86架构基本是完全一样的。
但是Wow64是完全不一样的,他是要经过天堂之门的。 也就是jmp far 33:xx
0x2 WOW64调用分析流程
0x2-1 x86下的OpenProcess的 NtOpenProcess
比如我们随便下个OpenProcess断点,
首先他是kernel32中,在x64中,kernel32都不干事,直接是去kernelbase的OpenProcess
kernel32.OpenProcess->kernelbase.OpenProcess->ntdll.OpenProcess call edx(常量)->ntdll.Wow64Transtaion-> 天堂之门
[Asm] 纯文本查看 复制代码 77CA2D50 NtOpenProcess B8 26 00 00 00 mov eax,0x00000026
77CA2D55 BA 40 8B CB 77 mov edx,ntdll.77CB8B40
77CA2D5A FF D2 call edx
77CA2D5C C2 10 00 retn 0x0010
可以看到,他直接还是eax SSDT Index
然后call edx,这是win10下的,win7直接是call 三环fs:[0xC0]的地方
也就是_TEB32.
//0x1000 bytes (sizeof)
[Asm] 纯文本查看 复制代码 struct _TEB32
{
struct _NT_TIB32 NtTib; //0x0
ULONG EnvironmentPointer; //0x1c
struct _CLIENT_ID32 ClientId; //0x20
ULONG ActiveRpcHandle; //0x28
ULONG ThreadLocalStoragePointer; //0x2c
ULONG ProcessEnvironmentBlock; //0x30
ULONG LastErrorValue; //0x34
ULONG CountOfOwnedCriticalSections; //0x38
ULONG CsrClientThread; //0x3c
ULONG Win32ThreadInfo; //0x40
ULONG User32Reserved[26]; //0x44
ULONG UserReserved[5]; //0xac
ULONG WOW32Reserved; //0xc0
ULONG CurrentLocale; //0xc4
ULONG FpSoftwareStatusRegister; //0xc8
ULONG ReservedForDebuggerInstrumentation[16]; //0xcc
ULONG SystemReserved1[26]; //0x10c
CHAR PlaceholderCompatibilityMode; //0x174
UCHAR PlaceholderHydrationAlwaysExplicit; //0x175
CHAR PlaceholderReserved[10]; //0x176
ULONG ProxiedProcessId; //0x180
struct _ACTIVATION_CONTEXT_STACK32 _ActivationStack; //0x184
UCHAR WorkingOnBehalfTicket[8]; //0x19c
LONG ExceptionCode; //0x1a4
ULONG ActivationContextStackPointer; //0x1a8
ULONG InstrumentationCallbackSp; //0x1ac
ULONG InstrumentationCallbackPreviousPc; //0x1b0
ULONG InstrumentationCallbackPreviousSp; //0x1b4
UCHAR InstrumentationCallbackDisabled; //0x1b8
UCHAR SpareBytes[23]; //0x1b9
ULONG TxFsContext; //0x1d0
struct _GDI_TEB_BATCH32 GdiTebBatch; //0x1d4
struct _CLIENT_ID32 RealClientId; //0x6b4
ULONG GdiCachedProcessHandle; //0x6bc
ULONG GdiClientPID; //0x6c0
ULONG GdiClientTID; //0x6c4
ULONG GdiThreadLocalInfo; //0x6c8
ULONG Win32ClientInfo[62]; //0x6cc
ULONG glDispatchTable[233]; //0x7c4
ULONG glReserved1[29]; //0xb68
ULONG glReserved2; //0xbdc
ULONG glSectionInfo; //0xbe0
ULONG glSection; //0xbe4
ULONG glTable; //0xbe8
ULONG glCurrentRC; //0xbec
ULONG glContext; //0xbf0
ULONG LastStatusValue; //0xbf4
struct _STRING32 StaticUnicodeString; //0xbf8
WCHAR StaticUnicodeBuffer[261]; //0xc00
ULONG DeallocationStack; //0xe0c
ULONG TlsSlots[64]; //0xe10
struct LIST_ENTRY32 TlsLinks; //0xf10
ULONG Vdm; //0xf18
ULONG ReservedForNtRpc; //0xf1c
ULONG DbgSsReserved[2]; //0xf20
ULONG HardErrorMode; //0xf28
ULONG Instrumentation[9]; //0xf2c
struct _GUID ActivityId; //0xf50
ULONG SubProcessTag; //0xf60
ULONG PerflibData; //0xf64
ULONG EtwTraceData; //0xf68
ULONG WinSockData; //0xf6c
ULONG GdiBatchCount; //0xf70
union
{
struct _PROCESSOR_NUMBER CurrentIdealProcessor; //0xf74
ULONG IdealProcessorValue; //0xf74
struct
{
UCHAR ReservedPad0; //0xf74
UCHAR ReservedPad1; //0xf75
UCHAR ReservedPad2; //0xf76
UCHAR IdealProcessor; //0xf77
};
};
ULONG GuaranteedStackBytes; //0xf78
ULONG ReservedForPerf; //0xf7c
ULONG ReservedForOle; //0xf80
ULONG WaitingOnLoaderLock; //0xf84
ULONG SavedPriorityState; //0xf88
ULONG ReservedForCodeCoverage; //0xf8c
ULONG ThreadPoolData; //0xf90
ULONG TlsExpansionSlots; //0xf94
ULONG MuiGeneration; //0xf98
ULONG IsImpersonating; //0xf9c
ULONG NlsCache; //0xfa0
ULONG pShimData; //0xfa4
ULONG HeapData; //0xfa8
ULONG CurrentTransactionHandle; //0xfac
ULONG ActiveFrame; //0xfb0
ULONG FlsData; //0xfb4
ULONG PreferredLanguages; //0xfb8
ULONG UserPrefLanguages; //0xfbc
ULONG MergedPrefLanguages; //0xfc0
ULONG MuiImpersonation; //0xfc4
union
{
volatile USHORT CrossTebFlags; //0xfc8
USHORT SpareCrossTebBits:16; //0xfc8
};
union
{
USHORT SameTebFlags; //0xfca
struct
{
USHORT SafeThunkCall:1; //0xfca
USHORT InDebugPrint:1; //0xfca
USHORT HasFiberData:1; //0xfca
USHORT SkipThreadAttach:1; //0xfca
USHORT WerInShipAssertCode:1; //0xfca
USHORT RanProcessInit:1; //0xfca
USHORT ClonedThread:1; //0xfca
USHORT SuppressDebugMsg:1; //0xfca
USHORT DisableUserStackWalk:1; //0xfca
USHORT RtlExceptionAttached:1; //0xfca
USHORT InitialThread:1; //0xfca
USHORT SessionAware:1; //0xfca
USHORT LoadOwner:1; //0xfca
USHORT LoaderWorker:1; //0xfca
USHORT SkipLoaderInit:1; //0xfca
USHORT SpareSameTebBits:1; //0xfca
};
};
ULONG TxnScopeEnterCallback; //0xfcc
ULONG TxnScopeExitCallback; //0xfd0
ULONG TxnScopeContext; //0xfd4
ULONG LockCount; //0xfd8
LONG WowTebOffset; //0xfdc
ULONG ResourceRetValue; //0xfe0
ULONG ReservedForWdf; //0xfe4
ULONGLONG ReservedForCrt; //0xfe8
struct _GUID EffectiveContainerId; //0xff0
};
可以看到,他是Call的Wow32Reserved,这个里面本质就是放了个天堂之门的Call.
77C27000 EA 09 70 C2 77 33 00 jmp far 0x0033 : 0x77C27009
77C27007 00 00 add byte ptr ds:[eax],al
77C27009 41 inc ecx
77C2700A FF A7 F8 00 00 00 jmp dword ptr ds:[edi+0x000000F8]
[/mw_shl_code]
我们打开Windbg查看gdt表
在这个里面,0x33作为段选择子,跨段跳转。
0x33==0y00110011
没切之前,CS==0x23==0y00100011
也就是一个的第4个,另一个是第6个
我们可以看到,第四个,明显不是64的段,因为他有段界限。第六个也是个代码段,这个时候一旦切换就处于64位模式下了。
这个就是所谓的天堂之门.
我们跳转之后,就到了Wow64Cpu.dll模块
实际上就是在这个函数的这个里面 也就是WOW64CPU.DLL的RunSimulatedCode
[Asm] 纯文本查看 复制代码 000000006B101660 RunSimulatedCode proc near ; CODE XREF: BTCpuSimulate:loc_6B1011B4↑p
.text:000000006B101660 ; DATA XREF: .pdata:000000006B106084↓o
.text:000000006B101660
.text:000000006B101660 var_A8 = qword ptr -0A8h
.text:000000006B101660 var_A0 = word ptr -0A0h
.text:000000006B101660 var_98 = dword ptr -98h
.text:000000006B101660 var_90 = qword ptr -90h
.text:000000006B101660 var_88 = qword ptr -88h
.text:000000006B101660 var_80 = qword ptr -80h
.text:000000006B101660 var_78 = qword ptr -78h
.text:000000006B101660 var_70 = qword ptr -70h
.text:000000006B101660 var_68 = qword ptr -68h
.text:000000006B101660 var_60 = qword ptr -60h
.text:000000006B101660 var_58 = qword ptr -58h
.text:000000006B101660 var_50 = dword ptr -50h
.text:000000006B101660 var_48 = dword ptr -48h
.text:000000006B101660
.text:000000006B101660 ; __unwind { // CpupSimulateHandler
.text:000000006B101660 push r15
.text:000000006B101662 push r14
.text:000000006B101664 push r13
.text:000000006B101666 push r12
.text:000000006B101668 push rbx
.text:000000006B101669 push rsi
.text:000000006B10166A push rdi
.text:000000006B10166B push rbp
.text:000000006B10166C sub rsp, 68h
.text:000000006B101670 mov r12, gs:30h
.text:000000006B101679 lea r15, TurboThunkDispatch
.text:000000006B101680 mov r13, [r12+1488h]
.text:000000006B101688 add r13, 80h
.text:000000006B10168F
.text:000000006B10168F resume_common_reg: ; CODE XREF: RunSimulatedCode+167↓j
.text:000000006B10168F btr dword ptr [r13-80h], 0 ; 恢复通用非易失寄存器
.text:000000006B101695 jb short resume_segment_reg
.text:000000006B101697 mov edi, [r13+20h]
.text:000000006B10169B mov esi, [r13+24h]
.text:000000006B10169F mov ebx, [r13+28h]
.text:000000006B1016A3 mov ebp, [r13+38h]
.text:000000006B1016A7 mov eax, [r13+34h]
.text:000000006B1016AB mov r14, rsp
.text:000000006B1016AE mov dword ptr [rsp+0A8h+var_A8+4], 23h
.text:000000006B1016B6 mov r8d, 2Bh
.text:000000006B1016BC mov ss, r8d
.text:000000006B1016BF mov r9d, [r13+3Ch]
.text:000000006B1016C3 mov dword ptr [rsp+0A8h+var_A8], r9d
.text:000000006B1016C7 mov esp, [r13+48h]
.text:000000006B1016CB jmp fword ptr [r14]
.text:000000006B1016CE ; ---------------------------------------------------------------------------
.text:000000006B1016CE
.text:000000006B1016CE resume_segment_reg: ; CODE XREF: RunSimulatedCode+35↑j
.text:000000006B1016CE mov ecx, 2Bh
.text:000000006B1016D3 mov ds, ecx
.text:000000006B1016D5 assume ds:nothing
.text:000000006B1016D5 mov es, ecx
.text:000000006B1016D7 assume es:nothing
.text:000000006B1016D7 mov gs, ecx
.text:000000006B1016D9 assume gs:nothing
.text:000000006B1016D9 test byte ptr ds:7FFE028Ah, 0FFh
.text:000000006B1016E1 jnz short loc_6B1016EE
.text:000000006B1016E3 mov r8d, 53h
.text:000000006B1016E9 mov fs, r8d
.text:000000006B1016EC jmp short hells_door ; "地狱之门" 构建iretq的返回数据
.text:000000006B1016EE ; ---------------------------------------------------------------------------
.text:000000006B1016EE
.text:000000006B1016EE loc_6B1016EE: ; CODE XREF: RunSimulatedCode+81↑j
.text:000000006B1016EE mov r8, gs:0
.text:000000006B1016F7 wrfsbase r8
.text:000000006B1016FC
.text:000000006B1016FC hells_door: ; CODE XREF: RunSimulatedCode+8C↑j
.text:000000006B1016FC movaps xmm0, xmmword ptr [r13+0F0h] ; "地狱之门" 构建iretq的返回数据
.text:000000006B101704 movaps xmm1, xmmword ptr [r13+100h]
.text:000000006B10170C movaps xmm2, xmmword ptr [r13+110h]
.text:000000006B101714 movaps xmm3, xmmword ptr [r13+120h]
.text:000000006B10171C movaps xmm4, xmmword ptr [r13+130h]
.text:000000006B101724 movaps xmm5, xmmword ptr [r13+140h]
.text:000000006B10172C mov ecx, [r13+30h]
.text:000000006B101730 mov edx, [r13+2Ch]
.text:000000006B101734 and dword ptr [r13-80h], 0FFFFFFFFh
.text:000000006B101739 mov edi, [r13+20h]
.text:000000006B10173D mov esi, [r13+24h]
.text:000000006B101741 mov ebx, [r13+28h]
.text:000000006B101745 mov ebp, [r13+38h]
.text:000000006B101749 mov eax, [r13+34h]
.text:000000006B10174D mov r14, rsp
.text:000000006B101750 mov word ptr [rsp+8], 23h ; Cs
.text:000000006B101757 mov word ptr [rsp+20h], 2Bh ; SS
.text:000000006B10175E mov r8d, [r13+44h]
.text:000000006B101762 and dword ptr [r13+44h], 0FFFFFEFFh
.text:000000006B10176A mov [rsp+10h], r8d ; ELAGS
.text:000000006B10176F mov r8d, [r13+48h]
.text:000000006B101773 mov [rsp+18h], r8 ; ESP
.text:000000006B101778 mov r8d, [r13+3Ch]
.text:000000006B10177C mov [rsp], r8 ; eip
.text:000000006B101780 iretq ; 构建iretq远调用返回
.text:000000006B101782 ; ---------------------------------------------------------------------------
.text:000000006B101782
.text:000000006B101782 CpupReturnFromSimulatedCode: ; CODE XREF: KiFastSystemCall2+18↓j
.text:000000006B101782 ; DATA XREF: BTCpuResetToConsistentState+96↓o ...
.text:000000006B101782 xchg rsp, r14 ; r14保存的是堆栈 交换x64 x86堆栈
.text:000000006B101785 mov r8d, [r14] ; 返回地址
.text:000000006B101788 add r14, 4
.text:000000006B10178C mov [r13+3Ch], r8d ; r13==TEB64的一个值 这个值里面保存这CONTEXT32环境
.text:000000006B10178C ; 方便以后会x86跳转
.text:000000006B101790 mov [r13+48h], r14d
.text:000000006B101794 lea r11, [r14+4] ; 可以看到 把x86的参数指针传过来了
.text:000000006B101798 mov [r13+20h], edi
.text:000000006B10179C mov [r13+24h], esi ; 保存其他非易失寄存器
.text:000000006B1017A0 mov [r13+28h], ebx
.text:000000006B1017A4 mov [r13+38h], ebp
.text:000000006B1017A8 pushfq ; 保存eflags
.text:000000006B1017A9 pop r8
.text:000000006B1017AB mov [r13+44h], r8d
.text:000000006B1017AF ; Exported entry 9. TurboDispatchJumpAddressStart
.text:000000006B1017AF
.text:000000006B1017AF public TurboDispatchJumpAddressStart
.text:000000006B1017AF TurboDispatchJumpAddressStart: ; DATA XREF: .rdata:off_6B104798↓o
.text:000000006B1017AF mov ecx, eax
.text:000000006B1017B1 shr ecx, 10h
.text:000000006B1017B4 jmp qword ptr [r15+rcx*8] ; r15这个函数上面进行了初始化,是函数指针数组,rcx*8 跳转
.text:000000006B1017B8 ; ---------------------------------------------------------------------------
.text:000000006B1017B8 ; Exported entry 8. TurboDispatchJumpAddressEnd
.text:000000006B1017B8
.text:000000006B1017B8 public TurboDispatchJumpAddressEnd
.text:000000006B1017B8 TurboDispatchJumpAddressEnd: ; CODE XREF: RunSimulatedCode+26B↓j
.text:000000006B1017B8 ; RunSimulatedCode+31B↓j
.text:000000006B1017B8 ; DATA XREF: ...
.text:000000006B1017B8 mov ecx, eax ; ecx==系统索引号
.text:000000006B1017BA mov rdx, r11 ; rdx==参数指针
.text:000000006B1017BD call cs:__imp_Wow64SystemServiceEx ; 这个是在WOW64中
.text:000000006B1017C3 mov [r13+34h], eax
.text:000000006B1017C7 jmp resume_common_reg ; 执行完返回
可以看到,他并不是跳到函数的头部,而是跳到CpuReturnFromSimulatedCode这个标号处。
然后这个地方保存了环境。调用了Wow64SystemServiceEx
这个是wow64.dll的函数。
我们用YzDbg动态跟踪
在这个里面,我们会发现,有一个真正指向ntdll.OpenProcess的地方
可以看到,这是真正的x64的OpenProcess,然后syscall,直接进入内核。
Wow其实就是给x86架构模拟执行用的,Wow64有很多dll,比如Wow64,Wow64Cpu,Wow64Win。
其实就是模拟x64下 x86的执行。这三个dll关系是
wow64Cpu是管派发的,因为系统调用有可能是在GDI SSDT也就是ShadowSSDT。
细节性的东西我们就不看了,总体来说就是模拟x86,然后转到真正的ntdll的系统调用,去syscall。
0x3 Wow64系统调用细节分析0x3-1 Wow64Cpu保存与恢复环境
我们直接打开32位的ntdll的ZwOpenProcess来查看
[Asm] 纯文本查看 复制代码 __stdcall ZwOpenProcess(x, x, x, x)
.text:4B2F2D50 public _ZwOpenProcess@16
.text:4B2F2D50 _ZwOpenProcess@16 proc near ; CODE XREF: RtlQueryProcessDebugInformation(x,x,x)+129↓p
.text:4B2F2D50 ; RtlQueryProcessDebugInformation(x,x,x)+1D7↓p ...
.text:4B2F2D50 mov eax, 26h ; NtOpenProcess
.text:4B2F2D55 mov edx, offset _Wow64SystemServiceCall@0 ; Wow64SystemServiceCall()
.text:4B2F2D5A call edx ; Wow64SystemServiceCall() ; Wow64SystemServiceCall()
.text:4B2F2D5C retn 10h
.text:4B2F2D5C _ZwOpenProcess@16 endp
可以看到,直接call edx,这个edx是跳到了Wow64Transition
_DWORD __stdcall Wow64SystemServiceCall()
_Wow64SystemServiceCall@0 proc near
jmp ds:_Wow64Transition
_Wow64SystemServiceCall@0 endp[/mw_shl_code]
前面我们跟踪了,是位于Jmp的这个Wow64Transition就是天堂之门,因此我们在Wow64CPu中找到这个函数
我们可以看到,是位于Wow64Cpu的 KiFastSystemCall,这是个硬编码,但是不难看出确实是一样的。
然后他直接就跳转到了CpuReturnFromSimulatedCode哪里
在这个函数中,其实就是保存了各种寄存器,交换堆栈(x86->x64),然后调用
不过这个保存的寄存器位于(gs)TEB64.特定位置
位于WOW64.dll(非GDI系统调用)__imp_Wow64SystemServiceEx这个函数
[Asm] 纯文本查看 复制代码 text:000000006B101782 ; DATA XREF: BTCpuResetToConsistentState+96↓o ...
.text:000000006B101782 xchg rsp, r14 ; r14保存的是堆栈 交换x64 x86堆栈
.text:000000006B101785 mov r8d, [r14] ; 返回地址
.text:000000006B101788 add r14, 4
.text:000000006B10178C mov [r13+3Ch], r8d ; r13==TEB64的一个值 这个值里面保存这CONTEXT32环境
.text:000000006B10178C ; 方便以后会x86跳转
.text:000000006B101790 mov [r13+48h], r14d
.text:000000006B101794 lea r11, [r14+4] ; 可以看到 把x86的参数指针传过来了
.text:000000006B101798 mov [r13+20h], edi
.text:000000006B10179C mov [r13+24h], esi ; 保存其他非易失寄存器
.text:000000006B1017A0 mov [r13+28h], ebx
.text:000000006B1017A4 mov [r13+38h], ebp
.text:000000006B1017A8 pushfq ; 保存eflags
.text:000000006B1017A9 pop r8
.text:000000006B1017AB mov [r13+44h], r8d
.text:000000006B1017AF ; Exported entry 9. TurboDispatchJumpAddressStart
.text:000000006B1017AF
.text:000000006B1017AF public TurboDispatchJumpAddressStart
.text:000000006B1017AF TurboDispatchJumpAddressStart: ; DATA XREF: .rdata:off_6B104798↓o
.text:000000006B1017AF mov ecx, eax
.text:000000006B1017B1 shr ecx, 10h
.text:000000006B1017B4 jmp qword ptr [r15+rcx*8] ; r15这个函数上面进行了初始化,是函数指针数组,rcx*8 跳转
.text:000000006B1017B8 ; ---------------------------------------------------------------------------
.text:000000006B1017B8 ; Exported entry 8. TurboDispatchJumpAddressEnd
.text:000000006B1017B8
.text:000000006B1017B8 public TurboDispatchJumpAddressEnd
.text:000000006B1017B8 TurboDispatchJumpAddressEnd: ; CODE XREF: RunSimulatedCode+26B↓j
.text:000000006B1017B8 ; RunSimulatedCode+31B↓j
.text:000000006B1017B8 ; DATA XREF: ...
.text:000000006B1017B8 mov ecx, eax ; ecx==系统索引号
.text:000000006B1017BA mov rdx, r11 ; rdx==参数指针
.text:000000006B1017BD call cs:__imp_Wow64SystemServiceEx ; 这个是在WOW64中
.text:000000006B1017C3 mov [r13+34h], eax
.text:000000006B1017C7 jmp resume_common_reg ; 执行完返回
.text:000000006B1017C7 ; ---------------------------------------------------------------------------
.text:000000006B1017CC ThunkNone db 0CCh ; DATA XREF: .rdata:000000006B104760↓o
如上图,而我们可以看他是如何返回的,也就是下面那个Jmp 到resume_common_reg
在WOW64.Wow64SystemServiceEx执行完毕之后,开始根据之前保存的寄存器 回复环境
ext:000000006B10168F btr dword ptr [r13-80h], 0 ; 恢复通用非易失寄存器
.text:000000006B101695 jb short resume_segment_reg
.text:000000006B101697 mov edi, [r13+20h]
.text:000000006B10169B mov esi, [r13+24h]
.text:000000006B10169F mov ebx, [r13+28h]
.text:000000006B1016A3 mov ebp, [r13+38h]
.text:000000006B1016A7 mov eax, [r13+34h]
.text:000000006B1016AB mov r14, rsp
.text:000000006B1016AE mov dword ptr [rsp+0A8h+var_A8+4], 23h
.text:000000006B1016B6 mov r8d, 2Bh
.text:000000006B1016BC mov ss, r8d
.text:000000006B1016BF mov r9d, [r13+3Ch]
.text:000000006B1016C3 mov dword ptr [rsp+0A8h+var_A8], r9d
.text:000000006B1016C7 mov esp, [r13+48h]
.text:000000006B1016CB jmp fword ptr [r14]
.text:000000006B1016CE ; ---------------------------------------------------------------------------
.text:000000006B1016CE
.text:000000006B1016CE resume_segment_reg: ; CODE XREF: RunSimulatedCode+35↑j
.text:000000006B1016CE mov ecx, 2Bh
.text:000000006B1016D3 mov ds, ecx
.text:000000006B1016D5 assume ds:nothing
.text:000000006B1016D5 mov es, ecx
.text:000000006B1016D7 assume es:nothing
.text:000000006B1016D7 mov gs, ecx
.text:000000006B1016D9 assume gs:nothing
.text:000000006B1016D9 test byte ptr ds:7FFE028Ah, 0FFh
.text:000000006B1016E1 jnz short loc_6B1016EE
.text:000000006B1016E3 mov r8d, 53h
.text:000000006B1016E9 mov fs, r8d
.text:000000006B1016EC jmp short hells_door ; "地狱之门" 构建iretq的返回数据
0x3-1-1 地狱之门的iretq返回
值得一提的是,wow64返回的时候,是构造iretq的返回堆栈来进行"地狱之门的返回的"
[Asm] 纯文本查看 复制代码 .text:000000006B1016FC movaps xmm0, xmmword ptr [r13+0F0h] ; "地狱之门" 构建iretq的返回数据
.text:000000006B101704 movaps xmm1, xmmword ptr [r13+100h]
.text:000000006B10170C movaps xmm2, xmmword ptr [r13+110h]
.text:000000006B101714 movaps xmm3, xmmword ptr [r13+120h]
.text:000000006B10171C movaps xmm4, xmmword ptr [r13+130h]
.text:000000006B101724 movaps xmm5, xmmword ptr [r13+140h]
.text:000000006B10172C mov ecx, [r13+30h]
.text:000000006B101730 mov edx, [r13+2Ch]
.text:000000006B101734 and dword ptr [r13-80h], 0FFFFFFFFh
.text:000000006B101739 mov edi, [r13+20h]
.text:000000006B10173D mov esi, [r13+24h]
.text:000000006B101741 mov ebx, [r13+28h]
.text:000000006B101745 mov ebp, [r13+38h]
.text:000000006B101749 mov eax, [r13+34h]
.text:000000006B10174D mov r14, rsp
.text:000000006B101750 mov word ptr [rsp+8], 23h ; Cs
.text:000000006B101757 mov word ptr [rsp+20h], 2Bh ; SS
.text:000000006B10175E mov r8d, [r13+44h]
.text:000000006B101762 and dword ptr [r13+44h], 0FFFFFEFFh
.text:000000006B10176A mov [rsp+10h], r8d ; ELAGS
.text:000000006B10176F mov r8d, [r13+48h]
.text:000000006B101773 mov [rsp+18h], r8 ; ESP
.text:000000006B101778 mov r8d, [r13+3Ch]
.text:000000006B10177C mov [rsp], r8 ; eip
.text:000000006B101780 iretq ; 构建iretq远调用返回
0x3-2 Wow64.Wow64SystemServiceEx
自此,一个WOW64CPU.dll调用的流程就分析完了,但是如果真的想要了解到底是怎么根据调用号,最终找到的函数(ntdllx64里面的),毫无疑问,我们需要去逆向以下这个函数
总的来说,这个函数也是很简单,他的作用就有一个,根据WOW64专属的ServiceTable,找到要中转的函数(这个函数里面在进行参数整合之后,最后会调用x64ntdll真正的处理函数)。
[Asm] 纯文本查看 复制代码 text:0000000180008EC0 ; __unwind { // __GSHandlerCheck_SEH
.text:0000000180008EC0 mov [rsp+arg_10], rbx
.text:0000000180008EC5 push rsi ; 进入这个函数之前,rcx==sysindex,rdx==参数列表
.text:0000000180008EC6 push rdi
.text:0000000180008EC7 push r14
.text:0000000180008EC9 sub rsp, 8A0h
.text:0000000180008ED0 mov rax, cs:__security_cookie ; 堆栈检查
.text:0000000180008ED7 xor rax, rsp
.text:0000000180008EDA mov [rsp+8B8h+var_28], rax
.text:0000000180008EE2 mov r14, rdx
.text:0000000180008EE5 mov edx, ecx
.text:0000000180008EE7 mov rax, gs:30h
.text:0000000180008EF0 mov rcx, [rax+14E0h]
.text:0000000180008EF7 mov [rsp+8B8h+var_880], rcx
.text:0000000180008EFC xor edi, edi
.text:0000000180008EFE mov [rsp+8B8h+var_878], edi
.text:0000000180008F02 mov rax, gs:30h
.text:0000000180008F0B lea rcx, [rsp+8B8h+var_880]
.text:0000000180008F10 mov [rax+14E0h], rcx
.text:0000000180008F17 mov r8d, edx
.text:0000000180008F1A shr r8d, 0Ch
.text:0000000180008F1E and r8d, 3
.text:0000000180008F22 and edx, 0FFFh
.text:0000000180008F28 lea r9, [r8+r8*2]
.text:0000000180008F2C add r9, r9 ; (index>>12 & 3)*6 ServiceTable是6*Stride的大小,Stride==8
.text:0000000180008F2C ; 下面会看到,所以ServiceTable的大小是48 0x30
.text:0000000180008F2F lea r10, ServiceTables ; 找到ServiceTable
.text:0000000180008F36 cmp edx, [r10+r9*8+10h]
.text:0000000180008F3B ja loc_18001CED2 ; 比较是否越界
而他是这样找到代{过}{滤}理函数的
[Asm] 纯文本查看 复制代码 text:0000000180008FB1 mov rax, [r10+r9*8] ; 找到函数表
.text:0000000180008FB5 mov rsi, [rax+rdx*8] ; 根据sysindex直接找到代{过}{滤}理函数
.text:0000000180008FB9 mov [rsp+8B8h+var_860], r8d
.text:0000000180008FBE mov [rsp+8B8h+var_85C], edx
也就是sysindex*8+ServiceTable.FuncTable
0x3-2-1 Wow64.ServiceTable以及其应用
我们可以看到,他直接根据ServiceTable找到一个函数,然后调用,为此,我们必须去看看ServiceTable究竟保存了什么。(表太长,没用截全)
[Asm] 纯文本查看 复制代码 .rdata:000000018003A640 sdwhnt32JumpTable dq offset whNtAccessCheck
.rdata:000000018003A648 dq offset whNtWorkerFactoryWorkerReady
.rdata:000000018003A650 dq offset whNtAcceptConnectPort
.rdata:000000018003A658 dq offset whNtMapUserPhysicalPagesScatter
.rdata:000000018003A660 dq offset whNtWaitForSingleObject
.rdata:000000018003A668 dq offset whNtCallbackReturn
.rdata:000000018003A670 dq offset whNtReadFile
.rdata:000000018003A678 dq offset whNtDeviceIoControlFile
.rdata:000000018003A680 dq offset whNtWriteFile
.rdata:000000018003A688 dq offset whNtRemoveIoCompletion
.rdata:000000018003A690 dq offset whNtReleaseSemaphore
.rdata:000000018003A698 dq offset whNtReplyWaitReceivePort
.rdata:000000018003A6A0 dq offset whNtReplyPort
.rdata:000000018003A6A8 dq offset whNtSetInformationThread
.rdata:000000018003A6B0 dq offset whNtSetEvent
.rdata:000000018003A6B8 dq offset whNtClose
.rdata:000000018003A6C0 dq offset whNtQueryObject
.rdata:000000018003A6C8 dq offset whNtQueryInformationFile
.rdata:000000018003A6D0 dq offset whNtOpenKey
.rdata:000000018003A6D8 dq offset whNtEnumerateValueKey
.rdata:000000018003A6E0 dq offset whNtFindAtom
.rdata:000000018003A6E8 dq offset whNtQueryDefaultLocale
.rdata:000000018003A6F0 dq offset whNtQueryKey
.rdata:000000018003A6F8 dq offset whNtQueryValueKey
.rdata:000000018003A700 dq offset whNtAllocateVirtualMemory
.rdata:000000018003A708 dq offset whNtQueryInformationProcess
.rdata:000000018003A710 dq offset whNtWaitForMultipleObjects
.rdata:000000018003A718 dq offset whNtWriteFileGather
.rdata:000000018003A720 dq offset whNtSetInformationProcess
.rdata:000000018003A728 dq offset whNtCreateKey
.rdata:000000018003A730 dq offset whNtFreeVirtualMemory
.rdata:000000018003A738 dq offset
其实ServiceTable的第一个成员就是这个表(刚刚逆向中有体会),这个就是sdwhnt32JumpTable,因此我们只需要Hook这个地方,就可以达到R3层最隐蔽的Hook,而这个函数其实是个代{过}{滤}理函数,他的作用是中转。
他会把x86传进来的参数指针转换成x64系统调用的形式,比如我们随便点进去看看。
mov r8, [rsp+68h+var_38]
.text:0000000180010948 mov edx, r14d
.text:000000018001094B mov rcx, rdi
.text:000000018001094E call cs:__imp_NtOpenProcess ; 直接调用x64ntdll的OpenProcess
.text:0000000180010955 nop dword ptr [rax+rax+00h]
.text:000000018001095A test esi, esi
0x4 替换ServiceTable达到对32位程序最隐蔽的ApiHook
经过前面的分析,我们可以知道,Wow64(32位)进程所有的API或者说是系统调用,都是要经过ServiceTable的。
常规的API Hook一般是Hook Api函数本身,在头部用E9 或者FF 25进行Jmp,从而达到拦截过滤的效果。
这种方法虽然简单,但是缺点也很明显,容易被检测,而且不会拦截到更深层次的调用。
比如我调用MessageBoxA,直接Hook,拦截不到直接调用MessageBoxTimeOutA,但是程序最终执行的结果是一样的。
如果是通过替换ServiceTable来进行Hook的话,可以这样说,只要不是Shellcode+sysindex+syscall+heavendoor的方式进行调用,x86进程调用任何api都是可以被我们拦截的。
这种替换函数表的形式非常像SSDT Hook,区别是SSDT Hook会PG,全局,而且不限程序架构是x64还是x86。
比如下面我用一个最简单的替换ServiceTable的Hook进行示范
Hook的函数是OpenProcess,hook方式是向32位进程注入64位Dll
因为是最简单的,所以ServiceTable是直接通过偏移拿到的,因此不通用。仅作演示
我用到了开源的Wow64Ext来获取那4个Wow64Dll
[C] 纯文本查看 复制代码 void __declspec(naked) HkOpenProcess() {
//_rcx = 0;
__asm {
__emit 0x51 //push rcx
__emit 0x56 //push rsi
__emit 0x57 //push rdi
__emit 0x53 //push rbx
__emit 0x52 //push rdx
__emit 0x54 //push rsp
__emit 0x55 //push rbp
}
HellDoor();
//修改参数 降权
__asm {
xor eax,eax
mov [ecx+4], eax
}
HeavenDoor();
__asm {
__emit 0x5d //pop rbp
__emit 0x5c //pop rsp
__emit 0x5a //pop rdx
__emit 0x5b
__emit 0x5f
__emit 0x5e
__emit 0x59
}
__asm {
__emit 0x48
mov eax, [g_oWow64OpenProcess_] //mov rax,&target
__emit 0x0
__emit 0x0
__emit 0x0
__emit 0x0
__emit 0x48 //mov rax,[rax]
__emit 0x8b
__emit 0x00
__emit 0xff //jmp rax
__emit 0xe0
}
}
上面函数的作用是降低打开进程的权限为0
我们只需要替换上面的函数到ServiceTable对应的值即可,我们来看Hook降权前后对比
正常OpenProcess,可以读成功
而Hook之后
最后,ServiceTableHook是半成品,不过开下源吧,可以自己找下ServiceTable 我是直接根据偏移获取的
最后,纯小白,希望有错误大佬不吝赐教!
https://github.com/Oxygen1a1/Wow64HookServiceTable/tree/master
参考:
1.火哥内核视频
2.https://www.anquanke.com/post/id/222243
3.https://www.dazhuanlan.com/vctzdb/topics/975681 |
免费评分
-
查看全部评分
|