在OllyDbg 中查看它似乎是一项繁重的任务。看起来像一个带有加密和(自定义?)压缩数据的自定义数据库。在这样的应用中通常就是这种情况。具有结构化数据的平面文件不属于该文件的一部分。
无论如何。作为初学者:
在尝试了一些通用压缩工具(如 7z 或 binwalk)(尚未测试)后,可以快速检查一下,可以使用来自 Sysinternals 的ProcMon。启动 ProcMon,然后启动您的应用程序并在 ProcMon 中的应用程序上设置过滤器。你很快就会发现:
简而言之,它读取不同大小的块,但对于主要数据处理,它读取 16384 字节的块。过程分步骤:
- 生成 256 个整数的种子图。(在应用程序启动时完成一次。)
 
- 循环:
2.1 从 .dat 文件中将 16384 字节读入缓冲区。
2.2 缓冲区上的异或例程使用偏移量和缓冲区的最后四个字节作为基数。
2.3 使用步骤 1 中的种子映射对 XOR 缓冲区进行校验和。
2.4 解析缓冲区并读出数据。 
应用程序还会多次读取相同的块。
2.1:
例子:
013D0010  D4 9E BE BF 1C 1C 0B D4 C5 E7 11 B5 09 48 87 FA  Ôž¾¿ÔÅçµ.H‡ú
013D0020  29 4C 03 C9 DE 4A 2B 71 74 7F D2 48 E7 13 94 4E  )LÉÞJ+qtÒHç”N
...
013D3FF0  6A D1 55 92 E2 16 60 53 69 89 86 7D D9 D8 10 BC  jÑU’â`Si‰†}ÙØ¼
013D4000  90 F3 D1 48 28 47 34 EC 39 36 EC 4D 69 2A 7D E5  óÑH(G4ì96ìMi*}å
                                             |_____._____|
                                                   | 
                         Last DWORD aka checksum --+
按发现顺序排列的步骤和详细信息:
将 .dat 文件拆分为 16384 字节的块,并生成每个文件的十六进制转储,以便于搜索和比较。说实话,我采用Linux对于这部分有dd,xxd -ps,grep,diff等。
启动 OllyDbg,打开应用,定位CreateFile并设置断点:
00401220   $-FF25 18825000  JMP DWORD PTR DS:[<&kernel32.CreateFileA>;  kernel32.CreateFileA
按F9直到文件名 (in EAX) 是 .dat 文件。在 上设置/启用断点ReadFile。F9阅读完成后,开始逐步查看完成的内容。
看着它:
2.2:
读取后,首先使用偏移量作为“魔术”修改缓冲区,从以下位置开始:
0045F5EC  /$ 53   PUSH EBX     ;  ALGO 2: XOR algorithm - post file read.
...
0045F6B6  \. C3   RETN         ;  ALGO 2: RETN
至少采取的两个操作似乎是libj_randl1()和libj_randl2()。(这将是上面列表中的步骤 2.2。)
简化:
edx = memory address of buffer
ecx = offset / 0x4000
edi = edx
ebx = ecx * 0x9b9
esi = last dword of buffer & 0x7fffffff
ecx = 0
i = 0;
while (i < 0x3ffc) { /* size of buffer - 4 */
    manipulate buffer
}
整个例程转换为 C 代码:
int xor_buf(uint8_t *buf, long offset, long buf_size)
{
    int32_t eax;
    int32_t ebx;
    int32_t esi;
    long i;
    buf_size -= 4;
    ebx = (offset / 0x4000) * 0x9b9;
    /* Intel int 32 */
    esi = (
        (buf[buf_size + 3] << 24) |
        (buf[buf_size + 2] << 16) |
        (buf[buf_size + 1] <<  8) |
         buf[buf_size + 0]
        ) & 0x7fffffff;
    for (i = 0; i < buf_size /*0x3ffc*/; ++i) {
        /* libj_randl2(sn) Ref. link above. */
        ebx = ((ebx % 0x0d1a4) * 0x9c4e) - ((ebx / 0x0d1a4) * 0x2fb3);
        if (ebx < 0) {
            ebx += 0x7fffffab;
        }
        /* libj_randl1(sn) Ref. link above. */
        esi = ((esi % 0x0ce26) * 0x9ef4) - ((esi / 0x0ce26) * 0x0ecf);
        if (esi < 0) {
            esi += 0x7fffff07;
        }
        eax = ebx - 0x7fffffab + esi;
        if (eax < 1) {
            eax += 0x7fffffaa;
        }
        /* Modify three next bytes. */    
        buf[i] ^= (eax >> 0x03) & 0xff;
        if (++i <= buf_size) {
            buf[i] ^= (eax >> 0x0d) & 0xff;
        }
        if (++i <= buf_size) {
            buf[i] ^= (eax >> 0x17) & 0xff;
        }
    }
    return 0;
}
然后生成结果缓冲区的校验和(减去最后一个 dword),并根据最后一个 dword 进行检查。这里它使用了一个来自 BSS 段的缓冲区,该缓冲区是在启动时生成的,步骤 1. 来自上面的列表。(偏移0x00505000+0x894并使用一个区域,4 * 0x100因为它是 256 个 32 位整数)。这个种子图似乎是恒定的(从不重新生成/更改),如果不想验证缓冲区可以跳过。
1.
反汇编中的代码点(我的评论):
0045E614 . 53   PUSH EBX           ;  ALGO 1: GENERATE CHECKSUM MAGICK BSS
...
0045E672 . C3   RETN               ;  ALGO 1: RETN
BSS 编号的代码可以简化为用 C 编写,例如:
int eax;    /* INT NR 1, next generated number to save */
int i, j;
unsigned int bss[0x100] = {0};  /* offset 00505894 */
for (i = 0; i < 0x100; ++i) {
    eax = i << 0x18;
    for (j = 0; j < 8; ++j) {
        if (eax & 0x80000000) {
            eax = (eax + eax) ^ 0x4c11db7;
        } else {
            eax <<= 1;
        }
    }
    bss[i] = eax;
}
2.3:
该 bss int 数组用于操作缓冲区以生成校验和,该校验和应等于从文件读取的 16384 字节中的最后一个整数。(最后一个双字,在校验和例程和异或运算中跳过的那个。)。这将是上面列表中的步骤 2.3。
unsigned char *buf = manipulated file buffer;
unsigned char *bss = memory dump 0x00505894 - 0x00505C90, or from code above
eax = 0x13d0010;  /* Memory location for buffer. */
edx = 0x3ffc;     /* Size of buffer - 4 bytes (checksum). */
...
退出时ecx等于校验和。
反汇编中的代码点(我的评论):
0045E5A8  /$ 53  PUSH EBX    ;  ALGO 3: CALCULATE CHECKSUM AFTER ALGORITHM 2
...
0045E5E0  \. C3  RETN        ;  ALGO 3: RETN (EAX=CHECKSUM == BUFFER LAST 4 BYTES)
缩短为 C 例程,它可能是这样的:
int32_t checksum(int32_t map[0x100], uint8_t *buf, long len)
{
    int i;
    int32_t k, cs = 0;
    for (i = 0; i < len; ++i) {
        k = (cs >> 0x18) & 0xff;
        cs = map[buf[i] ^ k] ^ (cs << 0x08);
    }
    return cs;
}
检查为 OK,然后缓冲区中的校验和设置为:两个最低有效字节 = 0,两个最高有效字节设置为某个数字(文件中的块号或读取号,(从 0 开始))。
0045F9BF   . C680 FC3F0000 >MOV BYTE PTR DS:[EAX+3FFC],0     ;  Set two lower bytes of checksum in dat buf to 0
0045F9C6   . C680 FD3F0000 >MOV BYTE PTR DS:[EAX+3FFD],0     ;  follows previous
0045F9CD   . 66:8B4D F8     MOV CX,WORD PTR SS:[EBP-8]       ;  Set CX to stack pointer value of addr EBP - 8 (counter of sorts)
0045F9D1   . 66:8988 FE3F00>MOV WORD PTR DS:[EAX+3FFE],CX    ;  Set .dat buffer higher bytes like CX.
现在完成所有这些之后,实际的数据复制开始于更多的算法。真正的工作开始了。识别数据类型、结构、位置和内容等。找到了一些提取名称等的例程。但是芬兰语的所有内容都无助于使其更容易掌握;)。
上面的数据可能是一个开始。
开始时可能会感兴趣的一些断点:
Breakpoints
Address    Module     Active    Disassembly  Comment
0045E5A8   Kirjaus5   Disabled  PUSH EBX     ALGO 3: CALCULATE CHECKSUM AFTER ALGORITHM 2
0045E5E0   Kirjaus5   Disabled  RETN         ALGO 3: RETN (EAX=CHECKSUM == BUFFER LAST 4 BYTES)
0045E614   Kirjaus5   Disabled  PUSH EBX     ALGO 1: GENERATE CHECKSUM MAGIC BSS
0045E672   Kirjaus5   Disabled  RETN         ALGO 1: RETN
0045F5EC   Kirjaus5   Disabled  PUSH EBX     ALGO 2: FILE POST XOR READ ALGORITHM
0045F6B6   Kirjaus5   Disabled  RETN         ALGO 2: RETN
一些注意事项:
保留您正在使用的 .dat 文件的备份。如果您中止应用程序,文件通常会被损坏,如@blabb所述,请将数据写回文件。.dat 文件似乎也处于活动状态,因此新下载它会产生不同的数据。