本帖最后由 LYQingYe 于 2015-8-27 18:35 编辑 ![]() ![]() ![]() ![]() 软件名称:超级视频合并软件 V2.20 编译器:貌似 Delphi6-7 无壳。 这个代码框让我数据丢失了两次,我又重新编过 效果不是很好,真失败。 正常情况下,打开弹出一个NAG窗口 要你注册还是试用,未注册版本合并视频后会有水印。下断点 MessageboxA 就可以找到关键 CALL 在这是 算法课,我就不说明了,很简单的。 从按钮事件开始 _TFrm_Reg_Btn_RegClick proc near 0057EAC8
loc_57EBFB 核心算法CALL 下面接着分析 loc_57EBFB 里面有四个 CALL loc_004E0D24 loc_004E0B4C(包含全部算法代码) loc_004047E8 loc_00403D04 004E0B66 push ebp 004E0B67 push 004E0D0A 004E0B6C push dword ptr fs:[eax] 004E0B6F mov dword ptr fs:[eax], esp 004E0B72 mov eax, [local.1] 004E0B75 call 004043C0 004E0B7A lea edx, [local.4] 004E0B7D mov eax, edi 004E0B7F call 004E0AE4 004E0B84 mov eax, [local.4] ; EAX = 用户名 004E0B87 call 0040469C ; 计算的用户名长度 004E0B8C mov esi, eax 004E0B8E test esi, esi 004E0B90 jle short 004E0BC2 ; 判断 是否为 0 004E0B92 mov ebx, 0x1 ; ebx 初始化 为0 004E0B97 lea edx, [local.6] 004E0B9A mov eax, edi 004E0B9C call 004E0AE4 004E0BA1 mov eax, [local.6] ; eax = 用户名地址 004E0BA4 movzx eax, byte ptr [eax+ebx-0x1] ; eax = 用户名第一位 ASICC值 004E0BA9 lea ecx, [local.5] 004E0BAC xor edx, edx 004E0BAE call 00409528 ; 需要分析 第一层算法 004093C8 or cl, cl 判断cl是否为0 cl = 10固定的 004093CA jnz short 004093E3 不为0不跳 004093CC or eax, eax 004093CE jns short 004093DE 004093D0 neg eax 004093D2 call 004093DE 004093D7 mov al, 0x2D 004093D9 inc ecx 004093DA dec esi 004093DB mov byte ptr [esi], al 004093DD retn 004093DE mov ecx, 0xA 004093E3 push edx 004093E4 push esi 004093E5 xor edx, edx ; edx 清零 004093E7 div ecx ; 此时 eax =X的ASCII 值 58 eax / ecx 商在 eax 余数在 edx 004093E9 dec esi ; esi - 1 004093EA add dl, 0x30 ; 余数 + 0x30 004093ED cmp dl, 0x3A ; 小于则跳转 004093F0 jb short 004093F5 004093F2 add dl, 0x7 004093F5 mov byte ptr [esi], dl 处理后的数据放到 esi指向的内存空间 004093F7 or eax, eax 判断 eax是否为0 为0不跳 004093F9 jnz short 004093E5 第一次循环 : eax = 58 = 'X' ecx = 10 eax/ecx 商=eax = 5 余数 = 8 =edx 余数+30 = 8+30= 38 放入 esi指向空间 第二次循环 eax = 5 ecx = 10 eax /ecx 商 = 0 =eax 循环可以跳走了 余数 =edx = 5 余数 +30 =35 esi-1 放入 前一个字节的空间 相当于 35 38 我们在内存看下 ASCII '585545504F4A4945' 004E0BB3 mov edx, [local.5] ; edx 等于 处理 后的 字符串 004E0BB6 lea eax, [local.2] 004E0BB9 call 004046A4 004E0BBE inc ebx 004E0BBF dec esi 004E0BC0 jnz short 004E0B97 004E0BB3 mov edx, [local.5] ; edx 等于 处理后的字符串地址 004E0BB6 lea eax, [local.2] 004E0BB9 call 004046A4 004E0BBE inc ebx 004E0BBF dec esi 004E0BC0 jnz short 004E0B97 下面就是第二层算法 ,其实没什么作用 004E0BC2 mov eax, [local.2] ; 将 运算的 东东 放到eax004E0BC5 call 0040469C ; 将 这字符串 前 4个 字节的 内容 给 eax004E0BCA mov esi, eax ; eax 给 esi004E0BCC test esi, esi ; 判断 esi是否 为 0004E0BCE jle short 004E0BFC004E0BD0 mov ebx, 0x1 ; ebx 给 0x1 貌似要 计数器004E0BD5 mov eax, [local.2] ; 运算后的 假吗 给 eax004E0BD8 call 0040469C ; 还是 刚才 那样 给前4个字节 数据 给 eax004E0BDD sub eax, ebx ; eax- ebx004E0BDF mov edx, [local.2] ; 假吗运算之 给 edx004E0BE2 mov dl, byte ptr [edx+eax] ; 第一次 取最后 一位004E0BE5 lea eax, [local.7] ; 估计 又是 将此 东西 放入缓冲区004E0BE8 call 004045A8004E0BED mov edx, [local.7]004E0BF0 lea eax, [local.3]004E0BF3 call 004046A4004E0BF8 inc ebx004E0BF9 dec esi004E0BFA jnz short 004E0BD5第二层算法作用也就是 把第一层算法得出的字符串倒过来 ASCII '5494A4F405545585' 004E0BFC lea eax, [local.2] 将处理后的字符串 的二级指针给转化为 一级指针给eax 这个是第一层算法算出的004E0BFF push eax 压入栈,保存起来004E0C00 mov ecx, 0x4 初始化变量 eax = 0x4004E0C05 mov edx, 0x1 初始化变量 edx = 0x1004E0C0A mov eax, [local.3] ; 将倒转后的字符串也就是第二层算法 后的字符串地址给eax004E0C0D call 004048FC 执行后 eax = 倒转字符串前四个字节 5494 字符串的二级指针004E0C12 lea eax, [local.3] 二级指针给eax 转换为 一级指针004E0C15 push eax 指针入栈 保存004E0C16 mov ecx, 0x4 初始化值004E0C1B mov edx, 0x5 初始化值004E0C20 mov eax, [local.3] 倒转字符串地址给 eax004E0C23 call 004048FC 执行后 eax = 倒转后字符串 接着在后四位 4AF4字符串的二级指针004E0C28 mov eax, [local.2] 将前四位字符串地址给eax004E0C2B call 0040469C 计算出他的长度004E0C30 cmp eax, 0x4 判断长度是否等于 0x4 等于则跳,我先撤了。已经跳了004E0C33 jge short 004E0C64004E0C35 mov eax, [local.2]004E0C38 call 0040469C004E0C3D mov ebx, eax004E0C3F cmp ebx, 0x3004E0C42 jg short 004E0C64004E0C44 /lea ecx, [local.8]004E0C47 |mov eax, ebx004E0C49 |shl eax, 0x2004E0C4C |xor edx, edx004E0C4E |call 00409528004E0C53 |mov edx, [local.8]004E0C56 |lea eax, [local.2]004E0C59 |call 004046A4004E0C5E |inc ebx004E0C5F |cmp ebx, 0x4004E0C62 \jnz short 004E0C44004E0C64 mov eax, [local.3] 接着后四位的字符串地址给eax 4AF4004E0C67 call 0040469C 计算器其长度004E0C6C cmp eax, 0x4 判断长度 跳了004E0C6F jge short 004E0CA0004E0C71 mov eax, [local.3]004E0C74 call 0040469C004E0C79 mov ebx, eax004E0C7B cmp ebx, 0x3004E0C7E jg short 004E0CA0004E0C80 /lea ecx, [local.9]004E0C83 |mov eax, ebx004E0C85 |shl eax, 0x2004E0C88 |xor edx, edx004E0C8A |call 00409528004E0C8F |mov edx, [local.9]004E0C92 |lea eax, [local.3]004E0C95 |call 004046A4004E0C9A |inc ebx004E0C9B |cmp ebx, 0x4004E0C9E \jnz short 004E0C80004E0CA0 lea eax, [local.10]004E0CA3 push eax004E0CA4 mov eax, dword ptr [edi+0x4] 在这产生一个新的字符串 vj268v2013 不要奇怪这个字符串是固定的 就是这个软件的exe名称和版本004E0CA7 mov ecx, 0x4004E0CAC mov edx, 0x1004E0CB1 call 004048FC 取前四位字符串 vj26004E0CB6 push [local.10]004E0CB9 push 004E0D20 ; UNICODE '-'004E0CBE push [local.2]004E0CC1 lea eax, [local.11]004E0CC4 push eax004E0CC5 mov eax, dword ptr [edi+0x4]004E0CC8 mov ecx, 0x5004E0CCD mov edx, 0x5004E0CD2 call 004048FC 取后 5位字符串 8v201004E0CD7 push [local.11]004E0CDA push 004E0D20 ; UNICODE '-' 看到这 杠号是不是很熟悉啊,哈哈 你已经拆到了004E0CDF push [local.3]004E0CE2 mov eax, [local.1]004E0CE5 mov edx, 0x6004E0CEA call 0040475C004E0CEF xor eax, eax004E0CF1 pop edx004E0CF2 pop ecx004E0CF3 pop ecx004E0CF4 mov dword ptr fs:[eax], edx004E0CF7 push 004E0D11004E0CFC lea eax, [local.11]004E0CFF mov edx, 0xA004E0D04 call 004043E4到最后就是 将前面出现过的字符串组装了,我就不废话了vj26-54948v201-A4F4 5494是将用户名经过二层算法获得的前四个字节的字符串A4F4是在后四位的字符串 其他的就是这个软件的 exe名称了 最后这个就是注册码了。。我们测试下。。 注册成功,哈哈,肚子太饿了,我就偷懒下,写这个帖子花了我三个小时,边分析边写帖子,大伙给我点动力把。一路 艰难险阻是不是都过来了?是否体验到算法带来的成就感?本次教程到此结束,我是 LYQingYe 我爱雪坡姐, 感谢大家支持。 |
|