某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

  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,
  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+]
  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+A9j
  38. .text:00467F87 lea eax, [ebp+var_4]
  39. .text:00467F8A push eax
  40. .text:00467F8B call sub_433840
  41. .text:00467F90 add esp,
  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+]
  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,
  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+]
  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: lea ecx, [ebp+var_144]
  74. .text: call sub_4013B0
  75. .text:0046800E lea ecx, [ebp+var_148]
  76. .text: call sub_4013B0
  77. .text: lea ecx, [ebp+var_10]
  78. .text:0046801C call sub_4013B0
  79. .text: jmp loc_4683F1
  80. .text: ; ---------------------------------------------------------------------------
  81. .text:
  82. .text: loc_468026: ; CODE XREF: sub_467EA0+140j
  83. .text: mov [ebp+var_14],
  84. .text:0046802D
  85. .text:0046802D loc_46802D: ; CODE XREF: sub_467EA0+100j
  86. .text:0046802D lea ecx, [ebp+var_1C]
  87. .text: call ds:??0CWaitCursor@DuiLib@@QAE@XZ
  88. .text: push ecx
  89. .text: mov ecx, esp
  90. .text: lea edx, [ebp+var_4]
  91. .text:0046803C push edx ; 邮箱
  92. .text:0046803D call sub_4069F0
  93. .text: mov ecx, offset unk_4CB420
  94. .text: call sub_46CC70 ; 网络验证:验证注册邮箱是否合法
  95. .text:0046804C mov [ebp+var_C], eax
  96. .text:0046804F cmp [ebp+var_C],
  97. .text: jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!”
  98. .text: push offset word_4B2D6C
  99. .text:0046805A call sub_433800
  100. .text:0046805F add esp,
  101. .text: push 40h
  102. .text: mov eax, [ebp+var_3B0]
  103. .text:0046806A mov ecx, [eax+]
  104. .text:0046806D push ecx
  105. .text:0046806E push offset aVipnotfoundema ; "VIPNotFoundEmail"
  106. .text: call sub_409A20 ; 弹出“邮箱验证失败,无法注册软件”
  107. .text: add esp, 0Ch
  108. .text:0046807B lea ecx, [ebp+var_1C]
  109. .text:0046807E call ds:??1CWaitCursor@DuiLib@@QAE@XZ
  110. .text: lea ecx, [ebp+var_4]
  111. .text: call sub_4013B0
  112. .text:0046808C lea ecx, [ebp+var_144]
  113. .text: call sub_4013B0
  114. .text: 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+1B3j
  124. .text:004680B4 cmp [ebp+var_C],

