欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]某pdf转word v6.3.0.2算法分析

[原创]某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, 8

.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+4]

.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, 4

.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+4]

.text:00467FAF                 push    eax

.text:00467FB0                 lea     ecx, [ebp+var_3A0]

.text:00467FB6                 push    ecx

.text:00467FB7                 call    sub_40D670      ; 弹出验证邮箱对话框

.text:00467FBC                 add     esp, 8

.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+4]

.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:00468003                 lea     ecx, [ebp+var_144]

.text:00468009                 call    sub_4013B0

.text:0046800E                 lea     ecx, [ebp+var_148]

.text:00468014                 call    sub_4013B0

.text:00468019                 lea     ecx, [ebp+var_10]

.text:0046801C                 call    sub_4013B0

.text:00468021                 jmp     loc_4683F1

.text:00468026 ; ---------------------------------------------------------------------------

.text:00468026

.text:00468026 loc_468026:                             ; CODE XREF: sub_467EA0+140↑j

.text:00468026                 mov     [ebp+var_14], 1

.text:0046802D

.text:0046802D loc_46802D:                             ; CODE XREF: sub_467EA0+100↑j

.text:0046802D                 lea     ecx, [ebp+var_1C]

.text:00468030                 call    ds:??0CWaitCursor@DuiLib@@QAE@XZ

.text:00468036                 push    ecx

.text:00468037                 mov     ecx, esp

.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 ; "VIPNotFoundEmail"

.text:00468073                 call    sub_409A20      ; 弹出“邮箱验证失败,无法注册软件”

.text:00468078                 add     esp, 0Ch

.text:0046807B                 lea     ecx, [ebp+var_1C]

.text:0046807E                 call    ds:??1CWaitCursor@DuiLib@@QAE@XZ

.text:00468084                 lea     ecx, [ebp+var_4]

.text:00468087                 call    sub_4013B0

.text:0046808C                 lea     ecx, [ebp+var_144]

.text:00468092                 call    sub_4013B0

.text:00468097                 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], 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. 流程分析

打开软件,

问题:这个机器码是在那里生成的?
最挫的方法:一步一步跟,注册码是根据机器码算出来的,所以检验注册码的上面一定会有注册码
找到出现机器码的那段代码,一步一步的回溯,代码如下:

.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"

004328B3   .  6A 40         push 0x40

004328B5   .  8D95 68FDFFFF lea edx,dword ptr ss:[ebp-0x298]

004328BB   .  52            push edx

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 ;取物理内存信息
限于篇幅原因,函数就不跟进去了

.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], 0

.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, 1

.text:0040D22D                 mov     [ebp+var_1010], edx

.text:0040D233

.text:0040D233 loc_40D233:

.text:0040D233                 cmp     [ebp+var_1010], 2

.text:0040D23A                 jge     loc_40D2DF

.text:0040D240                 mov     [ebp+var_1030], 0

.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, 8

.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 = 0;

    CHAR    temp = '\0';

    for (i = 0; i < uscStrSize; i += 2)

    {

        temp = szString[i];

        szString[i] = szString[i + 1];

        szString[i + 1] = temp;

    }

}

//--------------------------------------------------------------  

//功能:硬盘序列号

//参数:

//  lpszHD:传出参数,存储最终计算的硬盘信息

//  len:默认参128

//--------------------------------------------------------------  

BOOL GetHDSerial(char *lpszHD, int len/*=128*/)

{

    BOOL        bRet = FALSE;

    DWORD       bytesRtn = 0;

    char        szhd[80] = { 0 };

    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 = 0; j < 4; j++)

    {

        sprintf(szhd, "\\\\.\\PhysicalDrive%d", j);

        hDrive = CreateFileA(szhd,

            GENERIC_READ | GENERIC_WRITE,

            FILE_SHARE_READ | FILE_SHARE_WRITE,

            0,

            OPEN_EXISTING,

            0,

            0);

        if (NULL == hDrive)

        {

            continue;

        }

        if (!DeviceIoControl(hDrive, DFP_GET_VERSION, 0, 0, &vers, sizeof(vers), &bytesRtn, 0))

        {

            CloseHandle(hDrive);

            hDrive = NULL;

            continue;

        }

        //If IDE identify command not supported, fails

        if (!(vers.fCapabilities & 1))

        {

            CloseHandle(hDrive);

            hDrive = NULL;

            continue;

        }

        //Identify the IDE drives

        if (j & 1)

        {

            in.irDriveRegs.bDriveHeadReg = 0xb0;

        }

        else

        {

            in.irDriveRegs.bDriveHeadReg = 0xa0;

        }

        if (vers.fCapabilities&(16 >> j))

        {

            //We don't detect a ATAPI device.

            CloseHandle(hDrive);

            hDrive = NULL;

            continue;

        }

        else

        {

            in.irDriveRegs.bCommandReg = 0xec;

        }

        in.bDriveNumber = j;

        in.irDriveRegs.bSectorCountReg = 1;

        in.irDriveRegs.bSectorNumberReg = 1;

        in.cBufferSize = 512;

        if (!DeviceIoControl(hDrive, DFP_RECEIVE_DRIVE_DATA, &in, sizeof(in), &out, sizeof(out), &bytesRtn, 0))

        {

            //"DeviceIoControl failed:DFP_RECEIVE_DRIVE_DATA"<<endl;

            CloseHandle(hDrive);

            hDrive = NULL;

            continue;

        }

        phdinfo = (PIDSECTOR)out.bBuffer;

        char    s[21] = { 0 };

        memcpy(s, phdinfo->sSerialNumber, 20);

        s[20] = 0;

        ChangeByteOrder(s, 20);

        memcpy(lpszHD, s, 20);

        if (strlen(lpszHD) != 0)

        {

            bRet = TRUE;

        }

        break;

    }

    CloseHandle(hDrive);

    hDrive = NULL;

    return bRet;

}

