admin管理员组文章数量:1122850
某pdf转word v6.3.0.2算法分析
【文章标题】某pdf转word v6.3.0.2算法分析
【文章作者】jieliuhouzi
【原版下载】www.pdfcword
【保护方式】序列号
【分析过程】
一. 去掉随机基址
直接OD载入程序,入口是“一call一jmp”,基本上就是VS高版本编译的
为了避免随机基址的影响,先去除随机基址。找到“PE”下一行偏移为6的字节处,将“02”修改为“03”,可去掉随机基址
二. 注册码分析
随便输入一串注册码
此处MessageBox是断不下来的,使用“F12大法”:OD中按F12暂停程序,Alt+K查看调用堆栈
“黄框”部分是ycomuiu.DuiLib::CWindowWnd::ShowModal,搞这个软件之前我也没用过这个框架,百度了一下“在duilib中,可以调用CWindowWnd::ShowModal()来实现模态框的显示”, 在段尾retn处下断点
注意:消息循环是在CWindowWnd::ShowModal()中实现的,假如在ShowModal下面的几个函数返回处下断点,基本上就迷失在消息循环中了,由于不了解DuiLib我在这里耗了很久才跳出来的
然后点击“确定”按钮,会在刚才下断的地方段下来,F8
1 .text:00467F03 lea ecx, [ebp+var_148] 2 .text:00467F09 push ecx 3 .text:00467F0A mov ecx, [ebp+var_3B0] 4 .text:00467F10 call sub_467D30 5 .text:00467F15 push ecx 6 .text:00467F16 mov ecx, esp 7 .text:00467F18 lea edx, [ebp+var_10] 8 .text:00467F1B push edx 9 .text:00467F1C call sub_4069F0 10 .text:00467F21 lea eax, [ebp+var_144] 11 .text:00467F27 push eax ; 机器码 12 .text:00467F28 call sub_4325C0 ; 计算出注册码 13 .text:00467F2D add esp, 8 14 .text:00467F30 lea ecx, [ebp+var_148] 15 .text:00467F36 call unknown_libname_1 ; Microsoft VisualC 2-14/net runtime 16 .text:00467F3B push eax ; 我们自己输入的假码 17 .text:00467F3C lea ecx, [ebp+var_144] 18 .text:00467F42 call sub_42C270 ; 注册码比较 19 .text:00467F47 test eax, eax 20 .text:00467F49 jz short loc_467F87 ; 跳过注册码错误 21 .text:00467F4B push 40h 22 .text:00467F4D mov ecx, [ebp+var_3B0] 23 .text:00467F53 mov edx, [ecx+4] 24 .text:00467F56 push edx 25 .text:00467F57 push offset aSnerror 26 .text:00467F5C call sub_409A20 ; 弹窗提示“序列号错误,请重新输入” 27 .text:00467F61 add esp, 0Ch 28 .text:00467F64 lea ecx, [ebp+var_144] 29 .text:00467F6A call sub_4013B0 30 .text:00467F6F lea ecx, [ebp+var_148] 31 .text:00467F75 call sub_4013B0 32 .text:00467F7A lea ecx, [ebp+var_10] 33 .text:00467F7D call sub_4013B0 34 .text:00467F82 jmp loc_4683F1 35 .text:00467F87 ; --------------------------------------------------------------------------- 36 .text:00467F87 37 .text:00467F87 loc_467F87: ; CODE XREF: sub_467EA0+A9↑j 38 .text:00467F87 lea eax, [ebp+var_4] 39 .text:00467F8A push eax 40 .text:00467F8B call sub_433840 41 .text:00467F90 add esp, 4 42 .text:00467F93 lea ecx, [ebp+var_4] 43 .text:00467F96 call sub_4070F0 44 .text:00467F9B movzx ecx, al 45 .text:00467F9E test ecx, ecx 46 .text:00467FA0 jz loc_46802D 47 .text:00467FA6 mov edx, [ebp+var_3B0] 48 .text:00467FAC mov eax, [edx+4] 49 .text:00467FAF push eax 50 .text:00467FB0 lea ecx, [ebp+var_3A0] 51 .text:00467FB6 push ecx 52 .text:00467FB7 call sub_40D670 ; 弹出验证邮箱对话框 53 .text:00467FBC add esp, 8 54 .text:00467FBF push eax 55 .text:00467FC0 lea ecx, [ebp+var_4] 56 .text:00467FC3 call sub_4068A0 57 .text:00467FC8 lea ecx, [ebp+var_3A0] 58 .text:00467FCE call sub_4013B0 59 .text:00467FD3 lea ecx, [ebp+var_4] ; 检查邮箱是否合法 60 .text:00467FD6 call sub_4070F0 61 .text:00467FDB movzx edx, al 62 .text:00467FDE test edx, edx 63 .text:00467FE0 jz short loc_468026 ; 跳过“不提供购买邮箱的错误提示” 64 .text:00467FE2 push 40h 65 .text:00467FE4 mov eax, [ebp+var_3B0] 66 .text:00467FEA mov ecx, [eax+4] 67 .text:00467FED push ecx 68 .text:00467FEE push offset aVipnotemail ; 69 .text:00467FF3 call sub_409A20 70 .text:00467FF8 add esp, 0Ch 71 .text:00467FFB lea ecx, [ebp+var_4] 72 .text:00467FFE call sub_4013B0 73 .text:00468003 lea ecx, [ebp+var_144] 74 .text:00468009 call sub_4013B0 75 .text:0046800E lea ecx, [ebp+var_148] 76 .text:00468014 call sub_4013B0 77 .text:00468019 lea ecx, [ebp+var_10] 78 .text:0046801C call sub_4013B0 79 .text:00468021 jmp loc_4683F1 80 .text:00468026 ; --------------------------------------------------------------------------- 81 .text:00468026 82 .text:00468026 loc_468026: ; CODE XREF: sub_467EA0+140↑j 83 .text:00468026 mov [ebp+var_14], 1 84 .text:0046802D 85 .text:0046802D loc_46802D: ; CODE XREF: sub_467EA0+100↑j 86 .text:0046802D lea ecx, [ebp+var_1C] 87 .text:00468030 call ds:??0CWaitCursor@DuiLib@@QAE@XZ 88 .text:00468036 push ecx 89 .text:00468037 mov ecx, esp 90 .text:00468039 lea edx, [ebp+var_4] 91 .text:0046803C push edx ; 邮箱 92 .text:0046803D call sub_4069F0 93 .text:00468042 mov ecx, offset unk_4CB420 94 .text:00468047 call sub_46CC70 ; 网络验证:验证注册邮箱是否合法 95 .text:0046804C mov [ebp+var_C], eax 96 .text:0046804F cmp [ebp+var_C], 1 97 .text:00468053 jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!” 98 .text:00468055 push offset word_4B2D6C 99 .text:0046805A call sub_433800 100 .text:0046805F add esp, 4 101 .text:00468062 push 40h 102 .text:00468064 mov eax, [ebp+var_3B0] 103 .text:0046806A mov ecx, [eax+4] 104 .text:0046806D push ecx 105 .text:0046806E push offset aVipnotfoundema ; "VIPNotFoundEmail" 106 .text:00468073 call sub_409A20 ; 弹出“邮箱验证失败,无法注册软件” 107 .text:00468078 add esp, 0Ch 108 .text:0046807B lea ecx, [ebp+var_1C] 109 .text:0046807E call ds:??1CWaitCursor@DuiLib@@QAE@XZ 110 .text:00468084 lea ecx, [ebp+var_4] 111 .text:00468087 call sub_4013B0 112 .text:0046808C lea ecx, [ebp+var_144] 113 .text:00468092 call sub_4013B0 114 .text:00468097 lea ecx, [ebp+var_148] 115 .text:0046809D call sub_4013B0 116 .text:004680A2 lea ecx, [ebp+var_10] 117 .text:004680A5 call sub_4013B0 118 .text:004680AA jmp loc_4683F1 119 .text:004680AF ; --------------------------------------------------------------------------- 120 .text:004680AF jmp loc_468140 121 .text:004680B4 ; --------------------------------------------------------------------------- 122 .text:004680B4 123 .text:004680B4 loc_4680B4: ; CODE XREF: sub_467EA0+1B3↑j 124 .text:004680B4 cmp [ebp+var_C], 0
此处重点分析注册码生成部分:
.text:004325E0 lea ecx, [ebp+arg_4] ; 机器码 .text:004325E3 call sub_4017A0 ; 将机器码转换为字符串 .text:004325E8 mov [ebp+lpString], eax .text:004325EB cmp [ebp+lpString], 0 .text:004325EF jnz short loc_4325FD .text:004325F1 mov [ebp+var_C4], 0 .text:004325FB jmp short loc_432661 .text:004325FD ; --------------------------------------------------------------------------- .text:004325FD .text:004325FD loc_4325FD: ; CODE XREF: sub_4325C0+2F↑j .text:004325FD mov eax, [ebp+lpString] .text:00432600 push eax .text:00432601 call ds:lstrlenW .text:00432607 add eax, 1 .text:0043260A mov [ebp+var_8], eax .text:0043260D cmp [ebp+var_8], 3FFFFFFFh .text:00432614 jle short loc_432622 .text:00432616 mov [ebp+var_C8], 0 .text:00432620 jmp short loc_432655 .text:00432622 ; --------------------------------------------------------------------------- .text:00432622 .text:00432622 loc_432622: ; CODE XREF: sub_4325C0+54↑j .text:00432622 mov eax, [ebp+var_8] .text:00432625 shl eax, 1 .text:00432627 call __alloca_probe_16 ; alloc申请空间 .text:0043262C mov [ebp+lpMultiByteStr], esp .text:00432632 mov ecx, [ebp+CodePage] .text:00432638 push ecx .text:00432639 mov edx, [ebp+var_8] .text:0043263C shl edx, 1 .text:0043263E push edx .text:0043263F mov eax, [ebp+lpString] .text:00432642 push eax ; 机器码UNICODE字符串 .text:00432643 mov ecx, [ebp+lpMultiByteStr] .text:00432649 push ecx ; 接收转换完的ASCII字符串 .text:0043264A call sub_408BC0 ; Unicode机器码转Ascii .text:0043264F mov [ebp+var_C8], eax .text:00432655 .text:00432655 loc_432655: ; CODE XREF: sub_4325C0+60↑j .text:00432655 mov edx, [ebp+var_C8] .text:0043265B mov [ebp+var_C4], edx .text:00432661 .text:00432661 loc_432661: ; CODE XREF: sub_4325C0+3B↑j .text:00432661 mov eax, [ebp+var_C4] .text:00432667 mov [ebp+var_4], eax .text:0043266A push 0FFFFFFFFh .text:0043266C lea ecx, [ebp+arg_4] ; 机器码地址 .text:0043266F call sub_409B30 ; 获取长度 .text:00432674 mov cl, ds:byte_4A5912 .text:0043267A mov [ebp+var_98], cl .text:00432680 push 3Fh .text:00432682 push 0 .text:00432684 lea edx, [ebp+var_97] .text:0043268A push edx .text:0043268B call _memset .text:00432690 add esp, 0Ch .text:00432693 push 40h .text:00432695 push 0 .text:00432697 lea eax, [ebp+var_98] .text:0043269D push eax .text:0043269E call _memset .text:004326A3 add esp, 0Ch .text:004326A6 push 40h .text:004326A8 lea ecx, [ebp+var_98] .text:004326AE push ecx ; szBuffer .text:004326AF mov edx, [ebp+var_4] .text:004326B2 push edx ; ASCII格式机器码字符串 .text:004326B3 call Get_MD5 ; 计算机器码的MD5值 .text:004326B8 add esp, 0Ch .text:004326BB lea eax, [ebp+var_98] .text:004326C1 push eax .text:004326C2 call __strupr ; 机器码的MD5字符串转换为大写字符串 .text:004326C7 add esp, 4 .text:004326CA mov cl, ds:byte_4A5913 .text:004326D0 mov [ebp+MultiByteStr], cl .text:004326D3 push 3Fh .text:004326D5 push 0 .text:004326D7 lea edx, [ebp+var_4F] .text:004326DA push edx .text:004326DB call _memset .text:004326E0 add esp, 0Ch .text:004326E3 push 40h .text:004326E5 push 0 .text:004326E7 lea eax, [ebp+MultiByteStr] .text:004326EA push eax .text:004326EB call _memset .text:004326F0 add esp, 0Ch .text:004326F3 push 40h .text:004326F5 lea ecx, [ebp+MultiByteStr] .text:004326F8 push ecx .text:004326F9 lea edx, [ebp+var_98] .text:004326FF push edx .text:00432700 call Get_MD5 ; 将机器码的MD5字符串再次计算MD5 .text:00432705 add esp, 0Ch .text:00432708 lea eax, [ebp+MultiByteStr] .text:0043270B push eax .text:0043270C lea ecx, [ebp+obj_A8] .text:00432712 call sub_430200 ; 将ASCII转为UNICODE .text:00432717 push 4 .text:00432719 lea ecx, [ebp+var_AC] .text:0043271F push ecx ; 传出参数:用来存储计算完的注册码的第4段 .text:00432720 lea ecx, [ebp+obj_A8] ; MD5字符串 .text:00432726 call RegistrationCode_4 ; 注册码的第四段 .text:0043272B push eax .text:0043272C push 4 .text:0043272E push 16h .text:00432730 lea edx, [ebp+var_B0] .text:00432736 push edx .text:00432737 lea ecx, [ebp+obj_A8] .text:0043273D call RegistrationCode_2_3 ; 注册码的第三段 .text:00432742 push eax .text:00432743 push 4 .text:00432745 push 0Ah .text:00432747 lea eax, [ebp+var_B4] .text:0043274D push eax .text:0043274E lea ecx, [ebp+obj_A8] .text:00432754 call RegistrationCode_2_3 ; 注册码的第二段 .text:00432759 push eax .text:0043275A push 4 .text:0043275C lea ecx, [ebp+var_B8] .text:00432762 push ecx .text:00432763 lea ecx, [ebp+obj_A8] .text:00432769 call RegistrationCode_1 ; 注册码的第一段 .text:0043276E push eax .text:0043276F lea edx, [ebp+var_BC] .text:00432775 push edx .text:00432776 call MyStrCat ; 类似于strcat功能 .text:0043277B add esp, 0Ch .text:0043277E push eax .text:0043277F lea eax, [ebp+var_C0] .text:00432785 push eax .text:00432786 call MyStrCat .text:0043278B add esp, 0Ch .text:0043278E push eax .text:0043278F lea ecx, [ebp+var_A4] .text:00432795 push ecx .text:00432796 call MyStrCat .text:0043279B add esp, 0Ch
注册码分为4小段,其实算法基本上都一样,只是取的区间不同,注册码的第4段
.text:0042C3E0 RegistrationCode_4 proc near ; CODE XREF: sub_4277C0+B1↑p .text:0042C3E0 .text:0042C3E0 push ebp .text:0042C3E1 mov ebp, esp .text:0042C3E3 sub esp, 8 .text:0042C3E6 mov [ebp+var_8], ecx .text:0042C3E9 cmp [ebp+arg_4], 0 .text:0042C3ED jge short loc_42C3F6 .text:0042C3EF mov [ebp+arg_4], 0 .text:0042C3F6 .text:0042C3F6 loc_42C3F6: ; CODE XREF: RegistrationCode_4+D↑j .text:0042C3F6 mov ecx, [ebp+var_8] .text:0042C3F9 call sub_4017C0 ; 获取MD5字串长度 .text:0042C3FE mov [ebp+var_4], eax .text:0042C401 mov eax, [ebp+arg_4] .text:0042C404 cmp eax, [ebp+var_4] .text:0042C407 jl short loc_42C41A .text:0042C409 mov ecx, [ebp+var_8] .text:0042C40C push ecx .text:0042C40D mov ecx, [ebp+arg_0] .text:0042C410 call sub_4069F0 .text:0042C415 mov eax, [ebp+arg_0] .text:0042C418 jmp short loc_42C448 .text:0042C41A ; --------------------------------------------------------------------------- .text:0042C41A .text:0042C41A loc_42C41A: ; CODE XREF: RegistrationCode_4+27↑j .text:0042C41A mov ecx, [ebp+var_8] .text:0042C41D call sub_408D50 .text:0042C422 push eax ; int .text:0042C423 mov edx, [ebp+arg_4] .text:0042C426 push edx ; int .text:0042C427 mov ecx, [ebp+var_8] .text:0042C42A call GetString ; 获取字符串的首地址 .text:0042C42F mov ecx, [ebp+var_4] ; 其实关键的就这几行代码 .text:0042C432 lea edx, [eax+ecx*2] ; edx指针:移动到字符串的尾部 .text:0042C435 mov eax, [ebp+arg_4] .text:0042C438 shl eax, 1 .text:0042C43A sub edx, eax .text:0042C43C push edx ; 截取的注册码的一部分 .text:0042C43D mov ecx, [ebp+arg_0] .text:0042C440 call memcpy ; 内联的memcpy,这东西坑了我很久 .text:0042C445 mov eax, [ebp+arg_0] .text:0042C448 .text:0042C448 loc_42C448: ; CODE XREF: RegistrationCode_4+38↑j .text:0042C448 mov esp, ebp .text:0042C44A pop ebp .text:0042C44B retn 8 .text:0042C44B RegistrationCode_4 endp
注册码的第2和3段,其他几段大同小异
三. 网络验证去除
网络验证部分的代码:
.text:00468039 lea edx, [ebp+var_4] .text:0046803C push edx ; 邮箱 .text:0046803D call sub_4069F0 .text:00468042 mov ecx, offset unk_4CB420 .text:00468047 call sub_46CC70 ; 网络验证:验证注册邮箱是否合法 .text:0046804C mov [ebp+var_C], eax .text:0046804F cmp [ebp+var_C], 1 .text:00468053 jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!” .text:00468055 push offset word_4B2D6C .text:0046805A call sub_433800 .text:0046805F add esp, 4 .text:00468062 push 40h .text:00468064 mov eax, [ebp+var_3B0] .text:0046806A mov ecx, [eax+4] .text:0046806D push ecx .text:0046806E push offset aVipnotfoundema .text:00468073 call sub_409A20 ; 弹出“邮箱验证失败,无法注册软件”
网络验证函数sub_46CC70()返回1表示验证失败,打补丁修改返回值即可去除网络验证
四. 机器码获取分析
编写注册机,其实没必要分析机器码的生成,由于我不了解一般软件如何获取机器码的,我就慢慢的从头到尾跟一遍,这一跟才发现机器码的生成比注册码的生成麻烦多了,不过也学到了不少东西,如何获取硬件信息,如何获取物理内存信息等等
1. 流程分析
打开软件,点击:注册–>购买序列号,在下图弹窗找到本机的机器码“89354AF54032753D”
问题:这个机器码是在那里生成的?
最挫的方法:一步一步跟,注册码是根据机器码算出来的,所以检验注册码的上面一定会有注册码
找到出现机器码的那段代码,一步一步的回溯,代码如下:
.text:00467EA0 push ebp .text:00467EA1 mov ebp, esp .text:00467EA3 sub esp, 3B0h .text:00467EA9 mov eax, ___security_cookie .text:00467EAE xor eax, ebp .text:00467EB0 mov [ebp+var_20], eax .text:00467EB3 mov [ebp+var_3B0], ecx .text:00467EB9 mov [ebp+var_14], 0 .text:00467EC0 lea eax, [ebp+var_10] .text:00467EC3 push eax .text:00467EC4 mov ecx, offset unk_4CB400 .text:00467EC9 call GetMachineID .text:00467ECE lea ecx, [ebp+var_10] .text:00467ED1 call sub_4070F0 .text:00467ED6 movzx ecx, al .text:00467ED9 test ecx, ecx .text:00467EDB jz short loc_467F03 .text:00467EDD push 40h ; int .text:00467EDF mov edx, [ebp+var_3B0] .text:00467EE5 mov eax, [edx+4] .text:00467EE8 push eax ; int .text:00467EE9 push offset aNomachineid_0 ; "NoMachineId" .text:00467EEE call sub_409A20 .text:00467EF3 add esp, 0Ch .text:00467EF6 lea ecx, [ebp+var_10] .text:00467EF9 call sub_4013B0 .text:00467EFE jmp loc_4683F1 .text:00467F03 ; --------------------------------------------------------------------------- .text:00467F03 .text:00467F03 loc_467F03: ; CODE XREF: sub_467EA0+3B↑j .text:00467F03 lea ecx, [ebp+var_148] .text:00467F09 push ecx .text:00467F0A mov ecx, [ebp+var_3B0] .text:00467F10 call sub_467D30 .text:00467F15 push ecx .text:00467F16 mov ecx, esp .text:00467F18 lea edx, [ebp+var_10] .text:00467F1B push edx ; 出现机器码 .text:00467F1C call sub_4069F0
已知的出现机器码的是.text:00467F1B push edx,其中edx存放机器码的地址一层一层的往上回溯
00467F1B:edx //edx存放机器码地址 00467F18:var_10 //var_10存储机器码 00467EC9:GetMachineID //函数中进行一系列处理得到机器码 00467EC4:offset unk_4CB400 //这是个全局对象,第一个成员是一个堆地址,其中存储机器码
到这里定位到了全局对象,但是全局对象是在那里获取机器码的,只能下内存断点了。对全局对象的第一个成员的位置下硬件写入断点,注意是004CB404而不是004CB400,因为第一个位置是虚表指针,后推一个才是第一个数据成员
重新载入程序,会断下来好几次,断下来之后再对堆地址指向的内容下硬件写入断点,其中有一次来到下面这个位置,此时恰巧刚填写机器码的前两位“89”
一次一次的retn之后来到下面这段代码中00469527地址处
.text:004694D6 call _memset .text:004694DB add esp, 0Ch .text:004694DE lea edx, [ebp+pcbData] .text:004694E4 push edx .text:004694E5 lea eax, [ebp+pvData] .text:004694EB push eax .text:004694EC push 0 .text:004694EE push offset aPdfcword_2 ; Pdfcword .text:004694F3 push offset aSoftwareMicros_16 ; .text:004694F8 push 80000002h ; hkey .text:004694FD call ds:SHGetValueW ; 该函数获取注册表的键值 .text:00469503 mov [ebp+var_210], eax .text:00469509 cmp [ebp+var_210], 0 .text:00469510 jnz short loc_469527 .text:00469512 lea ecx, [ebp+pvData] .text:00469518 push ecx .text:00469519 mov ecx, [ebp+var_214] .text:0046951F add ecx, 4 .text:00469522 call sub_4013D0 .text:00469527 .text:00469527 loc_469527: ; CODE XREF: GetMachineID+2C↑j .text:00469527 ; GetMachineID+80↑j .text:00469527 mov edx, [ebp+var_214]
遇到SHGetValueW就说明了机器码被写到注册表了。可以通过参数定位到注册表路径
路径:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Pdfcword
此处只是读取注册表中的机器码,并不是直接计算出机器码,我们先把注册表项给删掉,重新载入程序,对SHSetValueA下断
注意:此处不能对RegSetValue(A)、RegSetValueEx(A)下断,不然会很痛苦的,由于自个的无知被这里也折磨了好久
软件跑起来后,点击“注册–>购买序列号”就会断下来,查看堆栈窗口此时正准备向注册表写入机器码,那么写入之前一定有地方获取机器码
返回到0x0043A08,并在段首下断点
下面函数生成字符串: “27fd2a8ad66d99c944a52b0a2f9b6ff1”
1 004328B3 . 6A 40 push 0x40 2 004328B5 . 8D95 68FDFFFF lea edx,dword ptr ss:[ebp-0x298] 3 004328BB . 52 push edx 4 004328BC . E8 9F5D0300 call PDFConve.00468660 ;计算出字符串
下面函数生成字符串: “e3a725d4be6392fae82a71339d6e381b”
004329B2 > \6A 40 push 0x40 004329B4 . 8D95 20FDFFFF lea edx,dword ptr ss:[ebp-0x2E0] 004329BA . 52 push edx 004329BB . 8B85 14FDFFFF mov eax,dword ptr ss:[ebp-0x2EC] 004329C1 . 50 push eax 004329C2 . E8 A9EBFFFF call <PDFConve.Get_MD5>
最后截取并转换为大写就是机器码:”E3A725D4BE6392FA”
机器码生成的大致流程:
2. 取硬盘信息
定位到:004686AE call sub_40C8B0 ;取硬盘序列号
继续向里面跟进来到:0040C8DB call sub_40BCE0
IDA中F5的代码如下所示:
用CreateFile函数打开\.\PhysicalDrive%d
然后用DeviceIoControl来获取硬盘的信息(扇区数,磁头数,柱面数)等
3. 取物理内存信息
定位到:004686C3 call sub_40D160 ;取物理内存信息
限于篇幅原因,函数就不跟进去了
1 .text:0040D1D1 call sub_40CFA0 ; 文件映射,拷贝数据 2 .text:0040D1D6 add esp, 0Ch 3 .text:0040D1D9 test eax, eax 4 .text:0040D1DB jnz short loc_40D1EC 5 .text:0040D1DD call sub_40CF70 ; 关闭内核对象,释放空间 6 .text:0040D1E2 mov eax, offset byte_4C9B40 7 .text:0040D1E7 jmp loc_40D2E9 8 .text:0040D1EC ; --------------------------------------------------------------------------- 9 .text:0040D1EC 10 .text:0040D1EC loc_40D1EC: 11 .text:0040D1EC jmp short loc_40D207 12 .text:0040D1EE ; --------------------------------------------------------------------------- 13 .text:0040D1EE 14 .text:0040D1EE loc_40D1EE: 15 .text:0040D1EE push 1000h ; size_t 16 .text:0040D1F3 push 0FE000h ; void * 17 .text:0040D1F8 lea eax, [ebp+var_1008] 18 .text:0040D1FE push eax ; void * 19 .text:0040D1FF call _memcpy 20 .text:0040D204 add esp, 0Ch 21 .text:0040D207 22 .text:0040D207 loc_40D207: 23 .text:0040D207 call sub_40D030 ; 填表函数 24 .text:0040D20C lea ecx, [ebp+var_1008] 25 .text:0040D212 mov [ebp+var_100C], ecx 26 .text:0040D218 mov [ebp+var_1010], 0 27 .text:0040D222 jmp short loc_40D233 28 .text:0040D224 ; --------------------------------------------------------------------------- 29 .text:0040D224 30 .text:0040D224 loc_40D224: 31 .text:0040D224 mov edx, [ebp+var_1010] 32 .text:0040D22A add edx, 1 33 .text:0040D22D mov [ebp+var_1010], edx 34 .text:0040D233 35 .text:0040D233 loc_40D233: 36 .text:0040D233 cmp [ebp+var_1010], 2 37 .text:0040D23A jge loc_40D2DF 38 .text:0040D240 mov [ebp+var_1030], 0 39 .text:0040D247 xor eax, eax 40 .text:0040D249 mov [ebp+var_102F], eax 41 .text:0040D24F mov [ebp+var_102B], eax 42 .text:0040D255 mov [ebp+var_1027], eax 43 .text:0040D25B mov [ebp+var_1023], eax 44 .text:0040D261 mov [ebp+var_101F], eax 45 .text:0040D267 mov [ebp+var_101B], eax 46 .text:0040D26D mov [ebp+var_1017], eax 47 .text:0040D273 mov [ebp+var_1013], ax 48 .text:0040D27A mov [ebp+var_1011], al 49 .text:0040D280 push 800h 50 .text:0040D285 mov ecx, [ebp+var_100C] 51 .text:0040D28B push ecx 52 .text:0040D28C call sub_40D0B0 ; 关键函数:通过物理内存的信息计算出一串字符串 53 .text:0040D291 add esp, 8 54 .text:0040D294 mov [ebp+var_4], eax 55 .text:0040D297 mov edx, [ebp+var_4] 56 .text:0040D29A push edx 57 .text:0040D29B push offset a04x ; "%04X" 58 .text:0040D2A0 lea eax, [ebp+var_1030] 59 .text:0040D2A6 push eax ; char * 60 .text:0040D2A7 call _sprintf 61 .text:0040D2AC add esp, 0Ch 62 .text:0040D2AF push 1000h ; size_t 63 .text:0040D2B4 lea ecx, [ebp+var_1030] 64 .text:0040D2BA push ecx ; char * 65 .text:0040D2BB push offset byte_4C9B40 ; char * 66 .text:0040D2C0 call _strncat ;拼接字符串 67 .text:0040D2C5 add esp, 0Ch 68 .text:0040D2C8 mov edx, [ebp+var_100C] 69 .text:0040D2CE add edx, 400h 70 .text:0040D2D4 mov [ebp+var_100C], edx 71 .text:0040D2DA jmp loc_40D224
五. 注册机编写
不能算是注册机,因为还有网络验证,虽然本地的注册算法验证可以通过,但是绕不过去网络验证,对于网络验证只能打补丁
1. 取硬盘信息
取硬盘信息部分:
1 void ChangeByteOrder(PCHAR szString, USHORT uscStrSize) 2 { 3 USHORT i = 0; 4 CHAR temp = '\0'; 5 for (i = 0; i < uscStrSize; i += 2) 6 { 7 temp = szString[i]; 8 szString[i] = szString[i + 1]; 9 szString[i + 1] = temp; 10 } 11 } 12 //-------------------------------------------------------------- 13 //功能:硬盘序列号 14 //参数: 15 // lpszHD:传出参数,存储最终计算的硬盘信息 16 // len:默认参128 17 //-------------------------------------------------------------- 18 BOOL GetHDSerial(char *lpszHD, int len/*=128*/) 19 { 20 BOOL bRet = FALSE; 21 DWORD bytesRtn = 0; 22 char szhd[80] = { 0 }; 23 PIDSECTOR phdinfo; 24 HANDLE hDrive = NULL; 25 GETVERSIONOUTPARAMS vers; 26 SENDCMDINPARAMS in; 27 SENDCMDOUTPARAMS out; 28 ZeroMemory(&vers, sizeof(vers)); 29 ZeroMemory(&in, sizeof(in)); 30 ZeroMemory(&out, sizeof(out)); 31 //搜索四个物理硬盘,取第一个有数据的物理硬盘 32 for (int j = 0; j < 4; j++) 33 { 34 sprintf(szhd, "\\\\.\\PhysicalDrive%d", j); 35 hDrive = CreateFileA(szhd, 36 GENERIC_READ | GENERIC_WRITE, 37 FILE_SHARE_READ | FILE_SHARE_WRITE, 38 0, 39 OPEN_EXISTING, 40 0, 41 0); 42 if (NULL == hDrive) 43 { 44 continue; 45 } 46 if (!DeviceIoControl(hDrive, DFP_GET_VERSION, 0, 0, &vers, sizeof(vers), &bytesRtn, 0)) 47 { 48 CloseHandle(hDrive); 49 hDrive = NULL; 50 continue; 51 } 52 //If IDE identify command not supported, fails 53 if (!(vers.fCapabilities & 1)) 54 { 55 CloseHandle(hDrive); 56 hDrive = NULL; 57 continue; 58 } 59 //Identify the IDE drives 60 if (j & 1) 61 { 62 in.irDriveRegs.bDriveHeadReg = 0xb0; 63 } 64 else 65 { 66 in.irDriveRegs.bDriveHeadReg = 0xa0; 67 } 68 if (vers.fCapabilities&(16 >> j)) 69 { 70 //We don't detect a ATAPI device. 71 CloseHandle(hDrive); 72 hDrive = NULL; 73 continue; 74 } 75 else 76 { 77 in.irDriveRegs.bCommandReg = 0xec; 78 } 79 in.bDriveNumber = j; 80 in.irDriveRegs.bSectorCountReg = 1; 81 in.irDriveRegs.bSectorNumberReg = 1; 82 in.cBufferSize = 512; 83 if (!DeviceIoControl(hDrive, DFP_RECEIVE_DRIVE_DATA, &in, sizeof(in), &out, sizeof(out), &bytesRtn, 0)) 84 { 85 //"DeviceIoControl failed:DFP_RECEIVE_DRIVE_DATA"<<endl; 86 CloseHandle(hDrive); 87 hDrive = NULL; 88 continue; 89 } 90 phdinfo = (PIDSECTOR)out.bBuffer; 91 char s[21] = { 0 }; 92 memcpy(s, phdinfo->sSerialNumber, 20); 93 s[20] = 0; 94 ChangeByteOrder(s, 20); 95 memcpy(lpszHD, s, 20); 96 if (strlen(lpszHD) != 0) 97 { 98 bRet = TRUE; 99 } 100 break; 101 } 102 CloseHandle(hDrive); 103 hDrive = NULL; 104 return bRet; 105 }
2. 取物理内存信息
获取物理内存信息:
1 int g_nTable[256] = { 0 }; //全局数组,作为表使用 2 PFNZwOpenSection ZwOpenSection = NULL; 3 PFNZwMapViewOfSection ZwMapViewOfSection = NULL; 4 PFNZwUnmapViewOfSection ZwUnmapViewOfSection = NULL; 5 PFNRtlInitUnicodeString RtlInitUnicodeString = NULL; 6 HMODULE hLibModule = NULL; 7 HANDLE hPhysicalMemoryHandle = NULL; 8 //从NTDLL获取我们需要的几个函数指针,并调用ZwOpenSection 9 BOOL sub_40CE30() 10 { 11 UNICODE_STRING PhysicalMemoryUnicodeString; 12 OBJECT_ATTRIBUTES ObjectAttributes; 13 wchar_t szBuffer[100] = L"\\Device\\PhysicalMemory"; 14 //获取函数指针 15 hLibModule = LoadLibraryA("ntdll.dll"); 16 if (!hLibModule) 17 return FALSE; 18 ZwOpenSection = (PFNZwOpenSection)GetProcAddress(hLibModule, "ZwOpenSection"); 19 if (!ZwOpenSection) 20 return FALSE; 21 ZwMapViewOfSection = (PFNZwMapViewOfSection)GetProcAddress(hLibModule, "ZwMapViewOfSection"); 22 if (!ZwMapViewOfSection) 23 return FALSE; 24 ZwUnmapViewOfSection = (PFNZwUnmapViewOfSection)GetProcAddress(hLibModule, "ZwUnmapViewOfSection"); 25 if (!ZwUnmapViewOfSection) 26 return FALSE; 27 RtlInitUnicodeString = (PFNRtlInitUnicodeString)GetProcAddress(hLibModule, "RtlInitUnicodeString"); 28 if (!RtlInitUnicodeString) 29 return FALSE; 30 RtlInitUnicodeString(&PhysicalMemoryUnicodeString, szBuffer); 31 ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); 32 ObjectAttributes.RootDirectory = 0; 33 ObjectAttributes.ObjectName = &PhysicalMemoryUnicodeString; 34 ObjectAttributes.Attributes = 0; 35 ObjectAttributes.SecurityDescriptor = NULL; 36 ObjectAttributes.SecurityQualityOfService = NULL; 37 NTSTATUS status = ZwOpenSection(&hPhysicalMemoryHandle, 38 4, 39 &ObjectAttributes); 40 if (NT_SUCCESS(status)) 41 return TRUE; 42 return FALSE; 43 } 44 //文件映射,拷贝数据 45 BOOL __cdecl sub_40CFA0(PVOID pvDataBuffer, DWORD dwAddress, DWORD dwLength) 46 { 47 PVOID pvVirtualAddress; // 映射的虚地址 48 LARGE_INTEGER base; // 物理内存地址 49 DWORD dwOutLenth = dwLength; 50 base.QuadPart = (ULONGLONG)(dwAddress); 51 pvVirtualAddress = NULL; 52 NTSTATUS status = ZwMapViewOfSection(hPhysicalMemoryHandle, 53 (HANDLE)-1, 54 &pvVirtualAddress, 55 0, 56 dwLength, 57 &base, 58 &dwOutLenth, 59 ViewShare, 60 0, 61 PAGE_READONLY); 62 if (!NT_SUCCESS(status)) 63 return FALSE; 64 //当前进程的虚地址空间中,复制数据到输出缓冲区 65 memcpy(pvDataBuffer, pvVirtualAddress, dwLength); 66 //完成访问,取消地址映射 67 return ZwUnmapViewOfSection((HANDLE)-1, pvVirtualAddress) >= 0; 68 } 69 //关闭内核对象,释放空间 70 BOOL sub_40CF70() 71 { 72 BOOL bRet = FALSE; 73 if (&hPhysicalMemoryHandle) 74 bRet = CloseHandle(&hPhysicalMemoryHandle); 75 if (hLibModule) 76 bRet = FreeLibrary(hLibModule); 77 return bRet; 78 } 79 //填表函数:将数据进行一系列运算,填入全局数组 80 int sub_40D030() 81 { 82 int nResult = 0; 83 signed int j; 84 unsigned int uNum = 0; 85 signed int i; 86 for (i = 0; i < 256; ++i) 87 { 88 uNum = i; 89 for (j = 8; j > 0; --j) 90 { 91 if (uNum & 1) 92 uNum = (uNum >> 1) ^ 0xEDB88320; 93 else 94 uNum >>= 1; 95 } 96 g_nTable[i] = uNum; 97 nResult = i + 1; 98 } 99 return nResult; 100 } 101 //查表计算 102 int __cdecl sub_40D300(BYTE bArg1, int* pArg2) 103 { 104 int nResult = 0; 105 nResult = *pArg2 & 0xFF; 106 *pArg2 = g_nTable[nResult ^ bArg1] ^ ((DWORD)*pArg2 >> 8); 107 return nResult; 108 } 109 int __cdecl sub_40D0B0(char* pStr, int nLen) 110 { 111 BYTE *bPTmp = NULL; 112 int nResult = -1; 113 bPTmp = (BYTE*)pStr; 114 for (int i = 0; i < nLen; ++i) 115 sub_40D300(*bPTmp++, &nResult); 116 return ~nResult; 117 } 118 //获取第二部分字符串 119 //地址:0040D28C call sub_40D0B0 120 //说明:参数是我自己后来添加的 121 BOOL GetBiosSerial(char* pBios) 122 { 123 char szBuf[0x1000] = { 0 }; 124 char szTmp[16] = { 0 }; 125 char *pTmp = NULL; 126 //获取文件映射的函数指针 127 BOOL bRet = sub_40CE30(); 128 if (bRet) 129 { 130 //创建文件映射,拷贝数据 131 bRet = sub_40CFA0(szBuf, 0x0FE000, 0x1000); 132 if (!bRet) 133 { 134 //关闭内核对象,释放资源 135 sub_40CF70(); 136 return FALSE; 137 } 138 //填表函数 139 sub_40D030(); 140 //计算最终的字符串 141 pTmp = szBuf; 142 for (int i = 0; i < 2; ++i) 143 { 144 char szTmp[32] = { 0 }; 145 int nRet = sub_40D0B0(pTmp, 2048); 146 sprintf(szTmp, "%04X", nRet); 147 strcat(pBios, szTmp); 148 pTmp += 1024; 149 } 150 bRet = TRUE; 151 } 152 return bRet; 153 }
2. 生成机器码
生成机器码:
1 //获取机器码:计算MD5的函数实现本处省略 2 char* GetMachineID(char* pMac, char* pBios, char* pMachineID) 3 { 4 BYTE uMD5Buf[32] = { 0 }; 5 BYTE uTmpBuf[32] = { 0 }; 6 char* pTmp = NULL; 7 //连接字符串 8 if (pMac == NULL) 9 { 10 if (pBios != NULL) 11 { 12 pTmp = pBios; 13 } 14 } 15 else 16 { 17 if (pBios != NULL) 18 { 19 strcat(pMac, pBios); 20 pTmp = pMac; 21 } 22 pTmp = pMac; 23 } 24 //计算MD5值:这个MD5不能出来空字符串,需要单独提出来处理 25 if (pTmp == NULL) 26 { 27 strcpy((char*)uTmpBuf, "d41d8cd98f00b204e9800998ecf8427e"); 28 } 29 else 30 { 31 MDString(pTmp, uMD5Buf); 32 HexToStr(uTmpBuf, uMD5Buf, 16); //将MD5数据转为16进制字符串 33 strlwr((char*)uTmpBuf); //将字符串转为小写 34 } 35 //尾部追加# 36 strcat((char*)uTmpBuf, "#"); 37 //计算MD5值 38 MDString((char*)uTmpBuf, uMD5Buf); 39 HexToStr(uTmpBuf, uMD5Buf, 16); //将MD5数据转为16进制字符串 40 //截取前半部分字符串 41 uTmpBuf[16] = '\0'; 42 memcpy(pMachineID, uTmpBuf, 16); 43 return pMachineID; 44 }
3.生成注册码
生成注册码:
1 //获取注册码的第一段 2 char* RegistrationCode_1(char* pSrc, char* pDest, int nLen) 3 { 4 memcpy(pDest, pSrc, nLen); 5 return pDest; 6 } 7 //获取注册码的第二/三段 8 char* RegistrationCode_2_3(char* pSrc, char* pDest, int nStart, int nLen) 9 { 10 if (nStart < 0) 11 { 12 nStart = 0; 13 } 14 if (nLen < 0) 15 { 16 nLen = 0; 17 } 18 //.... 19 memcpy(pDest, pSrc + nStart, nLen); 20 return pDest; 21 } 22 //获取注册码的第四段 23 char* RegistrationCode_4(char* pSrc, char* pDest, int nLen) 24 { 25 if (nLen < 0) 26 { 27 nLen = 0; 28 } 29 int nSrcLen = strlen(pSrc); 30 if (nLen < nSrcLen) 31 { 32 pSrc += nSrcLen; 33 pSrc -= nLen; 34 memcpy(pDest, pSrc, nLen); 35 } 36 return pDest; 37 } 38 //功能:根据机器码获取注册码 39 //参数: 40 // pMachineID:传入参数,机器码 41 // pKey:传出参数,存储计算完的注册码 42 char* GetSerialNumber(char* pMachineID, char* pKey) 43 { 44 char szTmpBuf[5] = { 0 }; 45 BYTE uMD5Buf[CODE_LEN] = { 0 }; 46 BYTE uTmpBuf[CODE_LEN] = { 0 }; 47 //计算机器码的MD5值 48 MDString(pMachineID, uMD5Buf); 49 //将MD5数据转为16进制字符串 50 HexToStr(uTmpBuf, uMD5Buf, 16); 51 //将机器码的MD5字符串再次计算MD5 52 MDString((char*)uTmpBuf, uMD5Buf); 53 //将MD5数据转为16进制字符串 54 HexToStr(uTmpBuf, uMD5Buf, 16); 55 //将字符串转为小写 56 strlwr((char*)uTmpBuf); 57 //注册码的第一段 58 RegistrationCode_1((char*)uTmpBuf, szTmpBuf, 4); 59 strcat(pKey, szTmpBuf); 60 //注册码的第二段 61 RegistrationCode_2_3((char*)uTmpBuf, szTmpBuf, 0x0A, 0x4); 62 sprintf(pKey, "%s-%s", pKey, szTmpBuf); 63 //注册码的第三段 64 RegistrationCode_2_3((char*)uTmpBuf, szTmpBuf, 0x16, 0x4); 65 sprintf(pKey, "%s-%s", pKey, szTmpBuf); 66 //注册码的第四段 67 RegistrationCode_4((char*)uTmpBuf, szTmpBuf, 4); 68 sprintf(pKey, "%s-%s", pKey, szTmpBuf); 69 return pKey; 70 }
总结
之前从来没有写过KeyGen,该软件的注册码生成基本上没啥难度,倒是追机器码搞得我头大
说明:
1.KeyGen
在虚拟机中可能无法获取硬盘信息和物理内存信息,我在XP虚拟机下正常获取,Win7下获取不到硬盘信息和物理内存信息(具体细节没研究),不过该软件在Win7虚拟机下也是获取不到硬盘信息和物理内存信息,KeyGen可以正常获取注册码
2.Win10
下需要管理员权限运行才能拿到硬件信息
3.
工程中的KeyGen不能直接实现软件注册(只用于学习,务作它用),因为该软件除了本地验证外还有网络验证(Windows Defender会报毒直接杀掉,介意的务尝试),只要输入注册码出现以下截图就说明本地注册完成,填写邮箱是网络验证的事情
KeyGen代码地址:https://pan.baidu/s/1i5peytJ 密码:ip5q(失效请练习我)
考文档:
获取硬盘序列号参考代码:http://blog.csdn/tody_guo/article/details/26084143
获取物理内存信息参考代码:http://blog.csdn/wangxvfeng101/article/details/7394725
转载于:https://wwwblogs/jieliuhouzi/p/8093294.html
版权声明:本文标题:某pdf转word v6.3.0.2算法分析 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1726378924a1084572.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论