如果你也想自学Android,可以关注我。我会把踩过的坑分享给你,相关的教程以及配套的讲解 ,分享给大家:
https://edu.51cto.com/lesson/1011461.html
作者:小安
背景
为什么会出现花指令?
划重点:
线性扫描算法:逐行反汇编(无法将数据和内容进行区分)递归行进算法:按照代码可能的执行顺序进行反汇编程序。正是因为这两种反汇编的规格和缺陷机制,所以才导致了会有花指令的诞生。
概念
花指令是企图隐藏掉不想被逆向工程的代码块(或其它功能)的一种方法,在真实代码中插入一些垃圾代码的同时还保证原有程序的正确执行,而程序无法很好地反编译, 难以理解程序内容,达到混淆视听的效果。
简单的说就是在代码中混入一些垃圾数据阻碍你的静态分析。
分类
可执行花指令
可执行花指令指的是这部分花指令代码在程序的正常执行过程中会被执行,但执行这些代码没有任何意义,执行前后不改变任何寄存器的值(当然eip这种除外),同时这部分代码也会被反汇编器正常识别。首先,花指令的首要目的依然是加大静态分析的难度,让你难以识别代码的真正意图;然后,这种花指令可以破坏反编译的分析,使得栈指针在反编译引擎中出现异常。(当然我们知道栈指针实际上是没有问题的,只不过反编译引擎还有待完善的空间)不可执行式花指令
花指令虽然被插入到了正常代码的中间,但是并不意味着它一定会得到执行,这类花指令通常形式为在代码中出现了类似数据的代码,或者IDA反汇编后为jmupout(xxxxx)。这类花指令一般不属于CPU可以识别的操作码,那么就需要在上面用跳转跳过这些花指令才能保证程序的正常运行。实现
简单的花指令简单的花指令 0xe8是跳转指令,可以对线性扫描算法进行干扰,但是递归扫描算法可以正常分析。
两个跳转一个指向无效数据,一个指向正常数据来干扰递归扫描算法。
简单的jmpOD能被骗过去,但是因为ida采用的是递归扫描的办法所以能够正常识别。
3. 多级跳转
本质上和简单跳转是一样的,只是加了几层跳转。显然无法干扰ida
4. jnx和jx条件跳转
利用jz和jnz的互补条件跳转指令来代替jmp。竟然没有骗过OD(是因为吾爱的这个有插件吗)。但是ida竟然没有正常识别。
5. 永真条件跳转
通过设置永真或者永假的,导致程序一定会执行,由于ida反汇编会优先反汇编接下去的部分(false分支)。也可以调用某些函数会返回确定值,来达到构造永真或永假条件。ida和OD都被骗过去了。
6. call&ret构造花指令
7. 汇编指令共用opcode
jmp的条指令是inc eax的第一个字节,inc eax和dec eax抵消影响。这种共用opcode确实比较麻烦。
创意花指令
前面几种花指令都是比较老套的,入门花指令还能勉勉强强骗过反编译器,不过有经验的逆向者一眼就能识破,以下几种花指令形式,可以任由自己构造。
替换ret指令由于:
call指令的本质:push 函数返回地址然后jmp 函数地址
ret指令的本质: pop eip
两者都是对寄存器eip中存放的地址的操作。
所以我们可以在call指令之后,清楚的明白函数返回地址存放于esp,可以将值取出,用跳转指令跳转到该地址,即可代替ret指令。
当然,这种构造跳转指令可以变化多样。
这一部分需要精通标志寄存器,每一个操作码都会对相应的标志寄存器产生相应的影响,如果我们对标志寄存器足够熟练,就可以使用对应的跳转指令构造永恒跳转!利用函数返回确定值
有些函数返回值是确定的,比如我们自己写的函数,返回值可以是任意非零整数,就可以自己构造永恒跳转。
还有些api函数也是如此:方面可以传入一些错误的参数,如LoadLibraryA。
如果我们故意传入一个不存在的模块名称,那么他就会返回一个确定的值NULL,我们就可以通过这个构造永恒跳转。
另一方面,某些api函数,我们既然使用他,肯定就是一定要调用成功的,而这些api函数基本上只要调用成功就就会返回一个确定的零或者非零值,如MessageBox:
该api只有在其调用失败的时候才能返回零,那么我们也可以通过这一点构造永恒跳转。
3. 花指令原理另类利用
当我们理解了花指令的原理后,我们可以在将花指令中的垃圾数据替换为一些特定的特征码,可以对应的,尤其在SMC自解码这个反调试技术中可以运用。例如:
将这串特征码hElLowoRlD嵌入到代码中,那我们只需要在当前进程中搜索hElLowoRlD字符串,就可以定位到当前代码位置,然后对下面的代码进行SMC自解密。
清除
手动清除找到所有的花指令,重新设置数据和代码地址。或者将花指令设置为nop(0x90)
在0x401051设置为数据类型(快捷键D),在0x401052设置为代码类型(快捷键C)
这里用一个ida python脚本添加ALT+N快捷键来将指令的第一个字节设置为NOP
自动清楚花指令面有3个类别ida无法正常识别互补条件跳转(比较好处理)永真条件跳转 (各种永真条件比较难匹配)call&ret跳转(比较难处理)
所以就只对第一种jnx和jx的花指令进行自动化处理。
所有的跳转指令,互补跳转指令只有最后一个bit位不同。
第一条指令跳转距离=第二条跳转距离+2。简单一点可以是x03和x01
抄的代码
总结
重点:构造永恒跳转,添加垃圾数据!
重一方面构造一个永恒的跳转,一方面又比较隐蔽,不仅骗过反编译器,更让破解者找不到花指令。
参考文章
https://www.anquanke.com/post/id/236490#h3-12加粗样式
如果你也想自学Android,可以关注我。我会把踩过的坑分享给你,相关的教程以及配套的讲解 ,分享给大家:
https://edu.51cto.com/lesson/1011461.html
本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
相关文章