本帖最后由 tree_fly 于 2015-8-19 23:11 编辑
分析KeyGenMe(KM)2014#001——山重水复疑无路,柳暗花明又一村。 【上篇】
链接 [url=https://www.chinapyg.com/thread-74486-1-1.html]分析KeygenMe2014#001——山重水复疑无路,柳暗花明又一村 【下篇】 算法注册机源码[/url]
今天我们来分析KeygenMe2014#001,这款软件破解难度略高,不过各位如果仔细、认真的分析代码,问题均会迎刃而解。
让我想起陆游的一首《游西山村》,可谓是:山重水复疑无路,柳暗花明又一村!
下面进入主题:
0x01 基础篇*****************************************************************************************************
PEiD检测Keygenme001.exe,被识别加壳软件:ASPack 2.12 -> Alexey Solodovnikov.
为了调试方便,这里我们脱壳后保存程序:KG_Unpack.exe.
0x02 机器码篇**************************************************************************************************
很容易,我们来到这里
[Asm] 纯文本查看 复制代码 004042D0 /$ 81EC 4C020000 SUB ESP, 24C
004042D6 |. A1 78244100 MOV EAX, DWORD PTR DS:[412478]
004042DB |. 53 PUSH EBX
004042DC |. 55 PUSH EBP
004042DD |. 56 PUSH ESI
004042DE |. 57 PUSH EDI ; ntdll.7C930228
004042DF |. 6A 68 PUSH 68 ; /RsrcName = 104.
004042E1 |. 50 PUSH EAX ; |hInst = NULL
004042E2 |. FF15 8C914000 CALL NEAR DWORD PTR DS:[<&USER32.Load>; \LoadIconA
004042E8 |. 8BB424 600200>MOV ESI, DWORD PTR SS:[ESP+260]
004042EF |. 50 PUSH EAX ; /lParam = 0
004042F0 |. 6A 01 PUSH 1 ; |wParam = 1
004042F2 |. 68 80000000 PUSH 80 ; |Message = WM_SETICON
004042F7 |. 56 PUSH ESI ; |hWnd = FFFFFFFF
004042F8 |. FF15 80914000 CALL NEAR DWORD PTR DS:[<&USER32.Send>; \SendMessageA
004042FE |. 8B0D 681E4100 MOV ECX, DWORD PTR DS:[411E68] ; C:\
00404304 |. 8D5424 34 LEA EDX, DWORD PTR SS:[ESP+34]
00404308 |. 894C24 28 MOV DWORD PTR SS:[ESP+28], ECX
0040430C |. 68 FF000000 PUSH 0FF ; /pFileSystemNameSize = 000000FF
00404311 |. 8D4424 34 LEA EAX, DWORD PTR SS:[ESP+34] ; |
00404315 |. 52 PUSH EDX ; |pFileSystemNameBuffer = ntdll.KiFastSystemCallRet
00404316 |. 8D4C24 34 LEA ECX, DWORD PTR SS:[ESP+34] ; |
0040431A |. 50 PUSH EAX ; |pFileSystemFlags = NULL
0040431B |. 8D5424 2C LEA EDX, DWORD PTR SS:[ESP+2C] ; |
0040431F |. 51 PUSH ECX ; |pMaxFilenameLength =
00404320 |. 52 PUSH EDX ; |pVolumeSerialNumber = ntdll.KiFastSystemCallRet
00404321 |. 8D4424 54 LEA EAX, DWORD PTR SS:[ESP+54] ; |
00404325 |. 68 FF000000 PUSH 0FF ; |MaxVolumeNameSize = FF (255.)
0040432A |. 8D4C24 40 LEA ECX, DWORD PTR SS:[ESP+40] ; |
0040432E |. 50 PUSH EAX ; |VolumeNameBuffer = NULL
0040432F |. 51 PUSH ECX ; |RootPathName
00404330 |. FF15 7C904000 CALL NEAR DWORD PTR DS:[<&KERNEL32.Ge>; \GetVolumeInformationA
00404336 |. B9 40000000 MOV ECX, 40 ; 获取C盘卷的序列号
0040433B |. 33C0 XOR EAX, EAX
0040433D |. 8D7C24 55 LEA EDI, DWORD PTR SS:[ESP+55]
00404341 |. B3 34 MOV BL, 34
00404343 |. F3:AB REP STOS DWORD PTR ES:[EDI]
00404345 |. 66:AB STOS WORD PTR ES:[EDI]
00404347 |. AA STOS BYTE PTR ES:[EDI]
00404348 |. 8B4424 20 MOV EAX, DWORD PTR SS:[ESP+20] ;
0040434C |. C64424 54 50 MOV BYTE PTR SS:[ESP+54], 50
00404351 |. C1E0 29 SHL EAX, 29
00404354 |. 99 CDQ
00404355 |. 33C2 XOR EAX, EDX ; ntdll.KiFastSystemCallRet
00404357 |. C64424 55 59 MOV BYTE PTR SS:[ESP+55], 59
0040435C |. 2BC2 SUB EAX, EDX ; ntdll.KiFastSystemCallRet
0040435E |. 8D5424 5B LEA EDX, DWORD PTR SS:[ESP+5B]
00404362 |. 50 PUSH EAX ; /<%d> = 0
00404363 |. 68 641E4100 PUSH KG_Unpac.00411E64 ; |%d
00404368 |. 52 PUSH EDX ; |s = ntdll.KiFastSystemCallRet
00404369 |. C64424 62 47 MOV BYTE PTR SS:[ESP+62], 47 ; |
0040436E |. C64424 63 32 MOV BYTE PTR SS:[ESP+63], 32 ; |
00404373 |. C64424 64 30 MOV BYTE PTR SS:[ESP+64], 30 ; |
00404378 |. C64424 65 31 MOV BYTE PTR SS:[ESP+65], 31 ; |
0040437D |. 885C24 66 MOV BYTE PTR SS:[ESP+66], BL ; |
00404381 |. FF15 9C914000 CALL NEAR DWORD PTR DS:[<&USER32.wspr>; \wsprintfA
00404387 |. 8B2D 84914000 MOV EBP, DWORD PTR DS:[<&USER32.SetD>; USER32.SetDlgItemTextA
这里使用了这个函数,GetVolumeInformationA 函数解释如下:
RootPathName String,欲获取信息的那个卷的根路径,KM提供的是C:\
VolumeNameBuffer String,用于装载卷名(卷标)的一个字串
VolumeNameSize Long,lpVolumeNameBuffer字串的长度
VolumeSerialNumber Long,用于装载磁盘卷序列号的变量
MaximumComponentLength Long,指定一个变量,用于装载文件名每一部分的长度。
堆栈 SS:[0012F9B8]=FC2363B9 ,这里就是C盘的序列号
EAX=00000001
注意这里
00404348 |. 8B4424 20 MOV EAX, DWORD PTR SS:[ESP+20] ;
0040434C |. C64424 54 50 MOV BYTE PTR SS:[ESP+54], 50
00404351 |. C1E0 29 SHL EAX, 29
C盘卷标被再次读取给EAX,SHL 0x29 = SHL 0x9
0xFC2363B9 * 2^0x9 = 0x46C77200(1187475968) ;这个就是机器码的后部分。 PYG2014 + 1187475968
0012F9EC 50 59 47 32 30 31 34 31 31 38 37 34 37 35 39 36 PYG2014118747596
0012F9FC 38 8
0x03 文件篇******************************************************************************************************
函数原型:FILE * fopen(const char * path,const char * mode);
1.程序查找目录内是否有 PYG2014.dat文件,如果不存在,随机生成一串字符写入内存,文件存在即读取。
[Asm] 纯文本查看 复制代码 004043E7 |. 68 5C1E4100 PUSH KG_Unpac.00411E5C ; /rb
004043EC |. 50 PUSH EAX ; |path = ".\\PYG2014.dat"
004043ED |. C64424 1C 59 MOV BYTE PTR SS:[ESP+1C], 59 ; |
004043F2 |. C64424 1D 47 MOV BYTE PTR SS:[ESP+1D], 47 ; |
004043F7 |. C64424 1E 32 MOV BYTE PTR SS:[ESP+1E], 32 ; |
004043FC |. C64424 1F 30 MOV BYTE PTR SS:[ESP+1F], 30 ; |
00404401 |. C64424 20 31 MOV BYTE PTR SS:[ESP+20], 31 ; |
00404406 |. 885C24 21 MOV BYTE PTR SS:[ESP+21], BL ; |
0040440A |. 884C24 22 MOV BYTE PTR SS:[ESP+22], CL ; |
0040440E |. C64424 23 64 MOV BYTE PTR SS:[ESP+23], 64 ; |
00404413 |. C64424 24 61 MOV BYTE PTR SS:[ESP+24], 61 ; |
00404418 |. C64424 25 74 MOV BYTE PTR SS:[ESP+25], 74 ; |
0040441D |. 885424 26 MOV BYTE PTR SS:[ESP+26], DL ; |
00404421 |. FF15 E4904000 CALL NEAR DWORD PTR DS:[<&msvcrt.fope>; \fopen
00404427 |. 8BD8 MOV EBX, EAX
00404429 |. 83C4 08 ADD ESP, 8
0040442C |. 85DB TEST EBX, EBX
2.文件流内存地址:77C2FCE0
向地址:004123F4 写入文件内容
对地址:004123FF 写入字节0结束符,提示将读取 F4->FE,这0xB个字符。
00404475 |. C605 FF234100>MOV BYTE PTR DS:[4123FF], 0
读文件:
[Asm] 纯文本查看 复制代码 0040442E |. 74 1C JE SHORT KG_Unpac.0040444C
00404430 |. 53 PUSH EBX ; /stream = 00050234
00404431 |. 6A 0A PUSH 0A ; |n = A (10.)
00404433 |. 6A 01 PUSH 1 ; |size = 1
00404435 |. 68 F4234100 PUSH KG_Unpac.004123F4 ; |ptr = KG_Unpac.004123F4
0040443A |. FF15 EC904000 CALL NEAR DWORD PTR DS:[<&msvcrt.frea>; \fread
00404440 |. 53 PUSH EBX ; /stream = 00050234
00404441 |. FF15 F0904000 CALL NEAR DWORD PTR DS:[<&msvcrt.fclo>; \fclose
否则随机生成0xB字节数据:
[Asm] 纯文本查看 复制代码 00404447 |. 83C4 14 ADD ESP, 14
0040444A |. EB 1A JMP SHORT KG_Unpac.00404466
0040444C |> 33DB XOR EBX, EBX
0040444E |> FF15 00914000 /CALL NEAR DWORD PTR DS:[<&msvcrt.ran>; [rand
00404454 |. 02C0 |ADD AL, AL
00404456 |. B1 01 |MOV CL, 1
00404458 |. 2AC8 |SUB CL, AL
0040445A |. 888B F4234100 |MOV BYTE PTR DS:[EBX+4123F4], CL
00404460 |. 43 |INC EBX
00404461 |. 83FB 0B |CMP EBX, 0B
00404464 |.^ 72 E8 \JB SHORT KG_Unpac.0040444E
3.获取当前文件的完整路径,存放在地址:12FAF0
kernel32.GetModuleFileNameA
[Asm] 纯文本查看 复制代码 00404466 |> 8D9424 580100>LEA EDX, DWORD PTR SS:[ESP+158]
0040446D |. 68 04010000 PUSH 104 ; /BufSize =
00404472 |. 52 PUSH EDX ; |PathBuffer = NULL
00404473 |. 6A 00 PUSH 0 ; |hModule = NULL
00404475 |. C605 FF234100>MOV BYTE PTR DS:[4123FF], 0 ; |
0040447C |. FF15 78904000 CALL NEAR DWORD PTR DS:[<&KERNEL32.Ge>; \GetModuleFileNameA
4.打开当前文件,执行READ操作
kernel32.CreateFileA
[Asm] 纯文本查看 复制代码 00404482 |. 6A 00 PUSH 0 ; /hTemplateFile = NULL
00404484 |. 6A 00 PUSH 0 ; |Attributes = 0
00404486 |. 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00404488 |. 6A 00 PUSH 0 ; |pSecurity = NULL
0040448A |. 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
0040448C |. 8D8424 6C0100>LEA EAX, DWORD PTR SS:[ESP+16C] ; |
00404493 |. 6A 08 PUSH 8 ; |Access = 8
00404495 |. 50 PUSH EAX ; |FileName =
00404496 |. FF15 74904000 CALL NEAR DWORD PTR DS:[<&KERNEL32.Cr>; \CreateFileA
0040449C |. 8BD8 MOV EBX, EAX
0040449E |. 83FB FF CMP EBX, -1
004044A1 |. 74 3C JE SHORT KG_Unpac.004044DF
5.获取文件大小,以字节为单位 294912(0x48000)
kernel32.GetFileSize
004044A3 |. 6A 00 PUSH 0 ; /pFileSizeHigh = NULL
004044A5 |. 53 PUSH EBX ; |hFile =
004044A6 |. FF15 70904000 CALL NEAR DWORD PTR DS:[<&KERNEL32.Ge>; \GetFileSize
6.向地址: 0012FAF0 写入当前文件大小,且十进制表示,如:ASCII "294912"
USER32.wsprintfA
[Asm] 纯文本查看 复制代码 004044AC |. 50 PUSH EAX ; /<%d> =
004044AD |. 8D8C24 5C0100>LEA ECX, DWORD PTR SS:[ESP+15C] ; |
004044B4 |. 68 641E4100 PUSH KG_Unpac.00411E64 ; |%d
004044B9 |. 51 PUSH ECX ; |s =
004044BA |. 894424 30 MOV DWORD PTR SS:[ESP+30], EAX ; |
004044BE |. FF15 9C914000 CALL NEAR DWORD PTR DS:[<&USER32.wspr>; \wsprintfA
004044C4 |. 83C4 0C ADD ESP, 0C
004044C7 |. 53 PUSH EBX ; /hObject = 00050234
004044C8 |. FF15 6C904000 CALL NEAR DWORD PTR DS:[<&KERNEL32.Cl>; \CloseHandle
7.这里比较文件大小,是否小于等于 0x11170,校验是否被脱壳,并对全局变量值[40A010]修改,这里很关键,关系到后面算法分析:
004044CE |. 817C24 24 701>CMP DWORD PTR SS:[ESP+24], 11170 ; 比较文件大小断点
004044D6 |. 77 07 JA SHORT KG_Unpac.004044DF
004044D8 |. C605 10A04000>MOV BYTE PTR DS:[40A010], 7E
*.为了我们伟大的胜利,修改一下吧。
004044CE 817C24 24 701>CMP DWORD PTR SS:[ESP+24], 11170
004044D6 76 07 JBE SHORT KG_Unpac.004044DF
修改成大于跳转,即 JA(76改77),并另存为新文件:KG_Unpack_Patch.exe
分析KeygenMe2014#001——山重水复疑无路,柳暗花明又一村 【下篇】 算法注册机源码
|