此处重点分析注册码生成部分:

  1. .text:004325E0 lea ecx, [ebp+arg_4] ; 机器码
  2. .text:004325E3 call sub_4017A0 ; 将机器码转换为字符串
  3. .text:004325E8 mov [ebp+lpString], eax
  4. .text:004325EB cmp [ebp+lpString],
  5. .text:004325EF jnz short loc_4325FD
  6. .text:004325F1 mov [ebp+var_C4],
  7. .text:004325FB jmp short loc_432661
  8. .text:004325FD ; ---------------------------------------------------------------------------
  9. .text:004325FD
  10. .text:004325FD loc_4325FD: ; CODE XREF: sub_4325C0+2Fj
  11. .text:004325FD mov eax, [ebp+lpString]
  12. .text: push eax
  13. .text: call ds:lstrlenW
  14. .text: add eax,
  15. .text:0043260A mov [ebp+var_8], eax
  16. .text:0043260D cmp [ebp+var_8], 3FFFFFFFh
  17. .text: jle short loc_432622
  18. .text: mov [ebp+var_C8],
  19. .text: jmp short loc_432655
  20. .text: ; ---------------------------------------------------------------------------
  21. .text:
  22. .text: loc_432622: ; CODE XREF: sub_4325C0+54j
  23. .text: mov eax, [ebp+var_8]
  24. .text: shl eax,
  25. .text: call __alloca_probe_16 ; alloc申请空间
  26. .text:0043262C mov [ebp+lpMultiByteStr], esp
  27. .text: mov ecx, [ebp+CodePage]
  28. .text: push ecx
  29. .text: mov edx, [ebp+var_8]
  30. .text:0043263C shl edx,
  31. .text:0043263E push edx
  32. .text:0043263F mov eax, [ebp+lpString]
  33. .text: push eax ; 机器码UNICODE字符串
  34. .text: mov ecx, [ebp+lpMultiByteStr]
  35. .text: push ecx ; 接收转换完的ASCII字符串
  36. .text:0043264A call sub_408BC0 ; Unicode机器码转Ascii
  37. .text:0043264F mov [ebp+var_C8], eax
  38. .text:
  39. .text: loc_432655: ; CODE XREF: sub_4325C0+60j
  40. .text: mov edx, [ebp+var_C8]
  41. .text:0043265B mov [ebp+var_C4], edx
  42. .text:
  43. .text: loc_432661: ; CODE XREF: sub_4325C0+3Bj
  44. .text: mov eax, [ebp+var_C4]
  45. .text: mov [ebp+var_4], eax
  46. .text:0043266A push 0FFFFFFFFh
  47. .text:0043266C lea ecx, [ebp+arg_4] ; 机器码地址
  48. .text:0043266F call sub_409B30 ; 获取长度
  49. .text: mov cl, ds:byte_4A5912
  50. .text:0043267A mov [ebp+var_98], cl
  51. .text: push 3Fh
  52. .text: push
  53. .text: lea edx, [ebp+var_97]
  54. .text:0043268A push edx
  55. .text:0043268B call _memset
  56. .text: add esp, 0Ch
  57. .text: push 40h
  58. .text: push
  59. .text: lea eax, [ebp+var_98]
  60. .text:0043269D push eax
  61. .text:0043269E call _memset
  62. .text:004326A3 add esp, 0Ch
  63. .text:004326A6 push 40h
  64. .text:004326A8 lea ecx, [ebp+var_98]
  65. .text:004326AE push ecx ; szBuffer
  66. .text:004326AF mov edx, [ebp+var_4]
  67. .text:004326B2 push edx ; ASCII格式机器码字符串
  68. .text:004326B3 call Get_MD5 ; 计算机器码的MD5
  69. .text:004326B8 add esp, 0Ch
  70. .text:004326BB lea eax, [ebp+var_98]
  71. .text:004326C1 push eax
  72. .text:004326C2 call __strupr ; 机器码的MD5字符串转换为大写字符串
  73. .text:004326C7 add esp,
  74. .text:004326CA mov cl, ds:byte_4A5913
  75. .text:004326D0 mov [ebp+MultiByteStr], cl
  76. .text:004326D3 push 3Fh
  77. .text:004326D5 push
  78. .text:004326D7 lea edx, [ebp+var_4F]
  79. .text:004326DA push edx
  80. .text:004326DB call _memset
  81. .text:004326E0 add esp, 0Ch
  82. .text:004326E3 push 40h
  83. .text:004326E5 push
  84. .text:004326E7 lea eax, [ebp+MultiByteStr]
  85. .text:004326EA push eax
  86. .text:004326EB call _memset
  87. .text:004326F0 add esp, 0Ch
  88. .text:004326F3 push 40h
  89. .text:004326F5 lea ecx, [ebp+MultiByteStr]
  90. .text:004326F8 push ecx
  91. .text:004326F9 lea edx, [ebp+var_98]
  92. .text:004326FF push edx
  93. .text: call Get_MD5 ; 将机器码的MD5字符串再次计算MD5
  94. .text: add esp, 0Ch
  95. .text: lea eax, [ebp+MultiByteStr]
  96. .text:0043270B push eax
  97. .text:0043270C lea ecx, [ebp+obj_A8]
  98. .text: call sub_430200 ; ASCII转为UNICODE
  99. .text: push
  100. .text: lea ecx, [ebp+var_AC]
  101. .text:0043271F push ecx ; 传出参数:用来存储计算完的注册码的第4
  102. .text: lea ecx, [ebp+obj_A8] ; MD5字符串
  103. .text: call RegistrationCode_4 ; 注册码的第四段
  104. .text:0043272B push eax
  105. .text:0043272C push
  106. .text:0043272E push 16h
  107. .text: lea edx, [ebp+var_B0]
  108. .text: push edx
  109. .text: lea ecx, [ebp+obj_A8]
  110. .text:0043273D call RegistrationCode_2_3 ; 注册码的第三段
  111. .text: push eax
  112. .text: push
  113. .text: push 0Ah
  114. .text: lea eax, [ebp+var_B4]
  115. .text:0043274D push eax
  116. .text:0043274E lea ecx, [ebp+obj_A8]
  117. .text: call RegistrationCode_2_3 ; 注册码的第二段
  118. .text: push eax
  119. .text:0043275A push
  120. .text:0043275C lea ecx, [ebp+var_B8]
  121. .text: push ecx
  122. .text: lea ecx, [ebp+obj_A8]
  123. .text: call RegistrationCode_1 ; 注册码的第一段
  124. .text:0043276E push eax
  125. .text:0043276F lea edx, [ebp+var_BC]
  126. .text: push edx
  127. .text: call MyStrCat ; 类似于strcat功能
  128. .text:0043277B add esp, 0Ch
  129. .text:0043277E push eax
  130. .text:0043277F lea eax, [ebp+var_C0]
  131. .text: push eax
  132. .text: call MyStrCat
  133. .text:0043278B add esp, 0Ch
  134. .text:0043278E push eax
  135. .text:0043278F lea ecx, [ebp+var_A4]
  136. .text: push ecx
  137. .text: call MyStrCat
  138. .text:0043279B add esp, 0Ch

