送给那些不会分析算法朋友的一篇图文教程-exescope
本帖最后由 LYQingYe 于 2015-9-4 13:48 编辑{:5_118:}放荡了三天,我又回来了,{:5_121:}介于前面的那两节算法课,我看到了不少的意见和建议,为此我只想说,感谢大家宝贵的建议,{:5_116:}介于很多朋友说 “看不懂” , 我提以下小小的建议。
① 要懂得MOV SUB ADD DIV MUL INC DEC 几个对数据操作的简单指令 以及他们的扩展形式指令,MOV EAX,BYTE PTR
在这条指令中 的作用是 取出 ECX指向的内存单元的值,因为是32位的指令,最多能取出四个字节的内容,然而 BYTE PTR 控制了获取的字节数, BYTE等于一个字节,所以 这条指令的作用就是把,ECX指向内存单元的第一个字节内容给EAX ,当然 还有很多扩展的,例如 像 MOV EAX ,DS: 之类的我就不一一详解了。
② 懂得数据操作指令还不够,例如还要懂得CMP TEST JNZ JZ JB 之类的逻辑流程控制指令,什么时候跳,什么时候不跳,这是必须要懂的,指令不一定要你背下来,看多了用多了就记住了。
③ 提升自己的逻辑连贯能力,就比如 某个算法 用到了机器码 或者 USENAME 做运算的时候 ,就要留心 用到机器码 或者 USENAME的CALL ,用记事本 记下每一次的运行结果,通过观察计算获得注册码,高级的就需要学会逆向算法。
今天,我打算给那些不会分析算法的人做一篇简单而又不简单的图文教程。为什么是简单 ? 因为他的流程很简单,之所以又不简单,是因为在某些小流程里不简单。那我们就愉快的开始吧!
软件名称:eXeScope
eXeScope是款功能强大的exe程序修改器,能修改exe程序中的字体、菜单位置、对话框排序、字符串、图片资源等。
保护状态:无壳,貌似 VC编译,这并不重要。哈
先说简单一下关键CALL的找法,当输入错误的时候会弹出一个错误 对话框, IVALID ID OR NAME 我采用的方法是,暂停->看堆栈窗口,查找我们输入的 NAME 和 ID 然后 找到相应的返回 CALL 某些CALL可能是 消息循环。 可以看下图 。
关键CALL地址004C2A44 ,本次调试用的 USERNAME 为 WWW.XUEPOJIE.COM ID 为 8888888888
Frsit那简单而又熟悉的开始
004C2A44 55 push ebp
004C2A45 8bec mov ebp, esp
004C2A47 33c9 xor ecx, ecx
004C2A49 51 push ecx
004C2A4A 51 push ecx
004C2A4B 51 push ecx
004C2A4C 51 push ecx
004C2A4D 51 push ecx
004C2A4E 53 push ebx
004C2A4F 56 push esi
004C2A50 57 push edi
004C2A51 8bd8 mov ebx, eax
004C2A53 33c0 xor eax, eax
004C2A55 55 push ebp
004C2A56 68a12b4c00 push 0x4c2ba1
004C2A5B 64ff30 push dword ptr fs:
004C2A5E 648920 mov dword ptr fs:, esp
004C2A61 a15cfc4c00 mov eax, dword ptr
004C2A66 803800 cmp byte ptr , 0
004C2A69 740f je 0x4c2a7a
004C2A6B c7834c020000020>mov dword ptr , 2
004C2A75 e9ff000000 jmp 0x4c2b79
004C2A7A 8d55fc lea edx, dword ptr
004C2A7D 8b83f8020000 mov eax, dword ptr
004C2A83 e8f4f8faff call 0x47237c ; GetText(Void)GetUserName
004C2A88 8b55fc mov edx, dword ptr ; EDX - >UserName WWW.XUEPOJIE.COM
004C2A8B a1e8fe4c00 mov eax, dword ptr
004C2A90 e84f1ff4ff call 0x4049e4
004C2A95 8d55f8 lea edx, dword ptr
004C2A98 8b83fc020000 mov eax, dword ptr
004C2A9E e8d9f8faff call 0x47237c ; GetTexy(Void)GetID
004C2AA3 8b55f8 mov edx, dword ptr ; EDX-> ID'8888888888'
004C2AA6 a14cfe4c00 mov eax, dword ptr
004C2AAB e8341ff4ff call 0x4049e4
004C2AB0 8b154cfe4c00 mov edx, dword ptr ; EDX-> P -> '8888888888' 二级指针 指向 ID
004C2AB6 8b12 mov edx, dword ptr ; 转化为一级指针 -> ID '8888888888'
004C2AB8 a154fc4c00 mov eax, dword ptr
004C2ABD 8b00 mov eax, dword ptr
004C2ABF e8b8940000 call 0x4cbf7c ; 意味着这是一个关键CALL
004C2AC4 84c0 test al, al ; 判断 AL是否等于 0
004C2AC6 0f848d000000 je 0x4c2b5a ; 如果AL = 0走向死亡 。
既然是关键CALL 我们就进去看下 。004CBF7C 55 push ebp
004CBF7D 8bec mov ebp, esp
004CBF7F 51 push ecx
004CBF80 53 push ebx
004CBF81 8955fc mov dword ptr , edx ; 将ID '8888888888' 放入 局部变量
004CBF84 8b45fc mov eax, dword ptr ; 再把 ID 给EAX
004CBF87 e8b48ef3ff call 0x404e40 ; 判断ID 所在内存地址 是否合法
004CBF8C 33c0 xor eax, eax ; EAX 清零
004CBF8E 55 push ebp
004CBF8F 681bc04c00 push 0x4cc01b
004CBF94 64ff30 push dword ptr fs:
004CBF97 648920 mov dword ptr fs:, esp ; 用到 FS寄存器 就没什么好看的了,VC机制操作
004CBF9A 33db xor ebx, ebx
004CBF9C 8b45fc mov eax, dword ptr ; EAX ->ID '8888888888'
004CBF9F e8ac8cf3ff call 0x404c50 ; 判断ID所在内存地址是否合法顺便取出他的长度VC程序会放在字符串地址前4个字节
004CBFA4 83f80a cmp eax, 0xa ; 长度要 = 100XA否则直接返回 0 走向失败
004CBFA7 755c jne 0x4cc005
004CBFA9 8b55fc mov edx, dword ptr ; EDX ->ID '8888888888' 下面好有个怪怪的字符串,跟进去 看看
004CBFAC b830c04c00 mov eax, 0x4cc030 ; ASCII "A1910"
004CBFB1 e8de8ff3ff call 0x404f94跟进去看看
004CBFB6 48 dec eax
004CBFB7 7410 je 0x4cbfc9
Second那引人的困境
00404F94 85c0 test eax, eax ; 判断字符串是否合法 'A1910'
00404F96 7440 je 0x404fd8
00404F98 85d2 test edx, edx ; 判断 ID '8888888888' 是否合法
00404F9A 7431 je 0x404fcd
00404F9C 53 push ebx
00404F9D 56 push esi
00404F9E 57 push edi
00404F9F 89c6 mov esi, eax ; ESI -> 字符串 'A1910'
00404FA1 89d7 mov edi, edx ; EDI -> ID '8888888888'
00404FA3 8b4ffc mov ecx, dword ptr ; ECX = ID的长度 0XA
00404FA6 57 push edi ; ID 入栈保存起来
00404FA7 8b56fc mov edx, dword ptr ; EDX = 字符串 'A1910'的长度
00404FAA 4a dec edx ; EDX = EDX - 1
00404FAB 781b js 0x404fc8
00404FAD 8a06 mov al, byte ptr ; 字符串第一个字节 'A'的ASICC 值给 AL 指向一个新的固定字符串 ‘1423’
00404FAF 46 inc esi ; ESI= ESI + 1 指针往后一位 挪动一位
00404FB0 29d1 sub ecx, edx
00404FB2 7e14 jle 0x404fc8
00404FB4 f2ae repne scasb al, byte ptr es:
00404FB6 7510 jne 0x404fc8 ; 找不到则跳,走向死亡
详解那令人困惑的汇编指令 repne scasb al, byte ptr es: 在字符串查找指定的字节数据 如果找不到 则 ZF = 0或者 ECX = 0
repne scasb al, byte ptr es: 在 EDI 指向的字符串中 匹配 和 AL 一样的字符串 如果匹配到 则 ECX - 1 匹配不到 则 ZF = 0或者 ECX = 0,并且重复判断 直到 匹配完 EDI指向的字符串。
此时 AL = '41'- 'A' EDI 是ID '8888888888'所以说 ID 第一位 要 是 字母A否则则走向死亡
这次 我们换个 ID 'A88888888' 继续往下
00404FB8 89cb mov ebx, ecx ; EBX = 字符串长度
00404FBA 56 push esi
00404FBB 57 push edi
00404FBC 89d1 mov ecx, edx
00404FBE f3a6 repe cmpsb byte ptr , byte ptr es:该指令下面详解
00404FC0 5f pop edi
00404FC1 5e pop esi
00404FC2 740c je 0x404fd0 ; 相等则跳 走向胜利
00404FC4 89d9 mov ecx, ebx
00404FC6^ ebec jmp 0x404fb4
00404FC8 5a pop edx
00404FC9 31c0 xor eax, eax
00404FCB eb08 jmp 0x404fd5
00404FCD 31c0 xor eax, eax
00404FCF c3 ret
repe cmpsb byte ptr , byte ptr es: 这指令和上面的 差不多 ,此时 ESI -> '1910'这个字符串EDI ->'888888888'我们的ID 是 A8888888888 ,从 字面上来看 repe 是重复操作 指令, cmp 是比较 指令 s b 是 stringbyte的缩写 ,故 指令作用为 ,重复操作,分别 比较 ESI -> 指向的字符串 EDI -> 指向的字符串一定要相等,否则下面就会走向死亡所以说 ID里面 要有 '1910' 这个字符串
结合上面,我们可以得出 前5个固定的字符 'A1910'
这次我们换个 ID A191088888好了,我们继续 。
004CBFB7 /7410 je 0x4cbfc9
004CBFB9 |8b55fc mov edx, dword ptr
004CBFBC |b840c04c00 mov eax, 0x4cc040 ; ASCII "A1423"
004CBFC1 |e8ce8ff3ff call 0x404f94这个CALL和上面那个是一样的。
004CBFC6 |48 dec eax
004CBFC7 |753c jne 0x4cc005
可以得出,正确的ID 前五个字符 必须要是 A1423 或者 是 A1910继续 往下
004CBFC9 b802000000 mov eax, 2 ; EAX 初始化 为 2
004CBFCE 8b55fc mov edx, dword ptr ; EDX ->ID 'A191088888'
004CBFD1 8a5402ff mov dl, byte ptr
004CBFD5 80fa30 cmp dl, 0x30
004CBFD8 722b jb 0x4cc005 ; ASCII 小于 0X30 则走向失败
004CBFDA 80fa39 cmp dl, 0x39
004CBFDD 7726 ja 0x4cc005 ; ASCII 大于 0x 39则走向失败了
004CBFDF 40 inc eax ; 字符串指针往后移动 一个字节
004CBFE0 83f80b cmp eax, 0xb ; 判断是否 遍历到 字符串 后面了
004CBFE3^ 75e9 jne 0x4cbfce
004CBFE5 8b45fc mov eax, dword ptr ; EAX -> ID
004CBFE8 0fb64008 movzx eax, byte ptr ; 取倒数第二个字节 内容给EAX
004CBFEC 8b55fc mov edx, dword ptr ; EAX -> ID
004CBFEF 0fb65209 movzx edx, byte ptr ; 取ID 最后一位值给EDX
004CBFF3 03c2 add eax, edx ; EAX + EDX
004CBFF5 b90a000000 mov ecx, 0xa ; ECX 初始化 为 0XA
004CBFFA 33d2 xor edx, edx ; EDX 清零
004CBFFC f7f1 div ecx ; EAX / ECX 商在 EAX 余数在 EDX
004CBFFE 83fa04 cmp edx, 4 ; 不相等 就跳
004CC001 7502 jne 0x4cc005 ; 跳过去就死了
004CC003 b301 mov bl, 1
004CC005 33c0 xor eax, eax
004CC007 5a pop edx
004CC008 59 pop ecx
004CC009 59 pop ecx
004CC00A 648910 mov dword ptr fs:, edx
004CC00D 6822c04c00 push 0x4cc022
004CC012 8d45fc lea eax, dword ptr
004CC015 e87689f3ff call 0x404990
004CC01A c3 ret
通过上面的算法我们 可以知道 正确的 ID要满足 一下 条件 : 前五个字符 必须要是 A1423 或者 是 A1910 长度必须为 0XA = 10
全部字符的 ASCII值 必须大于等于 0x30小于等于 0x39 所以满足规则只有 一下字符 0,1,2,3,4,5,6,7,8,9 还有 最后 两位字符的ASCII值的和,在这里 我设为 STR 要满足以下条件STR / 0XA 商 不能等于 0余数 要等于B
知道需要满足的规则 我们来 推导 后 两位的 逆运算 。
现在设 后两位字符的 ASCII 为 M,N 且 M , N满足大于等于 0X30小于等于 0X39 ,另外设一个 未知数Z 为正整数
则应该有M + N =Z X A + B 因为本人 不会C++ {:5_191:}所以无法写出注册机。在这就偷懒一下 。本次教程就到此结束,我是LYQingYe 我爱雪坡姐。 看完了的 别忘记评分哦。{:5_193:}
此贴必火,支持 好吧,算法是很强悍,来观望学习下,谢谢。 {:5_116:}虽然没看明白 但是还是支持一下 我相信对汇编略懂的人,看完这个教程,应该就没啥问题了,应该都可以简单的分析算法了。 学习一下哦 楼主好样的!
感谢发布算法教程,学习了!! 不错。感谢楼主无私分享了。支持 膜拜算法大牛,真厉害