某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高版本编译的

Alt text 
为了避免随机基址的影响,先去除随机基址。找到“PE”下一行偏移为6的字节处,将“02”修改为“03”,可去掉随机基址

Alt text

二. 注册码分析

随便输入一串注册码

Alt text 
此处MessageBox是断不下来的,使用“F12大法”:OD中按F12暂停程序,Alt+K查看调用堆栈

Alt text 
“黄框”部分是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”

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

.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,因为第一个位置是虚表指针,后推一个才是第一个数据成员

Alt text 
重新载入程序,会断下来好几次,断下来之后再对堆地址指向的内容下硬件写入断点,其中有一次来到下面这个位置,此时恰巧刚填写机器码的前两位“89”

Alt text 
一次一次的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

Alt text 
此处只是读取注册表中的机器码,并不是直接计算出机器码,我们先把注册表项给删掉,重新载入程序,对SHSetValueA下断 
注意:此处不能对RegSetValue(A)、RegSetValueEx(A)下断,不然会很痛苦的,由于自个的无知被这里也折磨了好久 
软件跑起来后,点击“注册–>购买序列号”就会断下来,查看堆栈窗口此时正准备向注册表写入机器码,那么写入之前一定有地方获取机器码

Alt text 
返回到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.com/s/1i5peytJ 密码:ip5q(失效请练习我)

考文档: 
获取硬盘序列号参考代码:http://blog.csdn.net/tody_guo/article/details/26084143 
获取物理内存信息参考代码:http://blog.csdn.net/wangxvfeng101/article/details/7394725

 

posted @ 2017-12-23 14:05  jieliuhouzi  阅读(901)  评论(0编辑  收藏  举报