调试加壳后Exe生成的Dump文件

之前处理外网崩溃最头痛的时莫过于拿到了崩溃的Dump但是VS对他无从下手,主要原因是加壳之后exe生成的Dump无法与未加壳的exe匹配,进而对不上符号文件,不能在源码级别调试。

直接汇编级调试Dump根据调用栈地址来判断具体调用的是哪个函数也是个方法,但来来回回跳转几层之后真是有点力不从心哇,下面给大家分享一段解决方案。

之前百度这个问题的时候看到这么一段内容:

VS调试Dump时是通过ModuleName、SizeOfImage、CheckSum、TimeDateStamp这四个属性进行匹配的,四项全部匹配才认为Exe与Dump是配套的

那么简单来说,哪怕Dump与Exe不配套,我们把这四个属性改成一一对应的,那岂不是可以强制匹配调试了?

对于PE文件而言,这四个属性很好弄。ModuleName就是文件名——XXX.exe或者XXX.dll。参者PE文件结构,TimeDateStamp在IMAGE_FILE_HEADER中、SizeOfImageI和CheckSum在MAGE_OPTIONAL_HEADER32中,对应着取出来就好。

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;  // 目标字段1
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
 
    //
    // NT additional fields.
    //
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;  // 目标字段2
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;     // 目标字段3
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

复杂的是Dump文件的结构,好不容易在一本软件调试的书里翻到一些内容。

Dump文件头是MINIDUMP_HEADER结构,_MINIDUMP_HEADER::NumberOfStreams表示Dump中Streams的个数,别的我们不关心我们只要找到StreamType为ModuleListStream的Stream就好。

typedef struct _MINIDUMP_HEADER {
    ULONG32 Signature;
    ULONG32 Version;
    ULONG32 NumberOfStreams;
    RVA StreamDirectoryRva;
    ULONG32 CheckSum;
    union {
        ULONG32 Reserved;
        ULONG32 TimeDateStamp;
    };
    ULONG64 Flags;
} MINIDUMP_HEADER, *PMINIDUMP_HEADER;

_MINIDUMP_HEADER::StreamDirectoryRva指向MINIDUMP_DIRECTORY*类型的数组,其长度就是_MINIDUMP_HEADER::NumberOfStreams。

遍历_MINIDUMP_HEADER::StreamDirectoryRva,找到_MINIDUMP_DIRECTORY::StreamType为ModuleListStream的那一项。

//
// The MINIDUMP_HEADER field StreamDirectoryRva points to 
// an array of MINIDUMP_DIRECTORY structures.
//
typedef struct _MINIDUMP_DIRECTORY {
    ULONG32 StreamType;
    MINIDUMP_LOCATION_DESCRIPTOR Location;
} MINIDUMP_DIRECTORY, *PMINIDUMP_DIRECTORY;

_MINIDUMP_DIRECTORY::Location记录着当前类型的Stream的位置及长度,读取数据段,强转为MINIDUMP_MODULE_LIST*类型。

_MINIDUMP_MODULE_LIST::NumberOfModules记录着Dump加载的所有模块的数量,模块的信息作为数组储存在_MINIDUMP_MODULE_LIST::Modules中,我们要修改的是exe的信息,一般来说不用去遍历_MINIDUMP_MODULE_LIST::Modules了,第一个元素就是。

//
// The minidump module list is a container for modules.
//
typedef struct _MINIDUMP_MODULE_LIST {
    ULONG32 NumberOfModules;
    MINIDUMP_MODULE Modules [ 0 ];  // 已加载模块
} MINIDUMP_MODULE_LIST, *PMINIDUMP_MODULE_LIST;

//
// The MINIDUMP_MODULE contains information about a
// a specific module. It includes the CheckSum and
// the TimeDateStamp for the module so the module
// can be reloaded during the analysis phase.
//
 
typedef struct _MINIDUMP_MODULE {
    ULONG64 BaseOfImage;
    ULONG32 SizeOfImage;    // 目标字段2
    ULONG32 CheckSum;       // 目标字段3
    ULONG32 TimeDateStamp;  // 目标字段1
    RVA ModuleNameRva;
    VS_FIXEDFILEINFO VersionInfo;
    MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
    MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
    ULONG64 Reserved0;                          // Reserved for future use.
    ULONG64 Reserved1;                          // Reserved for future use.
} MINIDUMP_MODULE, *PMINIDUMP_MODULE;

取出_MINIDUMP_MODULE_LIST::Modules[0],对应着上面取出来的Exe的信息把_MINIDUMP_MODULE::SizeOfImage、_MINIDUMP_MODULE::CheckSum、_MINIDUMP_MODULE::TimeDateStamp修改掉然后保存。

然后愉快的双击Dump按下调试,开始愉快的源码级调试吧~

题外话:改Dump的时候各类指针乱飞,最方便的做法是加载整个Dump进内存,就不用管部分加载计算偏移的事了,但是对于动辄800M+的游戏Dump文件,还要做批量修改,还是老老实实部分加载吧。

P.S. 如果崩溃点在你加壳代码混淆的部分内,放弃调试Dump吧,你啥也看不到 ╮(╯▽╰)╭。

搬运自toonyxm.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容