Wow64调用全景分析以及ServiceTableHook
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-> 天堂之门
77CA2D50NtOpenProcess B8 26 00 00 00 moveax,0x00000026
77CA2D55 BA 40 8B CB 77 movedx,ntdll.77CB8B40
77CA2D5A FF D2 calledx
77CA2D5C C2 10 00 retn0x0010
可以看到,他直接还是eax SSDT Index
然后call edx,这是win10下的,win7直接是call 三环fs:的地方
也就是_TEB32.
//0x1000 bytes (sizeof)
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; //0x44
ULONG UserReserved; //0xac
ULONG WOW32Reserved; //0xc0
ULONG CurrentLocale; //0xc4
ULONG FpSoftwareStatusRegister; //0xc8
ULONG ReservedForDebuggerInstrumentation; //0xcc
ULONG SystemReserved1; //0x10c
CHAR PlaceholderCompatibilityMode; //0x174
UCHAR PlaceholderHydrationAlwaysExplicit; //0x175
CHAR PlaceholderReserved; //0x176
ULONG ProxiedProcessId; //0x180
struct _ACTIVATION_CONTEXT_STACK32 _ActivationStack; //0x184
UCHAR WorkingOnBehalfTicket; //0x19c
LONG ExceptionCode; //0x1a4
ULONG ActivationContextStackPointer; //0x1a8
ULONG InstrumentationCallbackSp; //0x1ac
ULONG InstrumentationCallbackPreviousPc; //0x1b0
ULONG InstrumentationCallbackPreviousSp; //0x1b4
UCHAR InstrumentationCallbackDisabled; //0x1b8
UCHAR SpareBytes; //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; //0x6cc
ULONG glDispatchTable; //0x7c4
ULONG glReserved1; //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; //0xc00
ULONG DeallocationStack; //0xe0c
ULONG TlsSlots; //0xe10
struct LIST_ENTRY32 TlsLinks; //0xf10
ULONG Vdm; //0xf18
ULONG ReservedForNtRpc; //0xf1c
ULONG DbgSsReserved; //0xf20
ULONG HardErrorMode; //0xf28
ULONG Instrumentation; //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 far0x0033 : 0x77C27009
77C27007 00 00 addbyte ptr ds:,al
77C27009 41 incecx
77C2700A FF A7 F8 00 00 00 jmpdword ptr ds:
我们打开Windbg查看gdt表
在这个里面,0x33作为段选择子,跨段跳转。
0x33==0y00110011
没切之前,CS==0x23==0y00100011
也就是一个的第4个,另一个是第6个
我们可以看到,第四个,明显不是64的段,因为他有段界限。第六个也是个代码段,这个时候一旦切换就处于64位模式下了。
这个就是所谓的天堂之门.
我们跳转之后,就到了Wow64Cpu.dll模块
实际上就是在这个函数的这个里面 也就是WOW64CPU.DLL的RunSimulatedCode
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,
.text:000000006B101688 add r13, 80h
.text:000000006B10168F
.text:000000006B10168F resume_common_reg: ; CODE XREF: RunSimulatedCode+167↓j
.text:000000006B10168F btr dword ptr , 0 ; 恢复通用非易失寄存器
.text:000000006B101695 jb short resume_segment_reg
.text:000000006B101697 mov edi,
.text:000000006B10169B mov esi,
.text:000000006B10169F mov ebx,
.text:000000006B1016A3 mov ebp,
.text:000000006B1016A7 mov eax,
.text:000000006B1016AB mov r14, rsp
.text:000000006B1016AE mov dword ptr , 23h
.text:000000006B1016B6 mov r8d, 2Bh
.text:000000006B1016BC mov ss, r8d
.text:000000006B1016BF mov r9d,
.text:000000006B1016C3 mov dword ptr , r9d
.text:000000006B1016C7 mov esp,
.text:000000006B1016CB jmp fword ptr
.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 movapsxmm0, xmmword ptr ; "地狱之门" 构建iretq的返回数据
.text:000000006B101704 movapsxmm1, xmmword ptr
.text:000000006B10170C movapsxmm2, xmmword ptr
.text:000000006B101714 movapsxmm3, xmmword ptr
.text:000000006B10171C movapsxmm4, xmmword ptr
.text:000000006B101724 movapsxmm5, xmmword ptr
.text:000000006B10172C mov ecx,
.text:000000006B101730 mov edx,
.text:000000006B101734 and dword ptr , 0FFFFFFFFh
.text:000000006B101739 mov edi,
.text:000000006B10173D mov esi,
.text:000000006B101741 mov ebx,
.text:000000006B101745 mov ebp,
.text:000000006B101749 mov eax,
.text:000000006B10174D mov r14, rsp
.text:000000006B101750 mov word ptr , 23h ; Cs
.text:000000006B101757 mov word ptr , 2Bh ; SS
.text:000000006B10175E mov r8d,
.text:000000006B101762 and dword ptr , 0FFFFFEFFh
.text:000000006B10176A mov , r8d; ELAGS
.text:000000006B10176F mov r8d,
.text:000000006B101773 mov , r8 ; ESP
.text:000000006B101778 mov r8d,
.text:000000006B10177C mov , 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, ; 返回地址
.text:000000006B101788 add r14, 4
.text:000000006B10178C mov , r8d; r13==TEB64的一个值 这个值里面保存这CONTEXT32环境
.text:000000006B10178C ; 方便以后会x86跳转
.text:000000006B101790 mov , r14d
.text:000000006B101794 lea r11, ; 可以看到 把x86的参数指针传过来了
.text:000000006B101798 mov , edi
.text:000000006B10179C mov , esi; 保存其他非易失寄存器
.text:000000006B1017A0 mov , ebx
.text:000000006B1017A4 mov , ebp
.text:000000006B1017A8 pushfq ; 保存eflags
.text:000000006B1017A9 pop r8
.text:000000006B1017AB mov , 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 跳转
.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 , 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来查看
; __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
前面我们跟踪了,是位于Jmp的这个Wow64Transition就是天堂之门,因此我们在Wow64CPu中找到这个函数
我们可以看到,是位于Wow64Cpu的 KiFastSystemCall,这是个硬编码,但是不难看出确实是一样的。
然后他直接就跳转到了CpuReturnFromSimulatedCode哪里
在这个函数中,其实就是保存了各种寄存器,交换堆栈(x86->x64),然后调用
不过这个保存的寄存器位于(gs)TEB64.特定位置
位于WOW64.dll(非GDI系统调用)__imp_Wow64SystemServiceEx这个函数
.text:000000006B101782 ; DATA XREF: BTCpuResetToConsistentState+96↓o ...
.text:000000006B101782 xchg rsp, r14 ; r14保存的是堆栈 交换x64 x86堆栈
.text:000000006B101785 mov r8d, ; 返回地址
.text:000000006B101788 add r14, 4
.text:000000006B10178C mov , r8d; r13==TEB64的一个值 这个值里面保存这CONTEXT32环境
.text:000000006B10178C ; 方便以后会x86跳转
.text:000000006B101790 mov , r14d
.text:000000006B101794 lea r11, ; 可以看到 把x86的参数指针传过来了
.text:000000006B101798 mov , edi
.text:000000006B10179C mov , esi; 保存其他非易失寄存器
.text:000000006B1017A0 mov , ebx
.text:000000006B1017A4 mov , ebp
.text:000000006B1017A8 pushfq ; 保存eflags
.text:000000006B1017A9 pop r8
.text:000000006B1017AB mov , 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 跳转
.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 , 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执行完毕之后,开始根据之前保存的寄存器 回复环境
text:000000006B10168F btr dword ptr , 0 ; 恢复通用非易失寄存器
.text:000000006B101695 jb short resume_segment_reg
.text:000000006B101697 mov edi,
.text:000000006B10169B mov esi,
.text:000000006B10169F mov ebx,
.text:000000006B1016A3 mov ebp,
.text:000000006B1016A7 mov eax,
.text:000000006B1016AB mov r14, rsp
.text:000000006B1016AE mov dword ptr , 23h
.text:000000006B1016B6 mov r8d, 2Bh
.text:000000006B1016BC mov ss, r8d
.text:000000006B1016BF mov r9d,
.text:000000006B1016C3 mov dword ptr , r9d
.text:000000006B1016C7 mov esp,
.text:000000006B1016CB jmp fword ptr
.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的返回堆栈来进行"地狱之门的返回的"
.text:000000006B1016FC movapsxmm0, xmmword ptr ; "地狱之门" 构建iretq的返回数据
.text:000000006B101704 movapsxmm1, xmmword ptr
.text:000000006B10170C movapsxmm2, xmmword ptr
.text:000000006B101714 movapsxmm3, xmmword ptr
.text:000000006B10171C movapsxmm4, xmmword ptr
.text:000000006B101724 movapsxmm5, xmmword ptr
.text:000000006B10172C mov ecx,
.text:000000006B101730 mov edx,
.text:000000006B101734 and dword ptr , 0FFFFFFFFh
.text:000000006B101739 mov edi,
.text:000000006B10173D mov esi,
.text:000000006B101741 mov ebx,
.text:000000006B101745 mov ebp,
.text:000000006B101749 mov eax,
.text:000000006B10174D mov r14, rsp
.text:000000006B101750 mov word ptr , 23h ; Cs
.text:000000006B101757 mov word ptr , 2Bh ; SS
.text:000000006B10175E mov r8d,
.text:000000006B101762 and dword ptr , 0FFFFFEFFh
.text:000000006B10176A mov , r8d; ELAGS
.text:000000006B10176F mov r8d,
.text:000000006B101773 mov , r8 ; ESP
.text:000000006B101778 mov r8d,
.text:000000006B10177C mov , r8 ; eip
.text:000000006B101780 iretq ; 构建iretq远调用返回
0x3-2 Wow64.Wow64SystemServiceEx自此,一个WOW64CPU.dll调用的流程就分析完了,但是如果真的想要了解到底是怎么根据调用号,最终找到的函数(ntdllx64里面的),毫无疑问,我们需要去逆向以下这个函数
总的来说,这个函数也是很简单,他的作用就有一个,根据WOW64专属的ServiceTable,找到要中转的函数(这个函数里面在进行参数整合之后,最后会调用x64ntdll真正的处理函数)。
.text:0000000180008EC0 ; __unwind { // __GSHandlerCheck_SEH
.text:0000000180008EC0 mov , 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 , rax
.text:0000000180008EE2 mov r14, rdx
.text:0000000180008EE5 mov edx, ecx
.text:0000000180008EE7 mov rax, gs:30h
.text:0000000180008EF0 mov rcx,
.text:0000000180008EF7 mov , rcx
.text:0000000180008EFC xor edi, edi
.text:0000000180008EFE mov , edi
.text:0000000180008F02 mov rax, gs:30h
.text:0000000180008F0B lea rcx,
.text:0000000180008F10 mov , 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,
.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,
.text:0000000180008F3B ja loc_18001CED2 ; 比较是否越界
.
而他是这样找到代理函数的
text:0000000180008FB1 mov rax, ; 找到函数表
.text:0000000180008FB5 mov rsi, ; 根据sysindex直接找到代理函数
.text:0000000180008FB9 mov , r8d
.text:0000000180008FBE mov , edx
也就是sysindex*8+ServiceTable.FuncTable
0x3-2-1 Wow64.ServiceTable以及其应用我们可以看到,他直接根据ServiceTable找到一个函数,然后调用,为此,我们必须去看看ServiceTable究竟保存了什么。(表太长,没用截全)
.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,
.text:0000000180010948 mov edx, r14d
.text:000000018001094B mov rcx, rdi
.text:000000018001094E call cs:__imp_NtOpenProcess ; 直接调用x64ntdll的OpenProcess
.text:0000000180010955 nop dword ptr
.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
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 , 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, //mov rax,&target
__emit 0x0
__emit 0x0
__emit 0x0
__emit 0x0
__emit 0x48 //mov rax,
__emit 0x8b
__emit 0x00
__emit 0xff //jmp rax
__emit 0xe0
}
}
上面函数的作用是降低打开进程的权限为0
我们只需要替换上面的函数到ServiceTable对应的值即可,我们来看Hook降权前后对比
正常OpenProcess,可以读成功,Hook之后
最后,ServiceTableHook是半成品,不过开下源吧,可以自己找下ServiceTable 我是直接根据偏移获取的
**** Hidden Message *****
参考:
1.火哥内核视频
2.https://www.anquanke.com/post/id/222243
3.https://www.dazhuanlan.com/vctzdb/topics/975681
{:5_117:}学习一下 支持一下师兄的教程,辛苦了,一起加油 谢谢分享! 看下隐藏内容~~ 谢谢分享!!!!!!!!!!!!! 学习下,谢谢分享 号高深的样子,整不动 感谢楼主,支持一下!
页:
[1]
2