标题: shellcode自识别32/64位架构
创建: 2014-12-26 更新: 2017-09-26 17:18 链接: https://scz.617.cn/windows/201412260000.txt
本文为下列文档的意译,仅为收集整理点评,非原创。
Architecture Detection (x86 or x64) Assembly Stub - zero sum [2014-12-26] https://zerosum0x0.blogspot.com/2014/12/detect-x86-or-x64-assembly-stub.html
Detecting Architecture in Windows - OsandaMalith [2017-09-24] https://osandamalith.com/2017/09/24/detecting-architecture-in-windows/
checking_architecture.cpp https://gist.github.com/hasherezade/0994447e9d3dc184888fb2afd5a57301
The initial values of x86 registers https://github.com/corkami/docs/blob/master/InitialValues.md
1) 推荐方案(zero sum)
shellcode自识别32/64位架构极其重要,比如Windows的TIB,x86上用FS寻址,x64上 用GS寻址。下面这5字节汇编指令可以自动识别32/64位架构:
_shellcode:
xor ecx,ecx ; set ecx to 0
db 0x41 ; x86 opcode for: inc ecx
loop x64_code ; ecx now -1 in x64, we jmp
x86_code:
; use fs segment
; ret
x64_code:
; use gs segment
熟悉x64汇编的人应该知道,[0x40,0x50]经常用作指令前缀,可以利用这点微妙的区 别自动识别32/64位架构。
假设在x86上运行:
xor ecx,ecx ; 31c9 ecx=0 inc ecx ; 41 ecx=1 loop x64_code ; e201 ecx=0 no jmp
假设在x64上运行:
xor ecx,ecx ; 31c9 ecx=0 rex.B loop x64_code ; 41e201 ecx=-1 jmp
这里用"db 0x41",而不是直接写成"inc ecx",因为nasm会采用2字节指令避免这种 歧义,我们恰恰需要这种歧义。
上面给出的机器码只是演示,e201中的01是假设x86_code处只有一条ret指令,占1个 字节。
Eternalblue使用了本质上相同的类似方案:
31 C0 xor eax, eax 40 90 xchg eax, eax 0F 84 B5 05 00 00 jz x64_main
这是推荐方案,它只与CPU相关,与OS无关,理论上针对Linux也能使用这种技术。后 面其他方案都是针对Windows的。
2) Kronos malware
xor eax, eax mov ax, cs shr eax, 5
x86/Win7用户态的CS是0x1B,x64/Win7用户态的CS是0x23或0x33,因此最后的eax为0 时表示x86,为1时表示x64。
上述代码只适用于用户态,且无法区分SysWOW64。
3) 其他段选择子
x86/Win7 user
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000
x64/Win7 32-bits user
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
x64/Win7 64-bits user
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b
x86/Win7 kernel
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000
x64/Win7 kernel
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b
OsandaMalith受Kronos启发,用其他段选择子区分32/64位架构:
xor eax, eax mov ax, es ror ax, 0x3 and eax, 0x1 test eax, eax
xor eax, eax mov eax, gs test eax, eax
上述代码无法区分SysWOW64。当然,不必那么死板,只要存在差异,就可以区分。
4) TEB
x86/Win7 user
> dt ntdll!_TEB WOW32Reserved @$teb
+0x0c0 WOW32Reserved : (null)
> ? @$teb
Evaluate expression: 2147344384 = 7ffde000
x86用户态FS:0指向ntdll!_TEB,可以用"dg @fs"获取FS段基址。
> dg @fs
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
003B 7ffde000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
x64/Win7 32-bits user
> dt ntdll!_TEB WOW32Reserved @$teb
+0x0c0 WOW32Reserved : 0x74a92320 Void
> u 0x74a92320 l 1
74a92320 ea1e27a9743300 jmp 0033:74A9271E
> ? @$teb
Evaluate expression: 2130563072 = 7efdd000
> dg @fs
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0053 7efdd000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
x64/Win7 64-bits user
> dt ntdll!_TEB WOW32Reserved @$teb
+0x100 WOW32Reserved : (null)
> ? @$teb
Evaluate expression: 8796092882944 = 000007ff`fffde000
x64用户态GS:0指向ntdll!_TEB。对于x64,"dg @gs"无法获取GS段基址。
用WOW32Reserved可以区分SysWOW64,但WOW32Reserved的偏移对于三种情况不统一, 而且访问TEB时涉及FS、GS的选择,本方案意义不大。
xor eax, eax mov eax, [FS:0xc0] test eax, eax