注册码分为4小段,其实算法基本上都一样,只是取的区间不同,注册码的第4段

  1. .text:0042C3E0 RegistrationCode_4 proc near ; CODE XREF: sub_4277C0+B1p
  2. .text:0042C3E0
  3. .text:0042C3E0 push ebp
  4. .text:0042C3E1 mov ebp, esp
  5. .text:0042C3E3 sub esp,
  6. .text:0042C3E6 mov [ebp+var_8], ecx
  7. .text:0042C3E9 cmp [ebp+arg_4],
  8. .text:0042C3ED jge short loc_42C3F6
  9. .text:0042C3EF mov [ebp+arg_4],
  10. .text:0042C3F6
  11. .text:0042C3F6 loc_42C3F6: ; CODE XREF: RegistrationCode_4+Dj
  12. .text:0042C3F6 mov ecx, [ebp+var_8]
  13. .text:0042C3F9 call sub_4017C0 ; 获取MD5字串长度
  14. .text:0042C3FE mov [ebp+var_4], eax
  15. .text:0042C401 mov eax, [ebp+arg_4]
  16. .text:0042C404 cmp eax, [ebp+var_4]
  17. .text:0042C407 jl short loc_42C41A
  18. .text:0042C409 mov ecx, [ebp+var_8]
  19. .text:0042C40C push ecx
  20. .text:0042C40D mov ecx, [ebp+arg_0]
  21. .text:0042C410 call sub_4069F0
  22. .text:0042C415 mov eax, [ebp+arg_0]
  23. .text:0042C418 jmp short loc_42C448
  24. .text:0042C41A ; ---------------------------------------------------------------------------
  25. .text:0042C41A
  26. .text:0042C41A loc_42C41A: ; CODE XREF: RegistrationCode_4+27j
  27. .text:0042C41A mov ecx, [ebp+var_8]
  28. .text:0042C41D call sub_408D50
  29. .text:0042C422 push eax ; int
  30. .text:0042C423 mov edx, [ebp+arg_4]
  31. .text:0042C426 push edx ; int
  32. .text:0042C427 mov ecx, [ebp+var_8]
  33. .text:0042C42A call GetString ; 获取字符串的首地址
  34. .text:0042C42F mov ecx, [ebp+var_4] ; 其实关键的就这几行代码
  35. .text:0042C432 lea edx, [eax+ecx*] ; edx指针:移动到字符串的尾部
  36. .text:0042C435 mov eax, [ebp+arg_4]
  37. .text:0042C438 shl eax,
  38. .text:0042C43A sub edx, eax
  39. .text:0042C43C push edx ; 截取的注册码的一部分
  40. .text:0042C43D mov ecx, [ebp+arg_0]
  41. .text:0042C440 call memcpy ; 内联的memcpy,这东西坑了我很久
  42. .text:0042C445 mov eax, [ebp+arg_0]
  43. .text:0042C448
  44. .text:0042C448 loc_42C448: ; CODE XREF: RegistrationCode_4+38j
  45. .text:0042C448 mov esp, ebp
  46. .text:0042C44A pop ebp
  47. .text:0042C44B retn
  48. .text:0042C44B RegistrationCode_4 endp

