高级文件格式逆向分析

本文详细介绍了如何通过逆向工程技术分析一个全日文小游戏中的图片文件,从创建文件、文件读取到解密过程,揭示了游戏如何处理BMP文件并进行解密的步骤。通过跟踪函数调用,尤其是StretchDIBits和CreateFile,作者发现了文件解密的入口,并解析了复杂的解码算法,展示了逆向工程在游戏文件分析中的应用。
摘要由CSDN通过智能技术生成

廫榋栭偺壴壟丒懱尡斉(一个全日文小游戏) 图片文件(扩展名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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值