2. 取物理内存信息

获取物理内存信息:

int  g_nTable[256] = { 0 };       //全局数组,作为表使用

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[100] = 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 = 0;

    ObjectAttributes.ObjectName = &PhysicalMemoryUnicodeString;

    ObjectAttributes.Attributes = 0;

    ObjectAttributes.SecurityDescriptor = NULL;

    ObjectAttributes.SecurityQualityOfService = NULL;

    NTSTATUS status = ZwOpenSection(&hPhysicalMemoryHandle,

        4,

        &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)-1,

        &pvVirtualAddress,

        0,

        dwLength,

        &base,

        &dwOutLenth,

        ViewShare,

        0,

        PAGE_READONLY);

    if (!NT_SUCCESS(status))

        return FALSE;

    //当前进程的虚地址空间中,复制数据到输出缓冲区

    memcpy(pvDataBuffer, pvVirtualAddress, dwLength);

    //完成访问,取消地址映射 

    return ZwUnmapViewOfSection((HANDLE)-1, pvVirtualAddress) >= 0;

}

//关闭内核对象,释放空间

BOOL sub_40CF70()

{

    BOOL bRet = FALSE;

    if (&hPhysicalMemoryHandle)

        bRet = CloseHandle(&hPhysicalMemoryHandle);

    if (hLibModule)

        bRet = FreeLibrary(hLibModule);

    return bRet;

}

//填表函数:将数据进行一系列运算,填入全局数组

int sub_40D030()

{

    int nResult = 0;

    signed int j;

    unsigned int uNum = 0;

    signed int i;

    for (i = 0; i < 256; ++i)

    {

        uNum = i;

        for (j = 8; j > 0; --j)

        {

            if (uNum & 1)

                uNum = (uNum >> 1) ^ 0xEDB88320;

            else

                uNum >>= 1;

        }

        g_nTable[i] = uNum;

        nResult = i + 1;

    }

    return nResult;

}

//查表计算

int __cdecl sub_40D300(BYTE bArg1, int* pArg2)

{

    int nResult = 0;

    nResult = *pArg2 & 0xFF;

    *pArg2 = g_nTable[nResult ^ bArg1] ^ ((DWORD)*pArg2 >> 8);

    return nResult;

}

int __cdecl sub_40D0B0(char* pStr, int nLen)

{

    BYTE *bPTmp = NULL;

    int nResult = -1;

    bPTmp = (BYTE*)pStr;

    for (int i = 0; i < nLen; ++i)

        sub_40D300(*bPTmp++, &nResult);

    return ~nResult;

}

//获取第二部分字符串

//地址:0040D28C  call sub_40D0B0

//说明:参数是我自己后来添加的

BOOL GetBiosSerial(char* pBios)

{

    char szBuf[0x1000] = { 0 };

    char szTmp[16] = { 0 };

    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 = 0; i < 2; ++i)

        {

            char szTmp[32] = { 0 };

            int nRet = sub_40D0B0(pTmp, 2048);

            sprintf(szTmp, "%04X", nRet);

            strcat(pBios, szTmp);

            pTmp += 1024;

        }

        bRet = TRUE;

    }

    return bRet;

}

3. 生成机器码

生成机器码:

//获取机器码:计算MD5的函数实现本处省略

char* GetMachineID(char* pMac, char* pBios, char* pMachineID)

{

    BYTE uMD5Buf[32] = { 0 };

    BYTE uTmpBuf[32] = { 0 };

    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, 16);  //将MD5数据转为16进制字符串

        strlwr((char*)uTmpBuf);          //将字符串转为小写

    }

    //尾部追加#

    strcat((char*)uTmpBuf, "#");

    //计算MD5值

    MDString((char*)uTmpBuf, uMD5Buf);

    HexToStr(uTmpBuf, uMD5Buf, 16);  //将MD5数据转为16进制字符串

    //截取前半部分字符串

    uTmpBuf[16] = '\0';

    memcpy(pMachineID, uTmpBuf, 16);

    return pMachineID;

}


4. 生成注册码

生成注册码:

//获取注册码的第一段

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 < 0)

    {

        nStart = 0;

    }

    if (nLen < 0)

    {

        nLen = 0;

    }

    //....

    memcpy(pDest, pSrc + nStart, nLen);

    return pDest;

}

//获取注册码的第四段

char* RegistrationCode_4(char* pSrc, char* pDest, int nLen)

{

    if (nLen < 0)

    {

        nLen = 0;

    }

    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[5] = { 0 };

    BYTE uMD5Buf[CODE_LEN] = { 0 };

    BYTE uTmpBuf[CODE_LEN] = { 0 };

    //计算机器码的MD5值

    MDString(pMachineID, uMD5Buf);

    //将MD5数据转为16进制字符串

    HexToStr(uTmpBuf, uMD5Buf, 16);

    //将机器码的MD5字符串再次计算MD5

    MDString((char*)uTmpBuf, uMD5Buf);

    //将MD5数据转为16进制字符串

    HexToStr(uTmpBuf, uMD5Buf, 16);

    //将字符串转为小写

    strlwr((char*)uTmpBuf);

    //注册码的第一段

    RegistrationCode_1((char*)uTmpBuf, szTmpBuf, 4);

    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, 4);

    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


上传的附件 KeyGen.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功