注册码的第2和3段,其他几段大同小异

三. 网络验证去除

网络验证部分的代码:

  1. .text: lea edx, [ebp+var_4]
  2. .text:0046803C push edx ; 邮箱
  3. .text:0046803D call sub_4069F0
  4. .text: mov ecx, offset unk_4CB420
  5. .text: call sub_46CC70 ; 网络验证:验证注册邮箱是否合法
  6. .text:0046804C mov [ebp+var_C], eax
  7. .text:0046804F cmp [ebp+var_C],
  8. .text: jnz short loc_4680B4 ; 跳转到“升级成功,谢谢您的支持!”
  9. .text: push offset word_4B2D6C
  10. .text:0046805A call sub_433800
  11. .text:0046805F add esp,
  12. .text: push 40h
  13. .text: mov eax, [ebp+var_3B0]
  14. .text:0046806A mov ecx, [eax+]
  15. .text:0046806D push ecx
  16. .text:0046806E push offset aVipnotfoundema
  17. .text: call sub_409A20 ; 弹出“邮箱验证失败,无法注册软件”

网络验证函数sub_46CC70()返回1表示验证失败,打补丁修改返回值即可去除网络验证

四. 机器码获取分析

编写注册机,其实没必要分析机器码的生成,由于我不了解一般软件如何获取机器码的,我就慢慢的从头到尾跟一遍,这一跟才发现机器码的生成比注册码的生成麻烦多了,不过也学到了不少东西,如何获取硬件信息,如何获取物理内存信息等等

1. 流程分析

