-
-
[原创]X86指令混淆之函数分析和代码块粉碎
-
发表于:
2020-1-14 12:45
16876
-
二进制分析的时候经常遇到经过混淆的函数,所以一直想自己学习实现一个简单的指令乱序demo,也不需要太复杂(其实就是自己菜而已)。能阻止IDA F5党(就是我)就可以了(能去混淆的师傅除外),常见的指令乱序是把一段代码根据jcc指令划分成若干基本块jcc指令通俗来说就是常见的跳转指令诸如jz,jnz,jmp....此类。基本块的概念参考IDA截图,像这种loc_xxx就能看作基本块。
更直观一点就是下面这张图,代码被划分成块,执行流程被分析的明明白白
划分完基本块之后再打乱或者隐藏各个基本块之间的直接联系,使静态反编译工具无法分析执行流程。
更无法通过F5看伪代码。
最简单最原始的做法就是增加新的代码块A,找出所有jcc指令,修改该指令跳转到A,
再通过A跳转到正确的代码块,代码块A可以根据数学公式实现一些运算,动态计算出跳转地址,模糊控制流。
这种做法也被大牛们叫做控制流程平坦化,代码块A也叫做控制分发器,负责分发指令跳转。
当然这只是最简单最基本的控制流程平坦化,去混淆也很容易,几乎可以静态将代码打回原形。
我没有采用上面的方法,我的基本想法是以函数为单位进行混淆,比如有函数F,抽取出F函数的所有指令,
申请一个新的空间将每条指令随机乱序放置在新的空间,再增加指令保证两条指令的执行顺序和原始函数一致,
可以采用上面说的复杂算法计算出下一条指令的地址也可以使用直观的跳转指令进行链接。
实现每条指令空间顺序上的随机乱序,但是执行顺序不变,空间上相邻的两条指令之间也可以生成一些大小随机的花指令进行干扰。
最后修复跳转关系和重定位表。这样就完成了对一个函数的“粉碎”。
使用工具:自己撸的一个PE操作类,反汇编引擎使用的udis86,汇编引擎使用的asmjit
asmjit
udis86
#函数分析
函数分析的意思是,给定一个代码块,识别出函数的起始地址和大小,类似IDA以sub_xxx标注出函数的功能
如图
正确识别出函数是很困难的事情,因为每个编译器生成的函数特征可能都不一样,比如某些函数以ret指令结尾,
有些函数根本没有ret指令,有些函数也不是以push xxx开头。所以只能尽可能加入较多的函数特征。
连IDA这种级别的反编译器都不可能百分百识别出代码和数据,有些编译器把部分数据和代码混合编译在一起,
比如delphi。或者编程者故意插入了导致某些反编译结果出错的花指令,这种情况是无法分析函数的。
参考了玩命的关于代码数据识别的文章,自己再总结了一些规则,得出能识别大部分函数的算法,
暂时没有加入识别某些delphi函数的规则,这类函数代码和数据混杂在了一起。
基本算法如下
部分代码如下:
分析procmon.exe的winmain函数和IDA对比的效果如图
根据IDA的识别计算一下函数大小0x0045E6B4-0x0045D840=3700,和自己程序的识别结果一致
有了上面得到的信息就能进行粉碎了,用前面讲的方法将函数进行混淆得到新的代码块,
再找到reloc段的前面一个段,向下合并reloc段,创建一个新的text段,把混淆代码放进去,
最后在新text段后面创建reloc段,修复重定位信息。
混淆之前必须先扫描记录当前函数的所有重定位信息,混淆过程中将原始重定位信息和新的重定位信息联系在一起,以便后面进行重定位修复。
部分代码实现:
对procmon.exe的winmain函数进行粉碎生成procmon2.exe
procmon2.exe正常运行
混淆前:
混淆后:
原理很简单,只实现了乱序粉碎的功能,而且是很简单的函数粉碎,只作为学习的一个玩具demo参考
代码变形,常量隐藏,导入表加密等等功能都没有加入。没什么技术含量,代码很垃圾,大佬轻喷
发个demo bin玩玩,把Obfuscater.exe和procmon.exe放在同一目录,运行即可生procmon.obf.exe
Obfuscater.exe处理的exe和函数我都写死了,想用的大佬可以自行逆向patch一下
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)