转一段刘涛涛写的扭曲变换。有点vmprotect的感觉。不知是否用得到。贴过来做个备忘。
采用的代码扭曲方法:
* 用 JMP 把代码打乱。这已经不是什么新鲜的招数了,但它依然有效。
* 用 JMP 把多个函数缠绕在一起。这样可以让分析者找不到函数从什么地方开始,到什么地方结束。
* 把 call 改掉。破解者对 call 是极敏感的,这举可以让他找不到一个 call。比如,我可以把
call sub1
改为:
mov eax, offset sub1 + 3
push offset @1
sub eax, 3
jmp eax
@1:
* 把 ret 改掉。破解者对 ret 是极敏感的,这举可以让他找不到一个 ret。比如,我可以把ret写作
push ecx
mov ecx, [esp+4]
add esp,8
jmp ecx
* 改条件跳。条件跳也是极敏感的指令,比如我们可以把
cmp reg1, reg2
jge L_DST
L_NEXT:
写作:
push eax
mov eax, reg1
sub eax, reg2
shr eax, 1fh
neg eax
and eax, L2 - L1
add eax, L1
jmp eax
L1:
pop eax
jmp L_DST
L2:
pop eax
L_NEXT:
再看这个,你能看懂是什么意思吗
push offset @@L - offset L_3 + 23h
jmp L_1
L_2:
jz L_3
ret 4
L_3:
add dword ptr [esp+4], offset L_3 - 23h
add esp,4
ret
L_1:
call L_2
...
这里出现了call和ret,但又不是一般所期望的那种。这里的call不代表你发现了一个函数调用。
这里的ret不代表是一个函数的结束。
* 使用堆栈代替寄存器。比如:
MOV EAX, DWORD PTR [ECX+0AD8h]
PUSH EAX
MOV ECX, DWORD PTR [EAX]
可以写作:
PUSH EAX
PUSH ECX
MOV EAX, DWORD PTR [ESP]
ADD EAX, 0AD8h
MOV EAX, DWORD PTR [EAX]
MOV DWORD PTR [ESP+04h], EAX
PUSH DWORD PTR [ESP+04h]
MOV EAX, DWORD PTR [ESP]
MOV DWORD PTR [ESP+08h], EAX
MOV EAX, DWORD PTR [ESP]
MOV EAX, DWORD PTR [EAX]
MOV DWORD PTR [ESP+04h], EAX
MOV EAX, DWORD PTR [ESP]
MOV ECX, DWORD PTR [ESP+04h]
ADD ESP, 08h
你能看懂吗?很明显,这个变换是不可逆变换。因为它本来使用了哪个寄存器,已经没有办法知道了。
* ……还可以想出很多扭曲变换的方法。化繁为简只有一种方法,化简为繁可以有无穷多种方法。