打开软件,点击:注册–>购买序列号,在下图弹窗找到本机的机器码“89354AF54032753D”

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

  1. .text:00467EA0 push ebp
  2. .text:00467EA1 mov ebp, esp
  3. .text:00467EA3 sub esp, 3B0h
  4. .text:00467EA9 mov eax, ___security_cookie
  5. .text:00467EAE xor eax, ebp
  6. .text:00467EB0 mov [ebp+var_20], eax
  7. .text:00467EB3 mov [ebp+var_3B0], ecx
  8. .text:00467EB9 mov [ebp+var_14],
  9. .text:00467EC0 lea eax, [ebp+var_10]
  10. .text:00467EC3 push eax
  11. .text:00467EC4 mov ecx, offset unk_4CB400
  12. .text:00467EC9 call GetMachineID
  13. .text:00467ECE lea ecx, [ebp+var_10]
  14. .text:00467ED1 call sub_4070F0
  15. .text:00467ED6 movzx ecx, al
  16. .text:00467ED9 test ecx, ecx
  17. .text:00467EDB jz short loc_467F03
  18. .text:00467EDD push 40h ; int
  19. .text:00467EDF mov edx, [ebp+var_3B0]
  20. .text:00467EE5 mov eax, [edx+]
  21. .text:00467EE8 push eax ; int
  22. .text:00467EE9 push offset aNomachineid_0 ; "NoMachineId"
  23. .text:00467EEE call sub_409A20
  24. .text:00467EF3 add esp, 0Ch
  25. .text:00467EF6 lea ecx, [ebp+var_10]
  26. .text:00467EF9 call sub_4013B0
  27. .text:00467EFE jmp loc_4683F1
  28. .text:00467F03 ; ---------------------------------------------------------------------------
  29. .text:00467F03
  30. .text:00467F03 loc_467F03: ; CODE XREF: sub_467EA0+3Bj
  31. .text:00467F03 lea ecx, [ebp+var_148]
  32. .text:00467F09 push ecx
  33. .text:00467F0A mov ecx, [ebp+var_3B0]
  34. .text:00467F10 call sub_467D30
  35. .text:00467F15 push ecx
  36. .text:00467F16 mov ecx, esp
  37. .text:00467F18 lea edx, [ebp+var_10]
  38. .text:00467F1B push edx ; 出现机器码
  39. .text:00467F1C call sub_4069F0

已知的出现机器码的是.text:00467F1B push edx,其中edx存放机器码的地址一层一层的往上回溯

  1. 00467F1Bedx //edx存放机器码地址
  2. 00467F18var_10 //var_10存储机器码
  3. 00467EC9GetMachineID //函数中进行一系列处理得到机器码
  4. 00467EC4offset unk_4CB400 //这是个全局对象,第一个成员是一个堆地址,其中存储机器码

到这里定位到了全局对象,但是全局对象是在那里获取机器码的,只能下内存断点了。对全局对象的第一个成员的位置下硬件写入断点,注意是004CB404而不是004CB400,因为第一个位置是虚表指针,后推一个才是第一个数据成员

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

 
一次一次的retn之后来到下面这段代码中00469527地址处

  1. .text:004694D6 call _memset
  2. .text:004694DB add esp, 0Ch
  3. .text:004694DE lea edx, [ebp+pcbData]
  4. .text:004694E4 push edx
  5. .text:004694E5 lea eax, [ebp+pvData]
  6. .text:004694EB push eax
  7. .text:004694EC push
  8. .text:004694EE push offset aPdfcword_2 ; Pdfcword
  9. .text:004694F3 push offset aSoftwareMicros_16 ;
  10. .text:004694F8 push 80000002h ; hkey
  11. .text:004694FD call ds:SHGetValueW ; 该函数获取注册表的键值
  12. .text: mov [ebp+var_210], eax
  13. .text: cmp [ebp+var_210],
  14. .text: jnz short loc_469527
  15. .text: lea ecx, [ebp+pvData]
  16. .text: push ecx
  17. .text: mov ecx, [ebp+var_214]
  18. .text:0046951F add ecx,
  19. .text: call sub_4013D0
  20. .text:
  21. .text: loc_469527: ; CODE XREF: GetMachineID+2Cj
  22. .text: ; GetMachineID+80j
  23. .text: mov edx, [ebp+var_214]

遇到SHGetValueW就说明了机器码被写到注册表了。可以通过参数定位到注册表路径 
路径:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Pdfcword

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

 
返回到0x0043A08,并在段首下断点 
下面函数生成字符串: “27fd2a8ad66d99c944a52b0a2f9b6ff1”

  1. 004328B3 . 6A push 0x40
  2. 004328B5 . 8D95 68FDFFFF lea edx,dword ptr ss:[ebp-0x298]
  3. 004328BB . push edx
  4. 004328BC . E8 9F5D0300 call PDFConve. ;计算出字符串

