廫榋栭偺壴壟丒懱尡斉(一个全日文小游戏) 图片文件(扩展名DET)分析及解密(原创)
可以向我索取EXE文件和IDB文件。---------- 本人QQ:330759606 ≮风々吉它≯
重绘时,一定会调用到图片。再往回找,从而发现源头
首先查找绘图函数
得到StretchDIBits
int __stdcall StretchDIBits(HDC,int,int,int,int,int,int,int,int,const void *,const BITMAPINFO *,UINT,DWORD)
.idata:00430040 extrn StretchDIBits:dword
;IDA中的流程缩略图关了,重打开的办法:View-Toolbars-Navigation-Graph overview
.text:0040AC3F mov ebp, [esi]
.text:0040AC41 mov ebx, dword_43C208
.text:0040AC47 mov esi, [esi+0Ch]
.text:0040AC4A push SRCCOPY ; DWORD
.text:0040AC4F push 0 ; UINT
.text:0040AC51 push edi ; BITMAPINFO *
.text:0040AC52 mov edx, ebx
.text:0040AC54 push ebp ; CONST VOID *lpBits
.text:0040AC55 sub esi, ebx
.text:0040AC55
.text:0040AC57
.text:0040AC57 loc_40AC57: ; CODE XREF: DrawBitMap+ADj
.text:0040AC57 mov eax, dword_43CB8C
.text:0040AC5C mov ecx, dword_43CB80
.text:0040AC62 sub edx, eax
.text:0040AC64 lea eax, [edx+1]
.text:0040AC67 mov edx, dword_43CB88
.text:0040AC6D push eax ; int
.text:0040AC6E sub edx, ecx
.text:0040AC70 inc edx
.text:0040AC71 push edx ; int
.text:0040AC72 dec esi
.text:0040AC73 push esi ; int
.text:0040AC74 push ecx ; int
.text:0040AC75 push eax ; int
.text:0040AC76 mov eax, dword_43CB8C
.text:0040AC7B push edx ; int
.text:0040AC7C push eax ; int
.text:0040AC7D push ecx ; int
.text:0040AC7E mov ecx, [esp+68h+arg_0]
.text:0040AC82 push ecx ; HDC
.text:0040AC83 call ds:StretchDIBits //拉伸图片
.text:0040AC89 push edi
.text:0040AC8A call Delete
查看lpBits的内容,记下里面的数据
跟踪到lpBits来源处:
WinProc(消息是WM_PAINT)->SubDraw->DrawBitMap->StretchDIBits
WinPRoc:
call ds:GetDC
mov edx, dword_4CBDD0 ;背景数据
mov esi, eax
push esi ; hdc
push edx ; lpBit
call SubDraw
mov eax, hWnd
push esi ; hDC
push eax ; hWnd
call ds:ReleaseDC
发现从绘图出发,很难找到解密入口,改成从文件读取时查找解密入口
改变思路,从文件读取中查找,从而直接找到解密处
首先查找文件读取函数
中断CreateFile函数,直到是BMP文件(这个要通过OD动态分析)
然后再从OD的堆栈窗口中找到上级函数
Call stack of main thread
Address Stack Procedure / arguments Called from Frame
0012EF50 0041E16B Includes kernel32.CreateFileA 廫榋栭偺.0041E169 0012F62C
0012EF54 0012EF74 FileName = "pic/mlogo.bmp"
0012EF58 80000000 Access = GENERIC_READ
0012EF5C 00000000 ShareMode = 0
0012EF60 00000000 pSecurity = NULL
0012EF64 00000003 Mode = OPEN_EXISTING
0012EF68 00000080 Attributes = NORMAL
0012EF6C 00000000 hTemplateFile = NULL
0012F07C 0040C17F 廫榋栭偺.0041E120 廫榋栭偺.0040C17A
0012F0B4 00405D48 廫榋栭偺.0040C170 廫榋栭偺.00405D43
然后在0041E169处中断
0041E14C . 8B35 C0004300 mov esi, dword ptr [<&KERNEL32.Creat>; kernel32.CreateFileA
0041E152 . 6A 00 push 0 ; /hTemplateFile = NULL
0041E154 . 68 80000000 push 80 ; |Attributes = NORMAL
0041E159 . 6A 03 push 3 ; |Mode = OPEN_EXISTING
0041E15B . 6A 00 push 0 ; |pSecurity = NULL
0041E15D . 6A 00 push 0 ; |ShareMode = 0
0041E15F . 68 00000080 push 80000000 ; |Access = GENERIC_READ
0041E164 . 8D4424 1C lea eax, dword ptr [esp+1C] ; |
0041E168 . 50 push eax ; |FileName
0041E169 . FFD6 call esi ; CreateFile ; /CreateFileA
先是pic/mlog.bmp,然后是bg/mlog.bmp.然后是parts/mlog.bmp
经过测试,发现这个函数,每次打开这些文件都失败。
这说明,解出来的BMP并不放在文件中。
查看目录下的小文件含有的信息,试着找到与pic/mlog.bmp相关的内容。
发现目录下有以pic,bg,parts开头的文件。
逐个查看,发现,NME扩展名中存的是BMP文件名。
parts.nme:(内容)
event.bmp hr.bmp logo_00.bmp logo_s_00.bmp mes.bmp mlogo.bmp name.bmp pph1_00.bmp pph2.bmp ppS1.bmp test04.bmp
test04c.bmp test04d.bmp test04e.bmp c_mink_00.bmp b201.bmp nowonsale02.bmp nowonsale01.bmp ?
所以肯定会打开parts.nme;
重启动程序,再次跟踪CreateFile 直到出现parts.nme;
发现先打开parts.atm,再打开parts.nme.在之前是打开Pic和bg开头的。
所以认为ATM中存放的是配置,而NME是名称
从OD的堆栈窗口中找到上级函数
Call stack of main thread
Address Stack Procedure / arguments Called from Frame
0012F05C 0040C33C Includes kernel32.CreateFileA 廫榋栭偺.0040C33A
0012F060 0043CFB0 FileName = "parts.nme"
0012F064 80000000 Access = GENERIC_READ
0012F068 00000000 ShareMode = 0
0012F06C 00000000 pSecurity = NULL
0012F070 00000003 Mode = OPEN_EXISTING
0012F074 00000080 Attributes = NORMAL
0012F078 00000000 hTemplateFile = NULL
0012F0B4 00405D48 廫榋栭偺.0040C170 廫榋栭偺.00405D43
在40c33A处中断:
0040C2FA > /B8 67666666 mov eax, 66666667
0040C2FF . F7EB imul ebx
0040C301 . C1FA 03 sar edx, 3
0040C304 . 8BCA mov ecx, edx
0040C306 . 68 B8D04300 push 0043D0B8 ; ASCII "parts"
0040C30B . C1E9 1F shr ecx, 1F
0040C30E . 03D1 add edx, ecx
0040C310 . 68 34984300 push 00439834 ; ASCII "%s.nme"
0040C315 . 68 B0CF4300 push 0043CFB0 ; ASCII "parts.nme"
0040C31A . 895424 2C mov dword ptr [esp+2C], edx
0040C31E . FFD7 call edi ;wsprintf
0040C320 . 83C4 0C add esp, 0C
0040C323 . 6A 00 push 0
0040C325 . 68 80000000 push 80
0040C32A . 6A 03 push 3
0040C32C . 6A 00 push 0
0040C32E . 6A 00 push 0
0040C330 . 68 00000080 push 80000000
0040C335 . 68 B0CF4300 push 0043CFB0 ; ASCII "parts.nme"
0040C33A . FFD5 call ebp ;createfile
然后分析堆栈;
Call stack of main thread
Address Stack Procedure / arguments Called from Frame
0012F0B4 00405D48 廫榋栭偺.0040C170 廫榋栭偺.00405D43
Frame_ack->GetBmpInfo
发现这个来自一个线程
好像是用于进行帧处理的
然后分析下如何对这个文件操作的
0040C347 . 52 push edx
0040C348 . C74424 18 000>mov dword ptr [esp+18], 0
0040C350 . E8 3BFDFFFF call 0040C090 ;分析文件 ; DEncrypt
0040C355 . 83C4 04 add esp, 4
0040C358 . 85C0 test eax, eax ;成功与否
0040C35A . 7D 68 jge short 0040C3C4
0040C35C . 8B5C24 30 mov ebx, dword ptr [esp+30]
0040C360 . 53 push ebx
0040C361 . 68 B0CF4300 push 0043CFB0 ; ASCII "parts.nme"
0040C366 . E8 A5FDFFFF call 0040C110 ;err_show
0040C36B . 83C4 08 add esp, 8
0040C36E . 83FB 03 cmp ebx, 3
0040C371 . 7D 51 jge short 0040C3C4
跟踪call 0040C090
0040C090 /$ 83EC 08 sub esp, 8
0040C093 |. 53 push ebx
0040C094 |. 55 push ebp
0040C095 |. 8B6C24 14 mov ebp, dword ptr [esp+14]
0040C099 |. 57 push edi
0040C09A |. 6A 00 push 0 ; /pFileSizeHigh = NULL
0040C09C |. 56 push esi ; |hFile
0040C09D |. FF15 08014300 call dword ptr [<&KERNEL32.GetFileSiz>; /GetFileSize
0040C0A3 |. 8BF8 mov edi, eax
0040C0A5 |. 8D5F FC lea ebx, dword ptr [edi-4]
0040C0A8 |. 53 push ebx
0040C0A9 |. C74424 1C 000>mov dword ptr [esp+1C], 0
0040C0B1 |. E8 B75D0100 call 00421E6D ; new
0040C0B6 |. 83C4 04 add esp, 4
0040C0B9 |. 6A 00 push 0 ; /pOverlapped = NULL
0040C0BB |. 8D4C24 10 lea ecx, dword ptr [esp+10] ; |
0040C0BF |. 51 push ecx ; |pBytesRead
0040C0C0 |. 53 push ebx ; |BytesToRead
0040C0C1 |. 8B1D 10014300 mov ebx, dword ptr [<&KERNEL32.ReadF>; |kernel32.ReadFile
0040C0C7 |. 50 push eax ; |Buffer 文件内容
0040C0C8 |. 56 push esi ; |hFile
0040C0C9 |. 8945 00 mov dword ptr [ebp], eax ; |
0040C0CC |. FFD3 call ebx ; /ReadFile
0040C0CE |. 6A 00 push 0 ; /pOverlapped = NULL
0040C0D0 |. 8D5424 10 lea edx, dword ptr [esp+10] ; |
0040C0D4 |. 52 push edx ; |pBytesRead
0040C0D5 |. 6A 04 push 4 ; |BytesToRead = 4
0040C0D7 |. 8D4424 1C lea eax, dword ptr [esp+1C] ; |
0040C0DB |. 50 push eax ; |BUFFER 解密号
0040C0DC |. 56 push esi ; |hFile
0040C0DD |. FFD3 call ebx ; /ReadFile
0040C0DF |. 8B4D 00 mov ecx, dword ptr [ebp] ; new address
0040C0E2 |. 51 push ecx
0040C0E3 |. 8D57 FC lea edx, dword ptr [edi-4]
0040C0E6 |. 8D4C24 1C lea ecx, dword ptr [esp+1C]
0040C0EA |. E8 21FFFFFF call 0040C010 ; 通过文件内容算出加密号
0040C0EF |. 8B4424 14 mov eax, dword ptr [esp+14]
0040C0F3 |. 8B4C24 1C mov ecx, dword ptr [esp+1C]
0040C0F7 |. 83C4 04 add esp, 4
0040C0FA |. 3BC1 cmp eax, ecx ; 比较两个加密号
0040C0FC |. 5F pop edi
0040C0FD |. 5D pop ebp
0040C0FE |. 5B pop ebx
0040C0FF |. 74 07 je short 0040C108
0040C101 |. 83C8 FF or eax, FFFFFFFF ;出错
0040C104 |. 83C4 08 add esp, 8
0040C107 |. C3 retn
0040C108 |> 8BC2 mov eax, edx ;加密号位置
0040C10A |. 83C4 08 add esp, 8
0040C10D /. C3 retn
分析后得出:
取出文件内容直到倒数第4个字节。最后4个字节再独立取出来。
我们认为这4个字节用于文件校验。称为加密号
接下来应该对NME文件进行处理,我们猜想可能是查mlog.bmp对应的数据
0040C3F0 > 8B53 F8 mov edx, dword ptr [ebx-8]
0040C3F3 . |035424 14 add edx, dword ptr [esp+14] ; 取下一个OBJ
0040C3F7 . |68 A8CE4300 push 0043CEA8 ; ASCII "mlogo.bmp"
0040C3FC . |52 push edx ; NME中的文件名
0040C3FD . |E8 EF5F0100 call 004223F1 ; Find Obj
0040C402 . |83C4 08 add esp, 8
0040C405 . |85C0 test eax, eax ; 0:found
0040C407 . |0F85 B9000000 jnz 0040C4C6
0040C40D . |68 B8D04300 push 0043D0B8 ; ASCII "parts"
0040C412 . |68 3C984300 push 0043983C ; ASCII "%s.det"
0040C417 . |68 B0CF4300 push 0043CFB0 ; ASCII "parts.nme"
0040C41C . |FFD7 call edi ; wspintf
0040C41E . |83C4 0C add esp, 0C
0040C421 > |6A 00 push 0
0040C423 . |68 80000000 push 80
0040C428 . |6A 03 push 3
0040C42A . |6A 00 push 0
0040C42C . |6A 00 push 0
0040C42E . |68 00000080 push 80000000
0040C433 . |68 B0CF4300 push 0043CFB0 ; ASCII "parts.nme"
0040C438 . |FFD5 call ebp ; open this file
0040C43A . |6A 00 push 0 ; /Origin = FILE_BEGIN
0040C43C . |8BF8 mov edi, eax ; |
0040C43E . |8B43 FC mov eax, dword ptr [ebx-4] ; |
0040C441 . |6A 00 push 0 ; |pOffsetHi = NULL
0040C443 . |50 push eax ; |OffsetLo
0040C444 . |57 push edi ; |hFile
0040C445 . |FF15 0C014300 call dword ptr [<&KERNEL32.SetFilePoi>; /SetFilePointer
0040C44B . |8B33 mov esi, dword ptr [ebx]
0040C44D . |56 push esi
0040C44E . |E8 1A5A0100 call 00421E6D ; new
0040C453 . |83C4 04 add esp, 4
0040C456 . |6A 00 push 0 ; /pOverlapped = NULL
0040C458 . |8D4C24 2C lea ecx, dword ptr [esp+2C] ; |
0040C45C . |51 push ecx ; |pBytesRead
0040C45D . |56 push esi ; |BytesToRead
0040C45E . |8BE8 mov ebp, eax ; |
0040C460 . |55 push ebp ; |Buffer
0040C461 . |57 push edi ; |hFile
0040C462 . |FF15 10014300 call dword ptr [<&KERNEL32.ReadFile>] ; /ReadFile
0040C468 . |57 push edi ; /hObject
0040C469 . |FF15 D0004300 call dword ptr [<&KERNEL32.CloseHandl>; /CloseHandle
查到mlog.bmp时,除了返回TRUE,还需要一些相关信息。这些信息可能记录在内存中。或一个对象中
要么是OBJ对象。要么是全局变量。所以需要跟踪 call 004223F1
发现内部复杂,找不到有用的东西。暂时不管它。
我们又发现在不断的查找过程当中有个值在累加。是EBX
分析后面的代码发现:
0040C4C6 > /8B4424 18 mov eax, dword ptr [esp+18]
0040C4CA . 8B4C24 20 mov ecx, dword ptr [esp+20]
0040C4CE . 40 inc eax
0040C4CF . 83C3 14 add ebx, 14
0040C4D2 . 3BC1 cmp eax, ecx
0040C4D4 . 894424 18 mov dword ptr [esp+18], eax
0040C4D8 .^ 0F8C 12FFFFFF jl 0040C3F0
这说明信息并不是通过call 4223f1来得到的,而只是简单的一一对应方式。
mblog.bmp是parts.nme中的第6个,按0算,就是5*14h=64h; 14h一定是BMP的信息结构。
64h+8h是在parts.atm中的位置。也就是说,ATM文件中存放的是DEC文件中资源的定位信息。
parts.atm中头8个字节(值为0)可能有另用。
我们猜想接下来就是查看parts.det,并得到mlog.bmp对应的资源数据。
0040C40D . 68 B8D04300 push 0043D0B8 ; ASCII "parts"
0040C412 . 68 3C984300 push 0043983C ; ASCII "%s.det"
0040C417 . 68 B0CF4300 push 0043CFB0 ; ASCII "parts.det"
0040C41C . FFD7 call edi
0040C41E . 83C4 0C add esp, 0C
0040C421 > 6A 00 push 0
0040C423 . 68 80000000 push 80
0040C428 . 6A 03 push 3
0040C42A . 6A 00 push 0
0040C42C . 6A 00 push 0
0040C42E . 68 00000080 push 80000000
0040C433 . 68 B0CF4300 push 0043CFB0 ; ASCII "parts.det"
0040C438 . FFD5 call ebp ;CreateFile
0040C43A . 6A 00 push 0 ; /Origin = FILE_BEGIN
0040C43C . 8BF8 mov edi, eax ; hFile
0040C43E . 8B43 FC mov eax, dword ptr [ebx-4] ; 定位点 28f9d
0040C441 . 6A 00 push 0 ; |pOffsetHi = NULL
0040C443 . 50 push eax ; |OffsetLo
0040C444 . 57 push edi ; |hFile
0040C445 . FF15 0C014300 call dword ptr [<&KERNEL32.SetF>; /SetFilePointer
0040C44B . 8B33 mov esi, dword ptr [ebx] ; SizeOfFile 7179
0040C44D . 56 push esi
0040C44E . E8 1A5A0100 call 00421E6D ; new
0040C453 . 83C4 04 add esp, 4
0040C456 . 6A 00 push 0 ; /pOverlapped = NULL
0040C458 . 8D4C24 2C lea ecx, dword ptr [esp+2C] ; |
0040C45C . 51 push ecx ; |pBytesRead
0040C45D . 56 push esi ; |BytesToRead
0040C45E . 8BE8 mov ebp, eax ; |
0040C460 . 55 push ebp ; |Buffer
0040C461 . 57 push edi ; |hFile
0040C462 . FF15 10014300 call dword ptr [<&KERNEL32.Read>; /ReadFile
0040C468 . 57 push edi ; /hObject
0040C469 . FF15 D0004300 call dword ptr [<&KERNEL32.Clos>; /CloseHandle
得到的BUFFER中就是BMP对应的加密数据。
试着构造下资源的结构:
struct BmpVerData //加密的BMP数据
{
DWORD SizeOfBmpFile