这两天在琢磨花指令的一些特性。由于刚开始接触加密不久,对那些已经广为流行的花指令不是很熟悉。
一般来说,网上教我们的花指令样式为:
jz @F
jnz @F
db 0E8h
@@:
这种样式的花指令对付IDA静态反汇编,效果还好:
.text:004010D8 jz short near ptr loc_4010DC+1
.text:004010D8
.text:004010DA jnz short near ptr loc_4010DC+1
.text:004010DA
.text:004010DC
.text:004010DC loc_4010DC: ; CODE XREF: .text:004010D8j
.text:004010DC ; .text:004010DAj
.text:004010DC call near ptr 4050C649h
.text:004010E1 add al, ch
但是一旦载入OD,便一览无余。
004010D8 . /74 03 je short 004010DD
004010DA . |75 01 jnz short 004010DD
004010DC |E8 db E8
004010DD > \68 B5104000 push 004010B5
于是我把这种样式变换了一下
jz $+6
jnz $+4
db 0E8h
db 041h
效果一样,欺骗了IDA,但是OD ctrl+A去除花指令后仍然识别出了花指令。
再次变换:
jz $+2
jmp $+7
db 0e8h
jnz $-3
db 0E8h
db 041h
结果IDA识别出:
jz short $+2
jmp short loc_401184
虽然古怪,但准确地指出了实际跳转地址。而OD也识别出花指令。
----------------------
网上看了一下,觉得那些花指令集合一般在最后总有一个关键jmp,对付IDA和OD的效果都不是很令人满意。往往仔细向后一看,关键jmp就出来了。
于是想集合一下自己掌握的知识,能不能打造一个属于自己的花指令呢?
在代码段里的数据越乱,跳转越不清晰,对付反汇编的效果就越好。
既然现在IDA与OD等都越来越智能化,那明显的jmp类指令组成的花指令我觉得已经没有什么作用了。
那么,不使用jmp类指令,如何控制程序流程?不控制程序流程,一旦执行起我们填的那些脏数据,程序飞到天上去了。
经验告诉我们,call指令与ret指令也能改变程序流程。嘿嘿,这是一个好的方向。
说做就做,结合OD与IDA同步跟踪分析。
首先,应该通过call指令改变顺序流程,最后通过ret指令回跳。这样,call指令后面可以填写“脏数据”了。。。
ret指令从堆栈出取出返回地址。call指令压入的返回地址指向call的后面一条指令位置。我们可以用pop取出修改后push便能成功控制ret跳转。
关键是,我们想在call指令后面写一定数量的“脏数据”,最好是“任意数量”。“任意数量的脏数据”后面才是我们的正常代码。
这个“任意数量”在我们修改回跳地址的时候如何确定?
总不能我们写多少“脏数据”以后,自己去算多少个字节,然后写程序吧。如果你真的这样想,还是省省吧。通用性通用性,我们强调多少遍了!
既然我们不能去算,我们又必须在call函数里知道“脏数据”的长度,而“脏数据”的长度又在call指令之后形成。怎么办?
让编译器帮我们来计算这个长度!怎么计算?
结合@@:标志与$。为什么要用@@标志?你应该知道,我们可不只在一处使用花指令吧,那么我们可不能为标志命名问题来干扰我们并浪费我们的脑细胞吧。
说来可笑,不知道怎么想的,我竟然用这么一段代码来实现,虽然成功实现,但是。。。很烂。
虽然我在里面实现了传参与取参的巧妙操作,但是你看过后面的代码就知道,同样的效果,这一段代码的确很烂。
myjmp:
pop edx ;返回地址
pop ecx ;参数@nByte
push esi
pushf
@@: mov esi,$
cld
rep lodsb
mov ecx,offset @B
sub esi,ecx
add edx,esi
popf
pop esi
push edx
ret
start:
db 6Ah
db offset @F- $ -6
call myjmp
db '花指令乱码'
@@:
invoke GetModuleHandle,NULL
mov hInstance,eax
~~~~~~~~~~~~~~
下面是正式修改完毕的花指令代码:
myjmp proc
pop eax
add eax,ecx
push eax
ret
myjmp endp
start:
mov ecx,offset @F- $ -10
call myjmp
db '花指令乱码'
@@:;.........正常指令继续
使用这个花指令后,这是IDA的结果:
public start
start proc near
mov ecx, 0Ah
call sub_401167
mov ebx, 0C1B8D6A8h
out dx, al
retn 0C2D2h
start endp
这是OD的结果:
00401167 /$ 58 pop eax
00401168 |. 03C1 add eax, ecx
0040116A |. 50 push eax
0040116B \. C3 retn
0040116C >/$ B9 0A000000 mov ecx, 0A
00401171 |. E8 F1FFFFFF call 00401167
00401176 |. BB A8D6B8C1 mov ebx, C1B8D6A8
0040117B |. EE out dx, al
0040117C \. C2 D2C2 retn 0C2D2
0040117F EB db EB
00401180 6A db 6A ; CHAR 'j'
00401181 00 db 00
00401182 E8 db E8
00401183 4D db 4D ; CHAR 'M'
00401184 00 db 00
00401185 00 db 00
00401186 00 db 00
00401187 A3 db A3
00401188 90 nop
00401189 . 30 40 00 ascii "0@",0
0040118C . 68 4B 20 40 0>ascii "hK @",0
00401191 . 68 43 20 40 0>ascii "hC @",0
~~~~~~~~~~~
欺骗成功。就算在动态跟踪过程中,你也将面对那一串串db晕头转向。
花指令实现:可以在call指令后如你所愿地加入任意长度“脏数据”。当然,你愿意在后面加入一些乱七八糟的正常但无用的指令也随便。
嘿嘿嘿,可见有点花.....不敢藏私。赶紧拿出来分享。
这是我难得的技术文章。欢迎交流、拍砖。谢谢!
[课程]Linux pwn 探索篇!