下面函数生成字符串: “e3a725d4be6392fae82a71339d6e381b”

  1. 004329B2 > \6A push 0x40
  2. 004329B4 . 8D95 20FDFFFF lea edx,dword ptr ss:[ebp-0x2E0]
  3. 004329BA . push edx
  4. 004329BB . 8B85 14FDFFFF mov eax,dword ptr ss:[ebp-0x2EC]
  5. 004329C1 . push eax
  6. 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],
  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,
  33. .text:0040D22D mov [ebp+var_1010], edx
  34. .text:0040D233
  35. .text:0040D233 loc_40D233:
  36. .text:0040D233 cmp [ebp+var_1010],
  37. .text:0040D23A jge loc_40D2DF
  38. .text:0040D240 mov [ebp+var_1030],
  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,
  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 = ;
  4. CHAR temp = '\0';
  5. for (i = ; i < uscStrSize; i += )
  6. {
  7. temp = szString[i];
  8. szString[i] = szString[i + ];
  9. szString[i + ] = 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 = ;
  22. char szhd[] = { };
  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 = ; j < ; 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. ,
  39. OPEN_EXISTING,
  40. ,
  41. );
  42. if (NULL == hDrive)
  43. {
  44. continue;
  45. }
  46. if (!DeviceIoControl(hDrive, DFP_GET_VERSION, , , &vers, sizeof(vers), &bytesRtn, ))
  47. {
  48. CloseHandle(hDrive);
  49. hDrive = NULL;
  50. continue;
  51. }
  52. //If IDE identify command not supported, fails
  53. if (!(vers.fCapabilities & ))
  54. {
  55. CloseHandle(hDrive);
  56. hDrive = NULL;
  57. continue;
  58. }
  59. //Identify the IDE drives
  60. if (j & )
  61. {
  62. in.irDriveRegs.bDriveHeadReg = 0xb0;
  63. }
  64. else
  65. {
  66. in.irDriveRegs.bDriveHeadReg = 0xa0;
  67. }
  68. if (vers.fCapabilities&( >> 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 = ;
  81. in.irDriveRegs.bSectorNumberReg = ;
  82. in.cBufferSize = ;
  83. if (!DeviceIoControl(hDrive, DFP_RECEIVE_DRIVE_DATA, &in, sizeof(in), &out, sizeof(out), &bytesRtn, ))
  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[] = { };
  92. memcpy(s, phdinfo->sSerialNumber, );
  93. s[] = ;
  94. ChangeByteOrder(s, );
  95. memcpy(lpszHD, s, );
  96. if (strlen(lpszHD) != )
  97. {
  98. bRet = TRUE;
  99. }
  100. break;
  101. }
  102. CloseHandle(hDrive);
  103. hDrive = NULL;
  104. return bRet;
  105. }

2. 取物理内存信息

