某pdf转word v6.3.0.2算法分析
某pdf转word v6.3.0.2算法分析
【文章标题】某pdf转word v6.3.0.2算法分析
【文章作者】jieliuhouzi
【原版下载】www.pdfcword.cn
【保护方式】序列号
【分析过程】
一. 去掉随机基址
直接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
- .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:00467F21 lea eax, [ebp+var_144]
- .text:00467F27 push eax ; 机器码
- .text:00467F28 call sub_4325C0 ; 计算出注册码
- .text:00467F2D add esp,
- .text:00467F30 lea ecx, [ebp+var_148]
- .text:00467F36 call unknown_libname_1 ; Microsoft VisualC 2-14/net runtime
- .text:00467F3B push eax ; 我们自己输入的假码
- .text:00467F3C lea ecx, [ebp+var_144]
- .text:00467F42 call sub_42C270 ; 注册码比较
- .text:00467F47 test eax, eax
- .text:00467F49 jz short loc_467F87 ; 跳过注册码错误
- .text:00467F4B push 40h
- .text:00467F4D mov ecx, [ebp+var_3B0]
- .text:00467F53 mov edx, [ecx+]
- .text:00467F56 push edx
- .text:00467F57 push offset aSnerror
- .text:00467F5C call sub_409A20 ; 弹窗提示“序列号错误,请重新输入”
- .text:00467F61 add esp, 0Ch
- .text:00467F64 lea ecx, [ebp+var_144]
- .text:00467F6A call sub_4013B0
- .text:00467F6F lea ecx, [ebp+var_148]
- .text:00467F75 call sub_4013B0
- .text:00467F7A lea ecx, [ebp+var_10]
- .text:00467F7D call sub_4013B0
- .text:00467F82 jmp loc_4683F1
- .text:00467F87 ; ---------------------------------------------------------------------------
- .text:00467F87
- .text:00467F87 loc_467F87: ; CODE XREF: sub_467EA0+A9↑j
- .text:00467F87 lea eax, [ebp+var_4]
- .text:00467F8A push eax
- .text:00467F8B call sub_433840
- .text:00467F90 add esp,
- .text:00467F93 lea ecx, [ebp+var_4]
- .text:00467F96 call sub_4070F0
- .text:00467F9B movzx ecx, al
- .text:00467F9E test ecx, ecx
- .text:00467FA0 jz loc_46802D
- .text:00467FA6 mov edx, [ebp+var_3B0]
- .text:00467FAC mov eax, [edx+]
- .text:00467FAF push eax
- .text:00467FB0 lea ecx, [ebp+var_3A0]
- .text:00467FB6 push ecx
- .text:00467FB7 call sub_40D670 ; 弹出验证邮箱对话框
- .text:00467FBC add esp,
- .text:00467FBF push eax
- .text:00467FC0 lea ecx, [ebp+var_4]
- .text:00467FC3 call sub_4068A0
- .text:00467FC8 lea ecx, [ebp+var_3A0]
- .text:00467FCE call sub_4013B0
- .text:00467FD3 lea ecx, [ebp+var_4] ; 检查邮箱是否合法
- .text:00467FD6 call sub_4070F0
- .text:00467FDB movzx edx, al
- .text:00467FDE test edx, edx
- .text:00467FE0 jz short loc_468026 ; 跳过“不提供购买邮箱的错误提示”
- .text:00467FE2 push 40h
- .text:00467FE4 mov eax, [ebp+var_3B0]
- .text:00467FEA mov ecx, [eax+]
- .text:00467FED push ecx
- .text:00467FEE push offset aVipnotemail ;
- .text:00467FF3 call sub_409A20
- .text:00467FF8 add esp, 0Ch
- .text:00467FFB lea ecx, [ebp+var_4]
- .text:00467FFE call sub_4013B0
- .text: lea ecx, [ebp+var_144]
- .text: call sub_4013B0
- .text:0046800E lea ecx, [ebp+var_148]
- .text: call sub_4013B0
- .text: lea ecx, [ebp+var_10]
- .text:0046801C call sub_4013B0
- .text: jmp loc_4683F1
- .text: ; ---------------------------------------------------------------------------
- .text:
- .text: loc_468026: ; CODE XREF: sub_467EA0+140↑j
- .text: mov [ebp+var_14],
- .text:0046802D
- .text:0046802D loc_46802D: ; CODE XREF: sub_467EA0+100↑j
- .text:0046802D lea ecx, [ebp+var_1C]
- .text: call ds:??0CWaitCursor@DuiLib@@QAE@XZ
- .text: push ecx
- .text: mov ecx, esp
- .text: lea edx, [ebp+var_4]
- .text:0046803C push edx ; 邮箱
- .text:0046803D call sub_4069F0
- .text: mov ecx, offset unk_4CB420
- .text: call sub_46CC70 ; 网络验证:验证注册邮箱是否合法
- .text:0046804C mov [ebp+var_C], eax
- .text:0046804F cmp [ebp+var_C],
- .text: jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!”
- .text: push offset word_4B2D6C
- .text:0046805A call sub_433800
- .text:0046805F add esp,
- .text: push 40h
- .text: mov eax, [ebp+var_3B0]
- .text:0046806A mov ecx, [eax+]
- .text:0046806D push ecx
- .text:0046806E push offset aVipnotfoundema ; "VIPNotFoundEmail"
- .text: call sub_409A20 ; 弹出“邮箱验证失败,无法注册软件”
- .text: add esp, 0Ch
- .text:0046807B lea ecx, [ebp+var_1C]
- .text:0046807E call ds:??1CWaitCursor@DuiLib@@QAE@XZ
- .text: lea ecx, [ebp+var_4]
- .text: call sub_4013B0
- .text:0046808C lea ecx, [ebp+var_144]
- .text: call sub_4013B0
- .text: lea ecx, [ebp+var_148]
- .text:0046809D call sub_4013B0
- .text:004680A2 lea ecx, [ebp+var_10]
- .text:004680A5 call sub_4013B0
- .text:004680AA jmp loc_4683F1
- .text:004680AF ; ---------------------------------------------------------------------------
- .text:004680AF jmp loc_468140
- .text:004680B4 ; ---------------------------------------------------------------------------
- .text:004680B4
- .text:004680B4 loc_4680B4: ; CODE XREF: sub_467EA0+1B3↑j
- .text:004680B4 cmp [ebp+var_C],
此处重点分析注册码生成部分:
- .text:004325E0 lea ecx, [ebp+arg_4] ; 机器码
- .text:004325E3 call sub_4017A0 ; 将机器码转换为字符串
- .text:004325E8 mov [ebp+lpString], eax
- .text:004325EB cmp [ebp+lpString],
- .text:004325EF jnz short loc_4325FD
- .text:004325F1 mov [ebp+var_C4],
- .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: push eax
- .text: call ds:lstrlenW
- .text: add eax,
- .text:0043260A mov [ebp+var_8], eax
- .text:0043260D cmp [ebp+var_8], 3FFFFFFFh
- .text: jle short loc_432622
- .text: mov [ebp+var_C8],
- .text: jmp short loc_432655
- .text: ; ---------------------------------------------------------------------------
- .text:
- .text: loc_432622: ; CODE XREF: sub_4325C0+54↑j
- .text: mov eax, [ebp+var_8]
- .text: shl eax,
- .text: call __alloca_probe_16 ; alloc申请空间
- .text:0043262C mov [ebp+lpMultiByteStr], esp
- .text: mov ecx, [ebp+CodePage]
- .text: push ecx
- .text: mov edx, [ebp+var_8]
- .text:0043263C shl edx,
- .text:0043263E push edx
- .text:0043263F mov eax, [ebp+lpString]
- .text: push eax ; 机器码UNICODE字符串
- .text: mov ecx, [ebp+lpMultiByteStr]
- .text: push ecx ; 接收转换完的ASCII字符串
- .text:0043264A call sub_408BC0 ; Unicode机器码转Ascii
- .text:0043264F mov [ebp+var_C8], eax
- .text:
- .text: loc_432655: ; CODE XREF: sub_4325C0+60↑j
- .text: mov edx, [ebp+var_C8]
- .text:0043265B mov [ebp+var_C4], edx
- .text:
- .text: loc_432661: ; CODE XREF: sub_4325C0+3B↑j
- .text: mov eax, [ebp+var_C4]
- .text: mov [ebp+var_4], eax
- .text:0043266A push 0FFFFFFFFh
- .text:0043266C lea ecx, [ebp+arg_4] ; 机器码地址
- .text:0043266F call sub_409B30 ; 获取长度
- .text: mov cl, ds:byte_4A5912
- .text:0043267A mov [ebp+var_98], cl
- .text: push 3Fh
- .text: push
- .text: lea edx, [ebp+var_97]
- .text:0043268A push edx
- .text:0043268B call _memset
- .text: add esp, 0Ch
- .text: push 40h
- .text: push
- .text: 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,
- .text:004326CA mov cl, ds:byte_4A5913
- .text:004326D0 mov [ebp+MultiByteStr], cl
- .text:004326D3 push 3Fh
- .text:004326D5 push
- .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
- .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: call Get_MD5 ; 将机器码的MD5字符串再次计算MD5
- .text: add esp, 0Ch
- .text: lea eax, [ebp+MultiByteStr]
- .text:0043270B push eax
- .text:0043270C lea ecx, [ebp+obj_A8]
- .text: call sub_430200 ; 将ASCII转为UNICODE
- .text: push
- .text: lea ecx, [ebp+var_AC]
- .text:0043271F push ecx ; 传出参数:用来存储计算完的注册码的第4段
- .text: lea ecx, [ebp+obj_A8] ; MD5字符串
- .text: call RegistrationCode_4 ; 注册码的第四段
- .text:0043272B push eax
- .text:0043272C push
- .text:0043272E push 16h
- .text: lea edx, [ebp+var_B0]
- .text: push edx
- .text: lea ecx, [ebp+obj_A8]
- .text:0043273D call RegistrationCode_2_3 ; 注册码的第三段
- .text: push eax
- .text: push
- .text: push 0Ah
- .text: lea eax, [ebp+var_B4]
- .text:0043274D push eax
- .text:0043274E lea ecx, [ebp+obj_A8]
- .text: call RegistrationCode_2_3 ; 注册码的第二段
- .text: push eax
- .text:0043275A push
- .text:0043275C lea ecx, [ebp+var_B8]
- .text: push ecx
- .text: lea ecx, [ebp+obj_A8]
- .text: call RegistrationCode_1 ; 注册码的第一段
- .text:0043276E push eax
- .text:0043276F lea edx, [ebp+var_BC]
- .text: push edx
- .text: call MyStrCat ; 类似于strcat功能
- .text:0043277B add esp, 0Ch
- .text:0043277E push eax
- .text:0043277F lea eax, [ebp+var_C0]
- .text: push eax
- .text: call MyStrCat
- .text:0043278B add esp, 0Ch
- .text:0043278E push eax
- .text:0043278F lea ecx, [ebp+var_A4]
- .text: push ecx
- .text: 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,
- .text:0042C3E6 mov [ebp+var_8], ecx
- .text:0042C3E9 cmp [ebp+arg_4],
- .text:0042C3ED jge short loc_42C3F6
- .text:0042C3EF mov [ebp+arg_4],
- .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*] ; edx指针:移动到字符串的尾部
- .text:0042C435 mov eax, [ebp+arg_4]
- .text:0042C438 shl eax,
- .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
- .text:0042C44B RegistrationCode_4 endp
注册码的第2和3段,其他几段大同小异
三. 网络验证去除
网络验证部分的代码:
- .text: lea edx, [ebp+var_4]
- .text:0046803C push edx ; 邮箱
- .text:0046803D call sub_4069F0
- .text: mov ecx, offset unk_4CB420
- .text: call sub_46CC70 ; 网络验证:验证注册邮箱是否合法
- .text:0046804C mov [ebp+var_C], eax
- .text:0046804F cmp [ebp+var_C],
- .text: jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!”
- .text: push offset word_4B2D6C
- .text:0046805A call sub_433800
- .text:0046805F add esp,
- .text: push 40h
- .text: mov eax, [ebp+var_3B0]
- .text:0046806A mov ecx, [eax+]
- .text:0046806D push ecx
- .text:0046806E push offset aVipnotfoundema
- .text: 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],
- .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+]
- .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
- .text:004694EE push offset aPdfcword_2 ; Pdfcword
- .text:004694F3 push offset aSoftwareMicros_16 ;
- .text:004694F8 push 80000002h ; hkey
- .text:004694FD call ds:SHGetValueW ; 该函数获取注册表的键值
- .text: mov [ebp+var_210], eax
- .text: cmp [ebp+var_210],
- .text: jnz short loc_469527
- .text: lea ecx, [ebp+pvData]
- .text: push ecx
- .text: mov ecx, [ebp+var_214]
- .text:0046951F add ecx,
- .text: call sub_4013D0
- .text:
- .text: loc_469527: ; CODE XREF: GetMachineID+2C↑j
- .text: ; GetMachineID+80↑j
- .text: mov edx, [ebp+var_214]
遇到SHGetValueW就说明了机器码被写到注册表了。可以通过参数定位到注册表路径
路径:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Pdfcword
此处只是读取注册表中的机器码,并不是直接计算出机器码,我们先把注册表项给删掉,重新载入程序,对SHSetValueA下断
注意:此处不能对RegSetValue(A)、RegSetValueEx(A)下断,不然会很痛苦的,由于自个的无知被这里也折磨了好久
软件跑起来后,点击“注册–>购买序列号”就会断下来,查看堆栈窗口此时正准备向注册表写入机器码,那么写入之前一定有地方获取机器码
返回到0x0043A08,并在段首下断点
下面函数生成字符串: “27fd2a8ad66d99c944a52b0a2f9b6ff1”
- 004328B3 . 6A push 0x40
- 004328B5 . 8D95 68FDFFFF lea edx,dword ptr ss:[ebp-0x298]
- 004328BB . push edx
- 004328BC . E8 9F5D0300 call PDFConve. ;计算出字符串
下面函数生成字符串: “e3a725d4be6392fae82a71339d6e381b”
- 004329B2 > \6A push 0x40
- 004329B4 . 8D95 20FDFFFF lea edx,dword ptr ss:[ebp-0x2E0]
- 004329BA . push edx
- 004329BB . 8B85 14FDFFFF mov eax,dword ptr ss:[ebp-0x2EC]
- 004329C1 . 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 ;取物理内存信息
限于篇幅原因,函数就不跟进去了
- .text:0040D1D1 call sub_40CFA0 ; 文件映射,拷贝数据
- .text:0040D1D6 add esp, 0Ch
- .text:0040D1D9 test eax, eax
- .text:0040D1DB jnz short loc_40D1EC
- .text:0040D1DD call sub_40CF70 ; 关闭内核对象,释放空间
- .text:0040D1E2 mov eax, offset byte_4C9B40
- .text:0040D1E7 jmp loc_40D2E9
- .text:0040D1EC ; ---------------------------------------------------------------------------
- .text:0040D1EC
- .text:0040D1EC loc_40D1EC:
- .text:0040D1EC jmp short loc_40D207
- .text:0040D1EE ; ---------------------------------------------------------------------------
- .text:0040D1EE
- .text:0040D1EE loc_40D1EE:
- .text:0040D1EE push 1000h ; size_t
- .text:0040D1F3 push 0FE000h ; void *
- .text:0040D1F8 lea eax, [ebp+var_1008]
- .text:0040D1FE push eax ; void *
- .text:0040D1FF call _memcpy
- .text:0040D204 add esp, 0Ch
- .text:0040D207
- .text:0040D207 loc_40D207:
- .text:0040D207 call sub_40D030 ; 填表函数
- .text:0040D20C lea ecx, [ebp+var_1008]
- .text:0040D212 mov [ebp+var_100C], ecx
- .text:0040D218 mov [ebp+var_1010],
- .text:0040D222 jmp short loc_40D233
- .text:0040D224 ; ---------------------------------------------------------------------------
- .text:0040D224
- .text:0040D224 loc_40D224:
- .text:0040D224 mov edx, [ebp+var_1010]
- .text:0040D22A add edx,
- .text:0040D22D mov [ebp+var_1010], edx
- .text:0040D233
- .text:0040D233 loc_40D233:
- .text:0040D233 cmp [ebp+var_1010],
- .text:0040D23A jge loc_40D2DF
- .text:0040D240 mov [ebp+var_1030],
- .text:0040D247 xor eax, eax
- .text:0040D249 mov [ebp+var_102F], eax
- .text:0040D24F mov [ebp+var_102B], eax
- .text:0040D255 mov [ebp+var_1027], eax
- .text:0040D25B mov [ebp+var_1023], eax
- .text:0040D261 mov [ebp+var_101F], eax
- .text:0040D267 mov [ebp+var_101B], eax
- .text:0040D26D mov [ebp+var_1017], eax
- .text:0040D273 mov [ebp+var_1013], ax
- .text:0040D27A mov [ebp+var_1011], al
- .text:0040D280 push 800h
- .text:0040D285 mov ecx, [ebp+var_100C]
- .text:0040D28B push ecx
- .text:0040D28C call sub_40D0B0 ; 关键函数:通过物理内存的信息计算出一串字符串
- .text:0040D291 add esp,
- .text:0040D294 mov [ebp+var_4], eax
- .text:0040D297 mov edx, [ebp+var_4]
- .text:0040D29A push edx
- .text:0040D29B push offset a04x ; "%04X"
- .text:0040D2A0 lea eax, [ebp+var_1030]
- .text:0040D2A6 push eax ; char *
- .text:0040D2A7 call _sprintf
- .text:0040D2AC add esp, 0Ch
- .text:0040D2AF push 1000h ; size_t
- .text:0040D2B4 lea ecx, [ebp+var_1030]
- .text:0040D2BA push ecx ; char *
- .text:0040D2BB push offset byte_4C9B40 ; char *
- .text:0040D2C0 call _strncat ;拼接字符串
- .text:0040D2C5 add esp, 0Ch
- .text:0040D2C8 mov edx, [ebp+var_100C]
- .text:0040D2CE add edx, 400h
- .text:0040D2D4 mov [ebp+var_100C], edx
- .text:0040D2DA jmp loc_40D224
五. 注册机编写
不能算是注册机,因为还有网络验证,虽然本地的注册算法验证可以通过,但是绕不过去网络验证,对于网络验证只能打补丁
1. 取硬盘信息
取硬盘信息部分:
- void ChangeByteOrder(PCHAR szString, USHORT uscStrSize)
- {
- USHORT i = ;
- CHAR temp = '\0';
- for (i = ; i < uscStrSize; i += )
- {
- temp = szString[i];
- szString[i] = szString[i + ];
- szString[i + ] = temp;
- }
- }
- //--------------------------------------------------------------
- //功能:硬盘序列号
- //参数:
- // lpszHD:传出参数,存储最终计算的硬盘信息
- // len:默认参128
- //--------------------------------------------------------------
- BOOL GetHDSerial(char *lpszHD, int len/*=128*/)
- {
- BOOL bRet = FALSE;
- DWORD bytesRtn = ;
- char szhd[] = { };
- PIDSECTOR phdinfo;
- HANDLE hDrive = NULL;
- GETVERSIONOUTPARAMS vers;
- SENDCMDINPARAMS in;
- SENDCMDOUTPARAMS out;
- ZeroMemory(&vers, sizeof(vers));
- ZeroMemory(&in, sizeof(in));
- ZeroMemory(&out, sizeof(out));
- //搜索四个物理硬盘,取第一个有数据的物理硬盘
- for (int j = ; j < ; j++)
- {
- sprintf(szhd, "\\\\.\\PhysicalDrive%d", j);
- hDrive = CreateFileA(szhd,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- ,
- OPEN_EXISTING,
- ,
- );
- if (NULL == hDrive)
- {
- continue;
- }
- if (!DeviceIoControl(hDrive, DFP_GET_VERSION, , , &vers, sizeof(vers), &bytesRtn, ))
- {
- CloseHandle(hDrive);
- hDrive = NULL;
- continue;
- }
- //If IDE identify command not supported, fails
- if (!(vers.fCapabilities & ))
- {
- CloseHandle(hDrive);
- hDrive = NULL;
- continue;
- }
- //Identify the IDE drives
- if (j & )
- {
- in.irDriveRegs.bDriveHeadReg = 0xb0;
- }
- else
- {
- in.irDriveRegs.bDriveHeadReg = 0xa0;
- }
- if (vers.fCapabilities&( >> j))
- {
- //We don't detect a ATAPI device.
- CloseHandle(hDrive);
- hDrive = NULL;
- continue;
- }
- else
- {
- in.irDriveRegs.bCommandReg = 0xec;
- }
- in.bDriveNumber = j;
- in.irDriveRegs.bSectorCountReg = ;
- in.irDriveRegs.bSectorNumberReg = ;
- in.cBufferSize = ;
- if (!DeviceIoControl(hDrive, DFP_RECEIVE_DRIVE_DATA, &in, sizeof(in), &out, sizeof(out), &bytesRtn, ))
- {
- //"DeviceIoControl failed:DFP_RECEIVE_DRIVE_DATA"<<endl;
- CloseHandle(hDrive);
- hDrive = NULL;
- continue;
- }
- phdinfo = (PIDSECTOR)out.bBuffer;
- char s[] = { };
- memcpy(s, phdinfo->sSerialNumber, );
- s[] = ;
- ChangeByteOrder(s, );
- memcpy(lpszHD, s, );
- if (strlen(lpszHD) != )
- {
- bRet = TRUE;
- }
- break;
- }
- CloseHandle(hDrive);
- hDrive = NULL;
- return bRet;
- }
2. 取物理内存信息
获取物理内存信息:
- int g_nTable[] = { }; //全局数组,作为表使用
- PFNZwOpenSection ZwOpenSection = NULL;
- PFNZwMapViewOfSection ZwMapViewOfSection = NULL;
- PFNZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
- PFNRtlInitUnicodeString RtlInitUnicodeString = NULL;
- HMODULE hLibModule = NULL;
- HANDLE hPhysicalMemoryHandle = NULL;
- //从NTDLL获取我们需要的几个函数指针,并调用ZwOpenSection
- BOOL sub_40CE30()
- {
- UNICODE_STRING PhysicalMemoryUnicodeString;
- OBJECT_ATTRIBUTES ObjectAttributes;
- wchar_t szBuffer[] = L"\\Device\\PhysicalMemory";
- //获取函数指针
- hLibModule = LoadLibraryA("ntdll.dll");
- if (!hLibModule)
- return FALSE;
- ZwOpenSection = (PFNZwOpenSection)GetProcAddress(hLibModule, "ZwOpenSection");
- if (!ZwOpenSection)
- return FALSE;
- ZwMapViewOfSection = (PFNZwMapViewOfSection)GetProcAddress(hLibModule, "ZwMapViewOfSection");
- if (!ZwMapViewOfSection)
- return FALSE;
- ZwUnmapViewOfSection = (PFNZwUnmapViewOfSection)GetProcAddress(hLibModule, "ZwUnmapViewOfSection");
- if (!ZwUnmapViewOfSection)
- return FALSE;
- RtlInitUnicodeString = (PFNRtlInitUnicodeString)GetProcAddress(hLibModule, "RtlInitUnicodeString");
- if (!RtlInitUnicodeString)
- return FALSE;
- RtlInitUnicodeString(&PhysicalMemoryUnicodeString, szBuffer);
- ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
- ObjectAttributes.RootDirectory = ;
- ObjectAttributes.ObjectName = &PhysicalMemoryUnicodeString;
- ObjectAttributes.Attributes = ;
- ObjectAttributes.SecurityDescriptor = NULL;
- ObjectAttributes.SecurityQualityOfService = NULL;
- NTSTATUS status = ZwOpenSection(&hPhysicalMemoryHandle,
- ,
- &ObjectAttributes);
- if (NT_SUCCESS(status))
- return TRUE;
- return FALSE;
- }
- //文件映射,拷贝数据
- BOOL __cdecl sub_40CFA0(PVOID pvDataBuffer, DWORD dwAddress, DWORD dwLength)
- {
- PVOID pvVirtualAddress; // 映射的虚地址
- LARGE_INTEGER base; // 物理内存地址
- DWORD dwOutLenth = dwLength;
- base.QuadPart = (ULONGLONG)(dwAddress);
- pvVirtualAddress = NULL;
- NTSTATUS status = ZwMapViewOfSection(hPhysicalMemoryHandle,
- (HANDLE)-,
- &pvVirtualAddress,
- ,
- dwLength,
- &base,
- &dwOutLenth,
- ViewShare,
- ,
- PAGE_READONLY);
- if (!NT_SUCCESS(status))
- return FALSE;
- //当前进程的虚地址空间中,复制数据到输出缓冲区
- memcpy(pvDataBuffer, pvVirtualAddress, dwLength);
- //完成访问,取消地址映射
- return ZwUnmapViewOfSection((HANDLE)-, pvVirtualAddress) >= ;
- }
- //关闭内核对象,释放空间
- BOOL sub_40CF70()
- {
- BOOL bRet = FALSE;
- if (&hPhysicalMemoryHandle)
- bRet = CloseHandle(&hPhysicalMemoryHandle);
- if (hLibModule)
- bRet = FreeLibrary(hLibModule);
- return bRet;
- }
- //填表函数:将数据进行一系列运算,填入全局数组
- int sub_40D030()
- {
- int nResult = ;
- signed int j;
- unsigned int uNum = ;
- signed int i;
- for (i = ; i < ; ++i)
- {
- uNum = i;
- for (j = ; j > ; --j)
- {
- if (uNum & )
- uNum = (uNum >> ) ^ 0xEDB88320;
- else
- uNum >>= ;
- }
- g_nTable[i] = uNum;
- nResult = i + ;
- }
- return nResult;
- }
- //查表计算
- int __cdecl sub_40D300(BYTE bArg1, int* pArg2)
- {
- int nResult = ;
- nResult = *pArg2 & 0xFF;
- *pArg2 = g_nTable[nResult ^ bArg1] ^ ((DWORD)*pArg2 >> );
- return nResult;
- }
- int __cdecl sub_40D0B0(char* pStr, int nLen)
- {
- BYTE *bPTmp = NULL;
- int nResult = -;
- bPTmp = (BYTE*)pStr;
- for (int i = ; i < nLen; ++i)
- sub_40D300(*bPTmp++, &nResult);
- return ~nResult;
- }
- //获取第二部分字符串
- //地址:0040D28C call sub_40D0B0
- //说明:参数是我自己后来添加的
- BOOL GetBiosSerial(char* pBios)
- {
- char szBuf[0x1000] = { };
- char szTmp[] = { };
- char *pTmp = NULL;
- //获取文件映射的函数指针
- BOOL bRet = sub_40CE30();
- if (bRet)
- {
- //创建文件映射,拷贝数据
- bRet = sub_40CFA0(szBuf, 0x0FE000, 0x1000);
- if (!bRet)
- {
- //关闭内核对象,释放资源
- sub_40CF70();
- return FALSE;
- }
- //填表函数
- sub_40D030();
- //计算最终的字符串
- pTmp = szBuf;
- for (int i = ; i < ; ++i)
- {
- char szTmp[] = { };
- int nRet = sub_40D0B0(pTmp, );
- sprintf(szTmp, "%04X", nRet);
- strcat(pBios, szTmp);
- pTmp += ;
- }
- bRet = TRUE;
- }
- return bRet;
- }
2. 生成机器码
生成机器码:
- //获取机器码:计算MD5的函数实现本处省略
- char* GetMachineID(char* pMac, char* pBios, char* pMachineID)
- {
- BYTE uMD5Buf[] = { };
- BYTE uTmpBuf[] = { };
- char* pTmp = NULL;
- //连接字符串
- if (pMac == NULL)
- {
- if (pBios != NULL)
- {
- pTmp = pBios;
- }
- }
- else
- {
- if (pBios != NULL)
- {
- strcat(pMac, pBios);
- pTmp = pMac;
- }
- pTmp = pMac;
- }
- //计算MD5值:这个MD5不能出来空字符串,需要单独提出来处理
- if (pTmp == NULL)
- {
- strcpy((char*)uTmpBuf, "d41d8cd98f00b204e9800998ecf8427e");
- }
- else
- {
- MDString(pTmp, uMD5Buf);
- HexToStr(uTmpBuf, uMD5Buf, ); //将MD5数据转为16进制字符串
- strlwr((char*)uTmpBuf); //将字符串转为小写
- }
- //尾部追加#
- strcat((char*)uTmpBuf, "#");
- //计算MD5值
- MDString((char*)uTmpBuf, uMD5Buf);
- HexToStr(uTmpBuf, uMD5Buf, ); //将MD5数据转为16进制字符串
- //截取前半部分字符串
- uTmpBuf[] = '\0';
- memcpy(pMachineID, uTmpBuf, );
- return pMachineID;
- }
3.生成注册码
生成注册码:
- //获取注册码的第一段
- char* RegistrationCode_1(char* pSrc, char* pDest, int nLen)
- {
- memcpy(pDest, pSrc, nLen);
- return pDest;
- }
- //获取注册码的第二/三段
- char* RegistrationCode_2_3(char* pSrc, char* pDest, int nStart, int nLen)
- {
- if (nStart < )
- {
- nStart = ;
- }
- if (nLen < )
- {
- nLen = ;
- }
- //....
- memcpy(pDest, pSrc + nStart, nLen);
- return pDest;
- }
- //获取注册码的第四段
- char* RegistrationCode_4(char* pSrc, char* pDest, int nLen)
- {
- if (nLen < )
- {
- nLen = ;
- }
- int nSrcLen = strlen(pSrc);
- if (nLen < nSrcLen)
- {
- pSrc += nSrcLen;
- pSrc -= nLen;
- memcpy(pDest, pSrc, nLen);
- }
- return pDest;
- }
- //功能:根据机器码获取注册码
- //参数:
- // pMachineID:传入参数,机器码
- // pKey:传出参数,存储计算完的注册码
- char* GetSerialNumber(char* pMachineID, char* pKey)
- {
- char szTmpBuf[] = { };
- BYTE uMD5Buf[CODE_LEN] = { };
- BYTE uTmpBuf[CODE_LEN] = { };
- //计算机器码的MD5值
- MDString(pMachineID, uMD5Buf);
- //将MD5数据转为16进制字符串
- HexToStr(uTmpBuf, uMD5Buf, );
- //将机器码的MD5字符串再次计算MD5
- MDString((char*)uTmpBuf, uMD5Buf);
- //将MD5数据转为16进制字符串
- HexToStr(uTmpBuf, uMD5Buf, );
- //将字符串转为小写
- strlwr((char*)uTmpBuf);
- //注册码的第一段
- RegistrationCode_1((char*)uTmpBuf, szTmpBuf, );
- strcat(pKey, szTmpBuf);
- //注册码的第二段
- RegistrationCode_2_3((char*)uTmpBuf, szTmpBuf, 0x0A, 0x4);
- sprintf(pKey, "%s-%s", pKey, szTmpBuf);
- //注册码的第三段
- RegistrationCode_2_3((char*)uTmpBuf, szTmpBuf, 0x16, 0x4);
- sprintf(pKey, "%s-%s", pKey, szTmpBuf);
- //注册码的第四段
- RegistrationCode_4((char*)uTmpBuf, szTmpBuf, );
- sprintf(pKey, "%s-%s", pKey, szTmpBuf);
- return pKey;
- }
总结
之前从来没有写过KeyGen,该软件的注册码生成基本上没啥难度,倒是追机器码搞得我头大
说明:
1.KeyGen
在虚拟机中可能无法获取硬盘信息和物理内存信息,我在XP虚拟机下正常获取,Win7下获取不到硬盘信息和物理内存信息(具体细节没研究),不过该软件在Win7虚拟机下也是获取不到硬盘信息和物理内存信息,KeyGen可以正常获取注册码
2.Win10
下需要管理员权限运行才能拿到硬件信息
3.
工程中的KeyGen不能直接实现软件注册(只用于学习,务作它用),因为该软件除了本地验证外还有网络验证(Windows Defender会报毒直接杀掉,介意的务尝试),只要输入注册码出现以下截图就说明本地注册完成,填写邮箱是网络验证的事情

