├── README.md ├── main.cpp ├── obfuscate.cpp └── obfuscate.h /README.md: -------------------------------------------------------------------------------- 1 | demo crackme 链接:https://www.52pojie.cn/thread-1746849-1-1.html 2 | (代码是只支持x32,没处理重定位,稍微改一下就可以兼容x64,思路是一样的,简单说下实现思路) 3 | 4 | 1、读取一个可执行文件,判断是否是PE文件、ELF文件等等,比如用MZ标志和PE00标志判断(PE结构的知识) 5 | 6 | 2、读取文件后定位到要混淆的代码的位置(也是PE结构的知识,可以先把文件按内存分布读取到缓冲区,需要注意的是文件分布和内存分布是不一样的,对齐的大小不一样) 7 | 8 | 3、提取二进制数据后用反汇编工具(这里用的是capstone,它提供了强大的分析功能)反汇编 9 | 10 | 4、然后对汇编代码进行混淆,具体的混淆方法和实现放在下面叙述 11 | 12 | 5、将混淆后的汇编代码用汇编引擎生成二进制硬编码(这里用的是keystone) 13 | 14 | 6、新增一个节(注意校验节表是否有足够空间新增),存放混淆后的代码,然后在混淆开始的位置,可以用一个jmp指令跳转到新增加的节里的代码,然后将jmp指令后的原始指令全部抹除 15 | 16 | 7、保存文件 17 | 18 | 注意,上述仅为一个思路,事实上你也可以用同样的方法写一个vm出来,只不过要复杂的多,另外,当对多段代码进行混淆时,最好都存在一个节里,不然混淆几处就不够地方新增节了,本项目的代码还没实现。 19 | 20 | 21 | 22 | 混淆的方法和具体实现: 23 | 24 | 如果你看完了以上思路,那你应该能发现,这就将代码混淆变成了纯纯的字符串处理(如果你学过编译原理,那本项目的代码对你来说就是小辣鸡) 25 | 26 | 常量混淆 27 | 28 | mov eax, 0x401000 可变形为 29 | 30 | mov eax, 0x401000 + rand 31 | 32 | lea eax, [eax-rand] 33 | 34 | 上述即为一个简单的等价变形,需要注意的是lea指令并不会影响eflags,如果使用sub指令而不是lea指令,你需要保存eflags,比如 35 | 36 | mov eax, 0x401000 + rand 37 | 38 | pushfd 39 | 40 | sub eax, rand 41 | 42 | popfd 43 | 44 | 可以处理的指令包括但不限于 45 | 46 | push 立即数 47 | 48 | mov 寄存器,立即数 49 | 50 | mov 内存,立即数 51 | 52 | 53 | 54 | 局部混淆: 55 | 56 | 添加无用的stc,cmc指令等 57 | 58 | 比如一条指令为mov eax,123 59 | 60 | 你可以在此语句前插入一条 neg eax等等 61 | 62 | 63 | 64 | 函数调用与跳转混淆 65 | 66 | call addr可变形为 67 | 68 | push 返回地址 69 | 70 | jmp addr 71 | 72 | 或者 73 | 74 | push返回地址 75 | 76 | push addr 77 | 78 | ret 79 | 80 | 等等 81 | 82 | 83 | 84 | 防静态跟踪: 85 | 86 | 让ida无法判断堆栈、返回地址的情况 87 | 88 | 比如你可以使用[esp]的值作为下一条指令的地址 89 | 90 | push reg 91 | mov reg,addr 92 | xchg reg, [esp] 93 | ret 94 | 95 | 或者用jmp eax这样的指令,ida无法判断eax的值是多少,也就找不到下一条指令在哪 96 | 97 | 98 | 99 | 100 | 101 | 代码块拆分乱序(稍微有点复杂) 102 | 103 | 先将代码块按随机长度分块 104 | 105 | 1->2->3->4->5->6 106 | 107 | 然后随机顺序 108 | 109 | 3 1 4 5 6 2 110 | 111 | 如果序号左边不等于右边-1,那么代表这一块代码在乱序后还是连接在一起比如4,5,6这块代码 112 | 113 | 3 1之间就需要插入一个jmp指令,从3跳转到4的jmp 114 | 115 | 1 4也需要插入一个1跳转到2的jmp 116 | 117 | 118 | 119 | 120 | 121 | 抹除jcc 122 | 123 | 假设有如下汇编指令 124 | 125 | 0x400000: je 0x400010 126 | 127 | ...... //跳转不成立 128 | 129 | 0x400010: //跳转成立 130 | 131 | 那么可以发现这条jcc指令要么不成立,执行下一行0x400005,要么跳向0x400010 132 | 133 | 那么可以通过动态计算,算出一个值,这个值只有两种可能,就是这两个地址,然后用jmp eax,call eax,push ret之类的转移过去 134 | 135 | 0x400005 0x400010 0x400005 0x400010 136 | 137 | 0 1 1 0 138 | 139 | 可以用乘法、and运算之类的实现 140 | 141 | 142 | 143 | 整个代码的实现还考虑了可以迭代多层,还可以不同混淆方法之间按自定义顺序套来套去,再加上有代码乱序,用跟混淆同样的模板和方法实际上并不是很好去除混淆,并且如果塞入无用数据,模板还原可能更困难 144 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizukiyyds/asm_obfuscator/67dd9166f66aeba4f650644a62e5ed5aef2e09e2/main.cpp -------------------------------------------------------------------------------- /obfuscate.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizukiyyds/asm_obfuscator/67dd9166f66aeba4f650644a62e5ed5aef2e09e2/obfuscate.cpp -------------------------------------------------------------------------------- /obfuscate.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizukiyyds/asm_obfuscator/67dd9166f66aeba4f650644a62e5ed5aef2e09e2/obfuscate.h --------------------------------------------------------------------------------