获取物理内存信息:

  1. int g_nTable[] = { }; //全局数组,作为表使用
  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[] = 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 = ;
  33. ObjectAttributes.ObjectName = &PhysicalMemoryUnicodeString;
  34. ObjectAttributes.Attributes = ;
  35. ObjectAttributes.SecurityDescriptor = NULL;
  36. ObjectAttributes.SecurityQualityOfService = NULL;
  37. NTSTATUS status = ZwOpenSection(&hPhysicalMemoryHandle,
  38. ,
  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)-,
  54. &pvVirtualAddress,
  55. ,
  56. dwLength,
  57. &base,
  58. &dwOutLenth,
  59. ViewShare,
  60. ,
  61. PAGE_READONLY);
  62. if (!NT_SUCCESS(status))
  63. return FALSE;
  64. //当前进程的虚地址空间中,复制数据到输出缓冲区
  65. memcpy(pvDataBuffer, pvVirtualAddress, dwLength);
  66. //完成访问,取消地址映射
  67. return ZwUnmapViewOfSection((HANDLE)-, pvVirtualAddress) >= ;
  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 = ;
  83. signed int j;
  84. unsigned int uNum = ;
  85. signed int i;
  86. for (i = ; i < ; ++i)
  87. {
  88. uNum = i;
  89. for (j = ; j > ; --j)
  90. {
  91. if (uNum & )
  92. uNum = (uNum >> ) ^ 0xEDB88320;
  93. else
  94. uNum >>= ;
  95. }
  96. g_nTable[i] = uNum;
  97. nResult = i + ;
  98. }
  99. return nResult;
  100. }
  101. //查表计算
  102. int __cdecl sub_40D300(BYTE bArg1, int* pArg2)
  103. {
  104. int nResult = ;
  105. nResult = *pArg2 & 0xFF;
  106. *pArg2 = g_nTable[nResult ^ bArg1] ^ ((DWORD)*pArg2 >> );
  107. return nResult;
  108. }
  109. int __cdecl sub_40D0B0(char* pStr, int nLen)
  110. {
  111. BYTE *bPTmp = NULL;
  112. int nResult = -;
  113. bPTmp = (BYTE*)pStr;
  114. for (int i = ; 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] = { };
  124. char szTmp[] = { };
  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 = ; i < ; ++i)
  143. {
  144. char szTmp[] = { };
  145. int nRet = sub_40D0B0(pTmp, );
  146. sprintf(szTmp, "%04X", nRet);
  147. strcat(pBios, szTmp);
  148. pTmp += ;
  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[] = { };
  5. BYTE uTmpBuf[] = { };
  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, ); //将MD5数据转为16进制字符串
  33. strlwr((char*)uTmpBuf); //将字符串转为小写
  34. }
  35. //尾部追加#
  36. strcat((char*)uTmpBuf, "#");
  37. //计算MD5值
  38. MDString((char*)uTmpBuf, uMD5Buf);
  39. HexToStr(uTmpBuf, uMD5Buf, ); //将MD5数据转为16进制字符串
  40. //截取前半部分字符串
  41. uTmpBuf[] = '\0';
  42. memcpy(pMachineID, uTmpBuf, );
  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 < )
  11. {
  12. nStart = ;
  13. }
  14. if (nLen < )
  15. {
  16. nLen = ;
  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 < )
  26. {
  27. nLen = ;
  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[] = { };
  45. BYTE uMD5Buf[CODE_LEN] = { };
  46. BYTE uTmpBuf[CODE_LEN] = { };
  47. //计算机器码的MD5值
  48. MDString(pMachineID, uMD5Buf);
  49. //将MD5数据转为16进制字符串
  50. HexToStr(uTmpBuf, uMD5Buf, );
  51. //将机器码的MD5字符串再次计算MD5
  52. MDString((char*)uTmpBuf, uMD5Buf);
  53. //将MD5数据转为16进制字符串
  54. HexToStr(uTmpBuf, uMD5Buf, );
  55. //将字符串转为小写
  56. strlwr((char*)uTmpBuf);
  57. //注册码的第一段
  58. RegistrationCode_1((char*)uTmpBuf, szTmpBuf, );
  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, );
  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

