本帖最后由 LYQingYe 于 2015-9-4 13:48 编辑
放荡了三天,我又回来了, 介于前面的那两节算法课,我看到了不少的意见和建议,为此我只想说,感谢大家宝贵的建议, 介于很多朋友说 “看不懂” , 我提以下小小的建议。
① 要懂得MOV SUB ADD DIV MUL INC DEC 几个对数据操作的简单指令 以及他们的扩展形式指令,MOV EAX,BYTE PTR [ECX]
在这条指令中 [EAX] 的作用是 取出 ECX指向的内存单元的值,因为是32位的指令,最多能取出四个字节的内容,然而 BYTE PTR 控制了获取的字节数, BYTE等于一个字节,所以 这条指令的作用就是把,ECX指向内存单元的第一个字节内容给EAX ,当然 还有很多扩展的,例如 像 MOV EAX ,DS:[EAX] 之类的我就不一一详解了。
② 懂得数据操作指令还不够,例如还要懂得 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:[eax]
- 004C2A5E 648920 mov dword ptr fs:[eax], esp
- 004C2A61 a15cfc4c00 mov eax, dword ptr [0x4cfc5c]
- 004C2A66 803800 cmp byte ptr [eax], 0
- 004C2A69 740f je 0x4c2a7a
- 004C2A6B c7834c020000020>mov dword ptr [ebx + 0x24c], 2
- 004C2A75 e9ff000000 jmp 0x4c2b79
- 004C2A7A 8d55fc lea edx, dword ptr [ebp - 4]
- 004C2A7D 8b83f8020000 mov eax, dword ptr [ebx + 0x2f8]
- 004C2A83 e8f4f8faff call 0x47237c ; GetText(Void) GetUserName
- 004C2A88 8b55fc mov edx, dword ptr [ebp - 4] ; EDX - > UserName WWW.XUEPOJIE.COM
- 004C2A8B a1e8fe4c00 mov eax, dword ptr [0x4cfee8]
- 004C2A90 e84f1ff4ff call 0x4049e4
- 004C2A95 8d55f8 lea edx, dword ptr [ebp - 8]
- 004C2A98 8b83fc020000 mov eax, dword ptr [ebx + 0x2fc]
- 004C2A9E e8d9f8faff call 0x47237c ; GetTexy(Void) GetID
- 004C2AA3 8b55f8 mov edx, dword ptr [ebp - 8] ; EDX-> ID '8888888888'
- 004C2AA6 a14cfe4c00 mov eax, dword ptr [0x4cfe4c]
- 004C2AAB e8341ff4ff call 0x4049e4
- 004C2AB0 8b154cfe4c00 mov edx, dword ptr [0x4cfe4c] ; EDX-> P -> '8888888888' 二级指针 指向 ID
- 004C2AB6 8b12 mov edx, dword ptr [edx] ; 转化为一级指针 -> ID '8888888888'
- 004C2AB8 a154fc4c00 mov eax, dword ptr [0x4cfc54]
- 004C2ABD 8b00 mov eax, dword ptr [eax]
- 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 [ebp - 4], edx ; 将ID '8888888888' 放入 局部变量
- 004CBF84 8b45fc mov eax, dword ptr [ebp - 4] ; 再把 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:[eax]
- 004CBF97 648920 mov dword ptr fs:[eax], esp ; 用到 FS寄存器 就没什么好看的了,VC机制操作
- 004CBF9A 33db xor ebx, ebx
- 004CBF9C 8b45fc mov eax, dword ptr [ebp - 4] ; EAX ->ID '8888888888'
- 004CBF9F e8ac8cf3ff call 0x404c50 ; 判断ID所在内存地址是否合法顺便取出他的长度VC程序会放在字符串地址前4个字节
- 004CBFA4 83f80a cmp eax, 0xa ; 长度要 = 10 0XA否则直接返回 0 走向失败
- 004CBFA7 755c jne 0x4cc005
- 004CBFA9 8b55fc mov edx, dword ptr [ebp - 4] ; 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 [edi - 4] ; ECX = ID的长度 0XA
- 00404FA6 57 push edi ; ID 入栈保存起来
- 00404FA7 8b56fc mov edx, dword ptr [esi - 4] ; EDX = 字符串 'A1910'的长度
- 00404FAA 4a dec edx ; EDX = EDX - 1
- 00404FAB 781b js 0x404fc8
- 00404FAD 8a06 mov al, byte ptr [esi] ; 字符串第一个字节 '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:[edi]
- 00404FB6 7510 jne 0x404fc8 ; 找不到则跳 ,走向死亡
复制代码 详解那令人困惑的汇编指令 repne scasb al, byte ptr es:[edi] 在字符串查找指定的字节数据 如果找不到 则 ZF = 0 或者 ECX = 0
repne scasb al, byte ptr es:[edi] 在 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 [esi], byte ptr es:[edi] 该指令下面详解
- 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 [esi], byte ptr es:[edi] 这指令和上面的 差不多 ,此时 ESI -> '1910'这个字符串 EDI ->'888888888' 我们的ID 是 A8888888888 ,从 字面上来看 repe 是重复操作 指令, cmp 是比较 指令 s b 是 string byte的缩写 ,故 指令作用为 ,重复操作,分别 比较 ESI -> 指向的字符串 EDI -> 指向的字符串 一定要相等,否则下面就会走向死亡 所以说 ID里面 要有 '1910' 这个字符串
结合上面,我们可以得出 前5个固定的字符 'A1910'
这次我们换个 ID A191088888 好了,我们继续 。
- 004CBFB7 /7410 je 0x4cbfc9
- 004CBFB9 |8b55fc mov edx, dword ptr [ebp - 4]
- 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 [ebp - 4] ; EDX ->ID 'A191088888'
- 004CBFD1 8a5402ff mov dl, byte ptr [edx + eax - 1]
- 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 [ebp - 4] ; EAX -> ID
- 004CBFE8 0fb64008 movzx eax, byte ptr [eax + 8] ; 取倒数第二个字节 内容给EAX
- 004CBFEC 8b55fc mov edx, dword ptr [ebp - 4] ; EAX -> ID
- 004CBFEF 0fb65209 movzx edx, byte ptr [edx + 9] ; 取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:[eax], edx
- 004CC00D 6822c04c00 push 0x4cc022
- 004CC012 8d45fc lea eax, dword ptr [ebp - 4]
- 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++ 所以无法写出注册机。在这就偷懒一下 。本次教程就到此结束,我是LYQingYe 我爱雪坡姐。 看完了的 别忘记评分哦。
|