考文档:
获取硬盘序列号参考代码:http://blog.csdn.net/tody_guo/article/details/26084143
获取物理内存信息参考代码:http://blog.csdn.net/wangxvfeng101/article/details/7394725
某pdf转word v6.3.0.2算法分析的更多相关文章
- iText导出pdf、word、图片
一.前言 在企业的信息系统中,报表处理一直占比较重要的作用,本文将介绍一种生成PDF报表的Java组件--iText.通过在服务器端使用Jsp或JavaBean生成PDF报表,客户端采用超级连接显示或 ...
- .net mvc使用FlexPaper插件实现在线预览PDF,EXCEL,WORD的方法
FlexPaper插件可以实现在浏览器中在线预览pdf,word,excel等. 在网上看到很多关于这个插件实现预览的技术,但是很难做到word和excel在线预览. pdf很好实现. 首先下载相关的 ...
- Python处理PDF和Word文档常用的方法
Python处理PDF和Word文档的模块是PyPDF2,使用之前需要先导入. 打开一个PDF文档的操作顺序是:用open()函数打开文件并用一个变量来接收,然后把变量给传递给PdfFileReade ...
- Java 将PDF 转为Word、图片、SVG、XPS、Html、PDF/A
本文将介绍通过Java编程来实现PDF文档转换的方法.包括: 1. PDF转为Word 2. PDF转为图片 3. PDF转为Html 4. PDF转为SVG 4.1 将PDF每一页转为单个的SVG ...
- java PDF转word的初步实现
package com.springboot.springboot.util; import java.io.File; import java.io.FileOutputStream; import ...
- CentOS 6.6 升级GCC G++ (当前最新版本为v6.1.0) (完整)
---恢复内容开始--- CentOS 6.6 升级GCC G++ (当前最新GCC/G++版本为v6.1.0) 没有便捷方式, yum update.... yum install 或者 添加y ...
- 基于DevExpress实现对PDF、Word、Excel文档的预览及操作处理
http://www.cnblogs.com/wuhuacong/p/4175266.html 在一般的管理系统模块里面,越来越多的设计到一些常用文档的上传保存操作,其中如PDF.Word.Excel ...
- 一款免费支持PDF、word、excel、PPT、jpeg之间互转线上软件
偶然发现的一款免费支持PDF.word.excel.PPT.jpeg之间互转,支持合并pdf.加密解密PDF的线上软件,首先声明,不是广告党,我自己试用过,确实是目前我用过最好用的,如果有朋友有更好的 ...
- pdf转word
一.刚需 pdf转word,这个需求肯定是有的.但是大家都知道,pdf是用来排版打印的,所以编辑起来会比较麻烦,所以,大家都会尝试将pdf的内容转成word,然后再进行编辑. 二.方法 1.用offi ...
随机推荐
- 使用Lock锁生产者消费者模式
package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...
- [Bayesian] “我是bayesian我怕谁”系列 - Variational Autoencoders
本是neural network的内容,但偏偏有个variational打头,那就聊聊.涉及的内容可能比较杂,但终归会 end with VAE. 各个概念的详细解释请点击推荐的链接,本文只是重在理清 ...
- 基于QEMU的ARM Cortex-A9开发板Vexpress-ca9的Linux内核的编译和运行
宿主机:Ubuntu16.04 x64(Linux内核4.4.0) 交叉编译工具链:gcc-arm-linux-gnueabiarm-linux-gcc:4.4.3QEMU:2.5.0Linux ke ...
- Vim常用操作-Nginx配置文件批量加注释。
刚接触 Vim 会觉得它的学习曲线非常陡峭,要记住很多命令.所以这个系列的分享,不会教你怎么配置它,而是教你怎么快速的使用它. 本期我们要实现给 Nginx 配置文件批量注释的功能,先来看效果: 操作 ...
- 小程序 wx.getRecorderManager 录音 to 语音识别
微信扫小程序码看调用效果(自然语言理解小助手) 欢迎转载,请保留原文链接:http://www.happycxz.com/m/?p=125 这次主要是把我的api更新了一下,支持微信小程序新的录音接口 ...
- 史上最全的IntelliJIdea快捷键
Ctrl+Shift+方向键Up/Down 代码向上/下移动. Ctrl+X 删除行 Ctrl+Y 也是删除行,不知道有啥区别 Ctrl+D 复制行 Ctrl+Alt+L 格式化代码 Ctrl+N 查 ...
- SUID,SGID,SBIT这些到底是什么
SUID,SGID,SBIT这些都是文件的特殊权限. SUID(Set UID)文件执行过程中,用户拥有文件的root权限. SGID(Set GID)文件执行过程中,执行者拥有该文件的用户组的权限. ...
- C#中抽象类与接口的区别
1.面向接口编程和面向对象编程是什么关系 首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分.或者说,它是面向对象 ...
- Winform界面中主从表编辑界面的快速处理
在Winform开发中,我们往往除了常规的单表信息录入外,有时候设计到多个主从表的数据显示.编辑等界面,单表的信息一般就是控件和对象实体一一对应,然后调用API保存即可,主从表就需要另外特殊处理,本随 ...
- ASP.NET Core 认证与授权[5]:初识授权
经过前面几章的姗姗学步,我们了解了在 ASP.NET Core 中是如何认证的,终于来到了授权阶段.在认证阶段我们通过用户令牌获取到用户的Claims,而授权便是对这些的Claims的验证,如:是否拥 ...