某pdf转word v6.3.0.2算法分析的更多相关文章

  1. iText导出pdf、word、图片

    一.前言 在企业的信息系统中,报表处理一直占比较重要的作用,本文将介绍一种生成PDF报表的Java组件--iText.通过在服务器端使用Jsp或JavaBean生成PDF报表,客户端采用超级连接显示或 ...

  2. .net mvc使用FlexPaper插件实现在线预览PDF,EXCEL,WORD的方法

    FlexPaper插件可以实现在浏览器中在线预览pdf,word,excel等. 在网上看到很多关于这个插件实现预览的技术,但是很难做到word和excel在线预览. pdf很好实现. 首先下载相关的 ...

  3. Python处理PDF和Word文档常用的方法

    Python处理PDF和Word文档的模块是PyPDF2,使用之前需要先导入. 打开一个PDF文档的操作顺序是:用open()函数打开文件并用一个变量来接收,然后把变量给传递给PdfFileReade ...

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

  5. java PDF转word的初步实现

    package com.springboot.springboot.util; import java.io.File; import java.io.FileOutputStream; import ...

  6. CentOS 6.6 升级GCC G++ (当前最新版本为v6.1.0) (完整)

    ---恢复内容开始--- CentOS 6.6 升级GCC G++ (当前最新GCC/G++版本为v6.1.0) 没有便捷方式, yum update....   yum install 或者 添加y ...

  7. 基于DevExpress实现对PDF、Word、Excel文档的预览及操作处理

    http://www.cnblogs.com/wuhuacong/p/4175266.html 在一般的管理系统模块里面,越来越多的设计到一些常用文档的上传保存操作,其中如PDF.Word.Excel ...

  8. 一款免费支持PDF、word、excel、PPT、jpeg之间互转线上软件

    偶然发现的一款免费支持PDF.word.excel.PPT.jpeg之间互转,支持合并pdf.加密解密PDF的线上软件,首先声明,不是广告党,我自己试用过,确实是目前我用过最好用的,如果有朋友有更好的 ...

  9. pdf转word

    一.刚需 pdf转word,这个需求肯定是有的.但是大家都知道,pdf是用来排版打印的,所以编辑起来会比较麻烦,所以,大家都会尝试将pdf的内容转成word,然后再进行编辑. 二.方法 1.用offi ...

随机推荐

  1. 使用Lock锁生产者消费者模式

    package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...

  2. [Bayesian] “我是bayesian我怕谁”系列 - Variational Autoencoders

    本是neural network的内容,但偏偏有个variational打头,那就聊聊.涉及的内容可能比较杂,但终归会 end with VAE. 各个概念的详细解释请点击推荐的链接,本文只是重在理清 ...

  3. 基于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 ...

  4. Vim常用操作-Nginx配置文件批量加注释。

    刚接触 Vim 会觉得它的学习曲线非常陡峭,要记住很多命令.所以这个系列的分享,不会教你怎么配置它,而是教你怎么快速的使用它. 本期我们要实现给 Nginx 配置文件批量注释的功能,先来看效果: 操作 ...

  5. 小程序 wx.getRecorderManager 录音 to 语音识别

    微信扫小程序码看调用效果(自然语言理解小助手) 欢迎转载,请保留原文链接:http://www.happycxz.com/m/?p=125 这次主要是把我的api更新了一下,支持微信小程序新的录音接口 ...

  6. 史上最全的IntelliJIdea快捷键

    Ctrl+Shift+方向键Up/Down 代码向上/下移动. Ctrl+X 删除行 Ctrl+Y 也是删除行,不知道有啥区别 Ctrl+D 复制行 Ctrl+Alt+L 格式化代码 Ctrl+N 查 ...

  7. SUID,SGID,SBIT这些到底是什么

    SUID,SGID,SBIT这些都是文件的特殊权限. SUID(Set UID)文件执行过程中,用户拥有文件的root权限. SGID(Set GID)文件执行过程中,执行者拥有该文件的用户组的权限. ...

  8. C#中抽象类与接口的区别

    1.面向接口编程和面向对象编程是什么关系 首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分.或者说,它是面向对象 ...

  9. Winform界面中主从表编辑界面的快速处理

    在Winform开发中,我们往往除了常规的单表信息录入外,有时候设计到多个主从表的数据显示.编辑等界面,单表的信息一般就是控件和对象实体一一对应,然后调用API保存即可,主从表就需要另外特殊处理,本随 ...

  10. ASP.NET Core 认证与授权[5]:初识授权

    经过前面几章的姗姗学步,我们了解了在 ASP.NET Core 中是如何认证的,终于来到了授权阶段.在认证阶段我们通过用户令牌获取到用户的Claims,而授权便是对这些的Claims的验证,如:是否拥 ...