修改可执行文件时如何处理静态内存分配?

逆向工程 部件 反编译 调试 记忆 结构
2021-06-22 08:43:47

我不是 StackExchange 的新手,但我对逆向工程很陌生,所以请耐心等待!:P

目前我正在处理一个可执行文件,我想稍微修改一下,供个人使用;该应用程序的源代码不可用,所以我只能修改它或尝试死。我同时使用 IDA Pro 6.1 和 OllyDBG 2.0 作为工具。

确切的说,我只是想增加CFG_ENTRY的应用程序可以读取量5001000的方法ReadCfgFile其中,显然,在编译时预分配静态存储区:

.text:008F2860 ReadCfgFile     proc near ; DATA XREF: .rdata:0137A1ECo
.text:008F2860 var_20          = dword ptr -20h
.text:008F2860 var_1C          = dword ptr -1Ch
.text:008F2860 var_18          = dword ptr -18h
.text:008F2860 var_C           = dword ptr -0Ch
.text:008F2860 var_4           = dword ptr -4
.text:008F2860 arg_0           = dword ptr  4
.text:008F2860 arg_4           = byte ptr  8
.text:008F2860
.text:008F2860                 push    0FFFFFFFFh
.text:008F2862                 mov     eax, large fs:0
.text:008F2868                 push    offset sub_1288B18
.text:008F286D                 push    eax
.text:008F286E                 mov     large fs:0, esp
.text:008F2875                 sub     esp, 14h
.text:008F2878                 push    ebx
.text:008F2879                 push    ebp
.text:008F287A                 push    esi
.text:008F287B                 push    edi
.text:008F287C                 mov     edi, [esp+30h+arg_0]
.text:008F2880                 mov     eax, [edi+10h]
.text:008F2883                 cmp     eax, [edi+8]
.text:008F2886                 mov     esi, ecx
.text:008F2888                 jnb     loc_8F2930
.text:008F288E                 mov     edi, edi
.text:008F2890
.text:008F2890 loc_8F2890:
.text:008F2890                 mov     eax, [esi+79954h]
.text:008F2896                 cmp     eax, 3E8h
.text:008F289B                 jge     loc_8F29DF
.text:008F28A1                 mov     edx, eax
.text:008F28A3                 shl     edx, 5
.text:008F28A6                 lea     ecx, [eax+1]
.text:008F28A9                 sub     edx, eax
.text:008F28AB                 lea     eax, [eax+edx*8]
.text:008F28AE                 mov     [esi+79954h], ecx
.text:008F28B4                 push    edi
.text:008F28B5                 lea     ecx, [esi+eax*4+4]
.text:008F28B9                 call    ReadEDURecord
.text:008F28BE                 test    al, al
.text:008F28C0                 jz      loc_8F2947
.text:008F28C6                 mov     eax, [esi+79954h]
.text:008F28CC                 mov     ecx, eax
.text:008F28CE                 shl     ecx, 5
.text:008F28D1                 sub     ecx, eax
.text:008F28D3                 lea     edx, [eax+ecx*8]
.text:008F28D6                 mov     ebp, [esi+edx*4-3E0h]
.text:008F28DD                 lea     eax, [esp+30h+arg_0]
.text:008F28E1                 lea     ebx, [esi+79958h]
.text:008F28E7                 push    eax
.text:008F28E8                 mov     ecx, ebx
.text:008F28EA                 mov     [esp+34h+arg_0], ebp
.text:008F28EE                 call    sub_437DF0
.text:008F28F3                 test    eax, eax
.text:008F28F5                 jz      short loc_8F2902
.text:008F28F7                 cmp     [esp+30h+arg_4], 0
.text:008F28FC                 jz      loc_8F2994
.text:008F2902
.text:008F2902 loc_8F2902:
.text:008F2902                 mov     ecx, [esi+79954h]
.text:008F2908                 sub     ecx, 1
.text:008F290B                 lea     edx, [esp+30h+arg_0]
.text:008F290F                 push    edx
.text:008F2910                 lea     eax, [esp+34h+var_20]
.text:008F2914                 mov     [esp+34h+arg_0], ecx
.text:008F2918                 push    eax
.text:008F2919                 mov     ecx, ebx
.text:008F291B                 mov     [esp+38h+var_20], ebp
.text:008F291F                 call    sub_437890
.text:008F2924                 mov     ecx, [edi+10h]
.text:008F2927                 cmp     ecx, [edi+8]
.text:008F292A                 jb      loc_8F2890
.text:008F2930
.text:008F2930 loc_8F2930:
.text:008F2930                 pop     edi
.text:008F2931                 pop     esi
.text:008F2932                 pop     ebp
.text:008F2933                 mov     al, 1
.text:008F2935                 pop     ebx
.text:008F2936                 mov     ecx, [esp+20h+var_C]
.text:008F293A                 mov     large fs:0, ecx
.text:008F2941                 add     esp, 20h
.text:008F2944                 retn    8
.text:008F2947
.text:008F2947 loc_8F2947:
.text:008F2947                 push    0
.text:008F2949                 call    sub_D386E0
.text:008F294E                 add     esp, 4
.text:008F2951                 mov     ecx, edi
.text:008F2953                 mov     esi, eax
.text:008F2955                 call    sub_D4D270
.text:008F295A                 push    eax ; ArgList
.text:008F295B                 push    offset aErrMsg_1 ; Error Message
.text:008F2960                 call    sub_D386E0
.text:008F2965                 add     esp, 8
.text:008F2968                 call    sub_D388C0
.text:008F296D                 lea     edx, [esp+30h+var_20]
.text:008F2971                 push    edx
.text:008F2972                 lea     ecx, [esp+34h+var_18]
.text:008F2976                 mov     [esp+34h+var_20], eax
.text:008F297A                 mov     [esp+34h+var_1C], 640h
.text:008F2982                 call    sub_403D60
.text:008F2987                 mov     [esp+30h+var_4], 0
.text:008F298F                 jmp     loc_8F2A27
.text:008F2994
.text:008F2994 loc_8F2994:
.text:008F2994                 push    0
.text:008F2996                 call    sub_D386E0
.text:008F299B                 add     esp, 4
.text:008F299E                 mov     ecx, edi
.text:008F29A0                 mov     esi, eax
.text:008F29A2                 call    sub_D4D270
.text:008F29A7                 push    eax
.text:008F29A8                 push    ebp             ; ArgList
.text:008F29A9                 push    offset aErrMsg_2 ; Error Message
.text:008F29AE                 call    sub_D386E0
.text:008F29B3                 add     esp, 0Ch
.text:008F29B6                 call    sub_D388C0
.text:008F29BB                 mov     [esp+30h+var_20], eax
.text:008F29BF                 lea     eax, [esp+30h+var_20]
.text:008F29C3                 push    eax
.text:008F29C4                 lea     ecx, [esp+34h+var_18]
.text:008F29C8                 mov     [esp+34h+var_1C], 640h
.text:008F29D0                 call    sub_403D60
.text:008F29D5                 mov     [esp+30h+var_4], 1
.text:008F29DD                 jmp     short loc_8F2A27
.text:008F29DF
.text:008F29DF loc_8F29DF:
.text:008F29DF                 push    0
.text:008F29E1                 call    sub_D386E0
.text:008F29E6                 add     esp, 4
.text:008F29E9                 mov     ecx, edi
.text:008F29EB                 mov     esi, eax
.text:008F29ED                 call    sub_D4D270
.text:008F29F2                 push    eax             ; ArgList
.text:008F29F3                 push    offset aErrMsg_0 ; Error Message
.text:008F29F8                 call    sub_D386E0
.text:008F29FD                 add     esp, 8
.text:008F2A00                 call    sub_D388C0
.text:008F2A05                 lea     ecx, [esp+30h+var_20]
.text:008F2A09                 push    ecx
.text:008F2A0A                 lea     ecx, [esp+34h+var_18]
.text:008F2A0E                 mov     [esp+34h+var_20], eax
.text:008F2A12                 mov     [esp+34h+var_1C], 640h
.text:008F2A1A                 call    sub_403D60
.text:008F2A1F                 mov     [esp+30h+var_4], 2
.text:008F2A27
.text:008F2A27 loc_8F2A27:
.text:008F2A27                 mov     eax, [esp+30h+var_18]
.text:008F2A2B                 test    eax, eax
.text:008F2A2D                 jz      short loc_8F2A3A
.text:008F2A2F                 push    esi
.text:008F2A30                 push    eax
.text:008F2A31                 call    ds:??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const *)
.text:008F2A37                 add     esp, 8
.text:008F2A3A
.text:008F2A3A loc_8F2A3A:
.text:008F2A3A                 lea     ecx, [esp+30h+var_18]
.text:008F2A3E                 mov     [esp+30h+var_4], 0FFFFFFFFh
.text:008F2A46                 call    sub_403DF0
.text:008F2A4B                 mov     ecx, [esp+30h+var_C]
.text:008F2A4F                 pop     edi
.text:008F2A50                 pop     esi
.text:008F2A51                 pop     ebp
.text:008F2A52                 xor     al, al
.text:008F2A54                 pop     ebx
.text:008F2A55                 mov     large fs:0, ecx
.text:008F2A5C                 add     esp, 20h
.text:008F2A5F                 retn    8
.text:008F2A5F ReadCfgFile     endp

[编辑 1 - 从一开始我就应该知道的一切!]

在遵循@sealed ... 的答案的建议之后,我使用类检查器来检测虚拟函数表,并找到了完整的类描述符。嗯...实际上有两个类引用了我的目标方法,ReadCfgFile并且在整个可执行文件中没有直接调用它:

.rdata:0137A1D4 ; class DATABASE_TABLE<CFG_ENTRY,500,unsigned int> [SI] O: 0, A: 0
.rdata:0137A1D4 dd offset ??_R4?$DATABASE_TABLE@UCFG_ENTRY@@$0BPE@I@@6B@ ; RTTI Complete Object Locator
.rdata:0137A1D8 ; const DATABASE_TABLE<struct CFG_ENTRY,500,unsigned int> VF Table
.rdata:0137A1D8 ??_7?$DATABASE_TABLE@UCFG_ENTRY@@$0BPE@I@@6B@ dd offset sub_8EF0F0 ; DATA XREF: sub_8EEFC0+1Do
.rdata:0137A1DC dd offset nullsub_648
.rdata:0137A1E0 dd offset sub_8EAB30
.rdata:0137A1E4 dd offset sub_8EF060
.rdata:0137A1E8 dd offset sub_8EE500
.rdata:0137A1EC dd offset ReadCfgFile

.rdata:0137A1F0 ; class CFG_DB: DATABASE_TABLE<CFG_ENTRY,500,unsigned int> [SI] O: 0, A: 0
.rdata:0137A1F0 dd offset ??_R4CFG_DB@@6B@ ; RTTI Complete Object Locator
.rdata:0137A1F4 ; const CFG_DB VFTable
.rdata:0137A1F4 ??_7UNIT_DB@@6B@ dd offset sub_8EF2B0 ; DATA XREF: sub_8EF290+8o
.rdata:0137A1F8 dd offset nullsub_648
.rdata:0137A1FC dd offset sub_8EAB30
.rdata:0137A200 dd offset sub_8EF060
.rdata:0137A204 dd offset sub_8EE8B0
.rdata:0137A208 dd offset ReadCfgFile
.rdata:0137A20C dd offset sub_8EE5D0

[编辑 2 - 冒险继续!耶!]

阅读@Guntram Blohm 的答案后,我进行了更多调查,以收集和分析他建议的数据。我做的第一件事是用 PEiD 分析可执行文件,这是我从中获得的信息:

Compiler: Microsoft Visual C++ 7.0 Method2 [Debug]
Entropy: 6.24 (Not Packed)
Linker Info: 7.10

当我在我的ReadCfgFile方法上设置断点时,这是我从 OllyDBG 堆栈中得到的:

CPU Stack
Address   Value      ASCII Comments
0018B2C0  [008EE644  D�.  ; RETURN to myapp.008EE644

并且008EE644是以下方法的一小部分,据我所知,它查找配置文件并启动例程以读取它,但没有显式调用ReadCfgFile(突出显示的偏移量):

.text:008EE5D0 sub_8EE5D0      proc near ; CODE XREF: sub_411B20+2CBp
.text:008EE5D0 var_41          = byte ptr -41h
.text:008EE5D0 var_40          = dword ptr -40h
.text:008EE5D0 var_3C          = dword ptr -3Ch
.text:008EE5D0 var_38          = byte ptr -38h
.text:008EE5D0 var_34          = dword ptr -34h
.text:008EE5D0 var_30          = dword ptr -30h
.text:008EE5D0 var_2C          = byte ptr -2Ch
.text:008EE5D0 var_C           = dword ptr -0Ch
.text:008EE5D0 var_4           = dword ptr -4
.text:008EE5D0
.text:008EE5D0                 push    0FFFFFFFFh
.text:008EE5D2                 push    offset SEH_8EE5D0
.text:008EE5D7                 mov     eax, large fs:0
.text:008EE5DD                 push    eax
.text:008EE5DE                 mov     large fs:0, esp
.text:008EE5E5                 sub     esp, 38h
.text:008EE5E8                 push    ebx
.text:008EE5E9                 push    ebp
.text:008EE5EA                 mov     ebx, ecx
.text:008EE5EC                 push    offset aCfgFile ; "application.cfg"
.text:008EE5F1                 mov     [esp+50h+var_30], ebx
.text:008EE5F5                 call    sub_41BD00
.text:008EE5FA                 add     esp, 4
.text:008EE5FD                 push    eax
.text:008EE5FE                 lea     ecx, [esp+50h+var_38]
.text:008EE602                 call    sub_F018E0
.text:008EE607                 xor     ebp, ebp
.text:008EE609                 push    ebp
.text:008EE60A                 lea     eax, [esp+50h+var_38]
.text:008EE60E                 push    eax
.text:008EE60F                 lea     ecx, [esp+54h+var_2C]
.text:008EE613                 mov     [esp+54h+var_4], ebp
.text:008EE617                 call    sub_D50170
.text:008EE61C                 lea     ecx, [esp+4Ch+var_38] ; void *
.text:008EE620                 mov     byte ptr [esp+4Ch+var_4], 2
.text:008EE625                 call    sub_EFFE30
.text:008EE62A                 mov     edx, [ebx]
.text:008EE62C                 mov     ecx, ebx
.text:008EE62E                 mov     [ebx+7A938h], ebp
.text:008EE634                 call    dword ptr [edx+4]
.text:008EE637                 mov     eax, [ebx]
.text:008EE639                 push    ebp
.text:008EE63A                 lea     ecx, [esp+50h+var_2C]
.text:008EE63E                 push    ecx
.text:008EE63F                 mov     ecx, ebx
.text:008EE641                 call    dword ptr [eax+14h]
.text:008EE644 ; ---------------------------------------------------------------------------         
.text:008EE644                 test    al, al ; HERE IS THE STACK REFERENCE
.text:008EE644 ; ---------------------------------------------------------------------------   
.text:008EE646                 jnz     short loc_8EE66C
.text:008EE648                 lea     ecx, [esp+4Ch+var_2C]
.text:008EE64C                 mov     [esp+4Ch+var_4], 0FFFFFFFFh
.text:008EE654                 call    sub_D4BB30
.text:008EE659                 pop     ebp
.text:008EE65A                 xor     al, al
.text:008EE65C                 pop     ebx
.text:008EE65D                 mov     ecx, [esp+44h+var_C]
.text:008EE661                 mov     large fs:0, ecx
.text:008EE668                 add     esp, 44h
.text:008EE66B                 retn
.text:008EE66C
.text:008EE66C loc_8EE66C:
.text:008EE66C                 xor     edx, edx
.text:008EE66E                 or      eax, 0FFFFFFFFh
.text:008EE671                 mov     dword_1986644, edx
.text:008EE677                 mov     dword_1986650, eax
.text:008EE67C                 push    esi
.text:008EE67D                 mov     dword_1986648, edx
.text:008EE683                 mov     dword_1986654, eax
.text:008EE688                 push    edi
.text:008EE689                 mov     dword_198664C, edx
.text:008EE68F                 mov     dword_1986658, eax
.text:008EE694                 xor     edi, edi
.text:008EE696                 cmp     [ebx+79954h], ebp
.text:008EE69C                 jle     loc_8EE727
.text:008EE6A2                 lea     esi, [ebx+40h]
.text:008EE6A5                 jmp     short loc_8EE6B0
.text:008EE6A7                 align 10h
.text:008EE6B0
.text:008EE6B0 loc_8EE6B0:
.text:008EE6B0                 mov     eax, [esi]
.text:008EE6B2                 mov     cx, [esi+92h]
.text:008EE6B9                 lea     eax, ds:1986644h[eax*2]
.text:008EE6C0                 mov     ax, [eax]
.text:008EE6C3                 cmp     ax, cx
.text:008EE6C6                 jnb     short loc_8EE6CA
.text:008EE6C8                 mov     eax, ecx
.text:008EE6CA
.text:008EE6CA loc_8EE6CA:
.text:008EE6CA                 mov     ecx, [esi]
.text:008EE6CC                 mov     word ptr dword_1986644[ecx*2], ax
.text:008EE6D4                 mov     eax, [esi]
.text:008EE6D6                 mov     cx, [esi+92h]
.text:008EE6DD                 lea     eax, ds:1986650h[eax*2]
.text:008EE6E4                 mov     ax, [eax]
.text:008EE6E7                 cmp     ax, cx
.text:008EE6EA                 jb      short loc_8EE6EE
.text:008EE6EC                 mov     eax, ecx
.text:008EE6EE
.text:008EE6EE loc_8EE6EE:
.text:008EE6EE                 mov     edx, [esi]
.text:008EE6F0                 lea     ecx, [esi-3Ch]
.text:008EE6F3                 mov     word ptr dword_1986650[edx*2], ax
.text:008EE6FB                 call    sub_8ED600
.text:008EE700                 push    eax
.text:008EE701                 mov     eax, [ebx+7A938h]
.text:008EE707                 push    eax
.text:008EE708                 call    sub_F1E550
.text:008EE70D                 add     edi, 1
.text:008EE710                 add     esp, 8
.text:008EE713                 mov     [ebx+7A938h], eax
.text:008EE719                 add     esi, 3E4h
.text:008EE71F                 cmp     edi, [ebx+79954h]
.text:008EE725                 jl      short loc_8EE6B0
.text:008EE727
.text:008EE727 loc_8EE727:
.text:008EE727                 xor     esi, esi
.text:008EE729                 cmp     dword_1667290, ebp
.text:008EE72F                 mov     [esp+54h+var_3C], esi
.text:008EE733                 jbe     loc_8EE840
.text:008EE739                 lea     esp, [esp+0]
.text:008EE740
.text:008EE740 loc_8EE740:
.text:008EE740                 cmp     [ebx+79954h], ebp
.text:008EE746                 mov     [esp+54h+var_41], 0
.text:008EE74B                 mov     [esp+54h+var_40], ebp
.text:008EE74F                 mov     [esp+54h+var_34], ebp
.text:008EE753                 jle     loc_8EE7D9
.text:008EE759                 mov     ebp, 1
.text:008EE75E                 mov     ecx, esi
.text:008EE760                 shl     ebp, cl
.text:008EE762                 lea     edi, [ebx+3B0h]
.text:008EE768
.text:008EE768 loc_8EE768:
.text:008EE768                 cmp     [esp+54h+var_41], 0
.text:008EE76D                 jnz     short loc_8EE77F
.text:008EE76F                 test    [edi-2Ch], ebp
.text:008EE772                 jz      short loc_8EE77F
.text:008EE774                 test    byte ptr [edi+3], 20h
.text:008EE778                 jz      short loc_8EE77F
.text:008EE77A                 mov     [esp+54h+var_41], 1
.text:008EE77F
.text:008EE77F loc_8EE77F: 
.text:008EE77F                 xor     esi, esi
.text:008EE781                 xor     eax, eax
.text:008EE783
.text:008EE783 loc_8EE783:
.text:008EE783                 mov     ecx, [edi-24h]
.text:008EE786                 test    [eax+ecx], ebp
.text:008EE789                 jz      short loc_8EE7AF
.text:008EE78B                 cmp     eax, 10h
.text:008EE78E                 jnb     loc_8EE89B
.text:008EE794                 mov     ecx, esi
.text:008EE796                 shr     ecx, 5
.text:008EE799                 lea     edx, [esp+ecx*4+54h+var_40]
.text:008EE79D                 mov     ecx, esi
.text:008EE79F                 and     ecx, 1Fh
.text:008EE7A2                 mov     ebx, 1
.text:008EE7A7                 shl     ebx, cl
.text:008EE7A9                 or      [edx], ebx
.text:008EE7AB                 mov     ebx, [esp+54h+var_30]
.text:008EE7AF
.text:008EE7AF loc_8EE7AF:
.text:008EE7AF                 add     eax, 4
.text:008EE7B2                 add     esi, 1
.text:008EE7B5                 cmp     eax, 10h
.text:008EE7B8                 jb      short loc_8EE783
.text:008EE7BA                 mov     eax, [esp+54h+var_34]
.text:008EE7BE                 add     eax, 1
.text:008EE7C1                 add     edi, 3E4h
.text:008EE7C7                 cmp     eax, [ebx+79954h]
.text:008EE7CD                 mov     [esp+54h+var_34], eax
.text:008EE7D1                 jl      short loc_8EE768
.text:008EE7D3                 mov     esi, [esp+54h+var_3C]
.text:008EE7D7                 xor     ebp, ebp
.text:008EE7D9
.text:008EE7D9 loc_8EE7D9:
.text:008EE7D9                 push    esi
.text:008EE7DA                 call    sub_8D1490
.text:008EE7DF                 mov     edi, eax
.text:008EE7E1                 add     esp, 4
.text:008EE7E4                 cmp     byte ptr [edi+0BCh], 0
.text:008EE7EB                 jz      short loc_8EE82D
.text:008EE7ED                 xor     esi, esi
.text:008EE7EF                 cmp     esi, 4
.text:008EE7F2                 jnb     loc_8EE8A4
.text:008EE7F8
.text:008EE7F8 loc_8EE7F8:
.text:008EE7F8                 mov     ecx, esi
.text:008EE7FA                 and     ecx, 1Fh
.text:008EE7FD                 mov     edx, 1
.text:008EE802                 shl     edx, cl
.text:008EE804                 mov     ecx, esi
.text:008EE806                 shr     ecx, 5
.text:008EE809                 add     ecx, ecx
.text:008EE80B                 add     ecx, ecx
.text:008EE80D                 test    [esp+ecx+54h+var_40], edx
.text:008EE811                 setnz   al
.text:008EE814                 test    al, al
.text:008EE816                 jnz     short loc_8EE821
.text:008EE818                 not     edx
.text:008EE81A                 and     [ecx+edi+0C0h], edx
.text:008EE821
.text:008EE821 loc_8EE821:
.text:008EE821                 add     esi, 1
.text:008EE824                 cmp     esi, 4
.text:008EE827                 jb      short loc_8EE7F8
.text:008EE829                 mov     esi, [esp+54h+var_3C]
.text:008EE82D
.text:008EE82D loc_8EE82D:
.text:008EE82D                 add     esi, 1
.text:008EE830                 cmp     esi, dword_1667290
.text:008EE836                 mov     [esp+54h+var_3C], esi
.text:008EE83A                 jb      loc_8EE740
.text:008EE840
.text:008EE840 loc_8EE840:
.text:008EE840                 xor     esi, esi
.text:008EE842                 cmp     [ebx+79954h], ebp
.text:008EE848                 jle     short loc_8EE875
.text:008EE84A                 lea     edi, [ebx+108h]
.text:008EE850
.text:008EE850 loc_8EE850:
.text:008EE850                 mov     eax, [edi]
.text:008EE852                 mov     ecx, dword_16E9DC8
.text:008EE858                 push    eax ; Str2
.text:008EE859                 add     ecx, 84h
.text:008EE85F                 call    sub_10E86C0
.text:008EE864                 add     esi, 1
.text:008EE867                 add     edi, 3E4h
.text:008EE86D                 cmp     esi, [ebx+79954h]
.text:008EE873                 jl      short loc_8EE850
.text:008EE875
.text:008EE875 loc_8EE875:
.text:008EE875                 lea     ecx, [esp+54h+var_2C]
.text:008EE879                 mov     [esp+54h+var_4], 0FFFFFFFFh
.text:008EE881                 call    sub_D4BB30
.text:008EE886                 mov     ecx, [esp+54h+var_C]
.text:008EE88A                 pop     edi
.text:008EE88B                 pop     esi
.text:008EE88C                 pop     ebp
.text:008EE88D                 mov     al, 1
.text:008EE88F                 pop     ebx
.text:008EE890                 mov     large fs:0, ecx
.text:008EE897                 add     esp, 44h
.text:008EE89A                 retn
.text:008EE89B
.text:008EE89B loc_8EE89B:
.text:008EE89B                 lea     ecx, [esp+54h+var_40]
.text:008EE89F                 jmp     sub_8D0FE0
.text:008EE8A4
.text:008EE8A4 loc_8EE8A4:
.text:008EE8A4                 lea     ecx, [esp+54h+var_40]
.text:008EE8A8                 jmp     sub_8D0FE0
.text:008EE8A8 sub_8EE5D0      endp

该,我挖了一点找到CFG_DB构造函数,它看起来像这样(来自 IDA Pro 的伪代码):

void __thiscall sub_8EEFC0(void *this)
{
  void *v1 = this; // esi@1

  *(_DWORD *)this = &DATABASE_TABLE<CFG_ENTRY_500_unsigned_int>::_vftable_;

  sub_8EE500((int)this);
  sub_8EC030((char *)v1 + 502036);

  if ( *((_DWORD *)v1 + 124503) )
    operator delete__(*((void **)v1 + 124503));

  *((_DWORD *)v1 + 124503) = 0;

  unknown_libname_2673((char *)v1 + 4, 0x3E4u, 500, sub_8EEA00);
}

所以看起来“数组”CFG_ENTRY正在被实例化,调用属于链接到可执行文件的另一个库的方法。最后,我在ReadCfgFile方法的开头放置了一个断点,以查看传递给它的指针是否有任何帮助:

.text:008F287A                 push esi ==> esi = 00400000
[...]                          [...]
.text:008F2886                 mov esi, ecx ==> ecx = myapp.0190BD08

按照地址,0190BD08我偶然发现了这个:

.data:0190BD08 unk_190BD08     db    ? ; ; DATA XREF: sub_40FFF0:loc_410049o
.data:0190BD08                           ; sub_40FFF0:loc_410053o ...

.text:00410049 loc_410049: ; DATA XREF: .rdata:01484034o
.text:00410049 ; .rdata:0148489Co ...
.text:00410049                 mov     ecx, offset unk_190BD08

.text:00410053
.text:00410053 loc_410053: ; DATA XREF: .rdata:01484078o
.text:00410053 ; .rdata:01484C3Co ...
.text:00410053                 mov     ecx, offset unk_190BD08

这对我来说似乎是一个死胡同...

2个回答

在我看来,问题比您描述的要严重一些,而且没有简单的方法可以解决它。

首先,@sealed 似乎是正确的,该函数是一个类方法,并且您的编译器将类指针传递给ecx,因为这是唯一的方法

.text:008F28B5                 lea     ecx, [esi+eax*4+4]
.text:008F28B9                 call    ReadCfgEntry

有意义 - 其他参数被压入堆栈,并且 eax、edx 和 ebx 的值似乎不是很有意义,因此编译器不使用某种寄存器中的参数 fastcall abi。

现在,让我对从循环开始到调用的语句重新排序(不改变含义,只是为了更清楚地说明发生了什么)。编译器在其他指令之间传播“将计数加一”指令,可能是为了利用处理器内的流水线:

-- get the current "number of config entries" count, and abort the loop if it exceeds 500

.text:008F2890                 mov     eax, [esi+79954h]
.text:008F2896                 cmp     eax, 1F4h
.text:008F289B                 jge     loc_8F29DF

-- increment the count by one

.text:008F28A6                 lea     ecx, [eax+1]
.text:008F28AE                 mov     [esi+79954h], ecx

-- eax = (( count << 5 - count ) * 8 ) + count = count*249
.text:008F28A1                 mov     edx, eax
.text:008F28A3                 shl     edx, 5
.text:008F28A9                 sub     edx, eax
.text:008F28AB                 lea     eax, [eax+edx*8]

-- push edi, which is a parameter to this function, on the stack;
-- pass this = esi+4*eax+4  == esi+996*count+4 in ecx. Remember esi was set to
-- `this`=`ecx` at the start of the current method and hasn't been changed.

.text:008F28B4                 push    edi
.text:008F28B5                 lea     ecx, [esi+eax*4+4]
.text:008F28B9                 call    ReadCfgEntry

这似乎 ReadCfgEntry 也是一个类方法,它this在 cx 中获取其指针。从数组索引的计算方式来看,我假设原始 C++ 类如下所示:

class Configuration {
    int whatever;
    ConfigurationEntry entries[500];
    ....
}

ConfigurationEntry 是一个 996 字节的类。现在,坏消息是:这两个类成员需要 4+(500*996) 个字节。这等于 498004 或 0x79954。因此,您的条目计数直接位于 0x79954 处的条目后面,而另一个变量位于 0x79958:

class Configuration {
    int whatever;
    ConfigurationEntry entries[500];
    int entryCount;
    int somethingelse;
    ... ??? ...
}

现在,如果条目是一个指针并用 分配new,那么将 new 中的 size 参数从 500 修改为 1000 会更容易。但在您的情况下,您必须修改 Configuration 类的新方法,并且您还必须修改对配置条目“后面”变量的所有引用。您已经提到了偏移量 0x79954 处的计数变量和偏移量 0x79958 处的下一个变量,但可能有更多的变量没有在您的阅读器函数中被引用,因此您将很难找到所有这些变量。


当我看到你对问题的编辑时,我正要发布这个。

正如您在编辑中意识到的那样,您需要更改对要增加的条目数组后面的结构组件的所有访问。你需要在你的类方法中这样做(你可以很容易地找到,因为你有 vtables),但也在你的程序的其余部分中这样做(因为你不知道哪些原始类变量是公共的并且可以从在类本身之外)。不幸的是,您无法真正实现自动化,因为对于每次出现,比如 0x79954,您都必须检查它是否是您的类的索引或其他内容。

因为我不知道用哪个编译器来编写你的程序,所以我不能告诉你它是如何存储函数 vtable 的。幸运的是,第一个条目(我whatever之前调用的那个)是 vtable 指针。如果您使用调试器运行程序,您可以检查这一点,并检查whatever变量在到达 ReadConfigFile 方法时是否指向 vtable。如果是这样,这很好,因为我们在扩展结构时不必关心覆盖 vtable。

然后,您的类必须有一些分配器函数。由于您似乎有一个名为 DATABASE_TABLE 的类和一个名为 CFG_DB 的派生类,因此第二个类可能更大。尝试找到第二个更大的类的初始化方法。它应该调用new或类似的内存分配器,其大小适合结构大小(因此它可能介于 79960h 和 79a00h 之间),并且它应该将 vtable 指针移动到新分配的内存中,因此您可能能够找到它检查 vtable 的交叉引用。或者,使用调试器并在 ReadConfigFile 上设置断点并检查调用它的堆栈,很可能找到一个首先分配类实例的函数,然后调用其 ReadConfigFile 成员函数。

在找到类的实例化位置后,我可能会尽量不将数组大小从 500 增加到 1000,而是在结构后面分配一个更大的数组。例如,如果您当前的函数分配了 79a00h 字节,则向其中添加 1000 个条目所需的 996000d 字节,从而得到 16cca0h 字节。然后,更改

lea ecx, [esi+eax*4+4]

在 ReadCfgEntry 前面

lea ecx, [esi+eax*4+16cca0h]

这样,您就在当前结构后面创建了另一个数组而不是扩展当前结构。这意味着您的结构偏移量,除了配置项本身,都没有改变

在 C++ 中,我们只是将类更改为

class Configuration {
    int whatever;
    ConfigurationEntry entries[500];
    int entryCount;
    int somethingelse;
    ... ??? ...
    ConfigurationEntry newEntries[1000];
}

在下一步中,我们必须重写所有访问过的entries以使用newentries.

检查您的成员函数是否访问原始数组,并替换它们。最简单的方法可能是

  • 用调试器启动程序
  • 在您之前确定的分配结构的函数上设置断点
  • 分配结构后,在(结构地址+4)上设置硬件断点
  • 继续执行程序,只要遇到硬件断点,就会找到匹配项

由于硬件断点位于条目 [0] 上,因此对条目的每次访问都有可能在某个时间点到达它的第 0 个。

此外,由于 ReadCfgEntry 可能是一个类方法,很有可能在某处有一个循环,它只为每个条目分配一个类实例 - 类似于

for (i=0; i<500; i++) {
    entries[i]=new ConfigurationEntry()
}

您的硬件断点应该很快捕获这个循环。修补可执行文件以将 500 更改为 1000,并将条目 [i] 计算更改为您的新数组。之后,您的新数组将被初始化,但旧数组将只保存 NULL 指针。这意味着,将来您可能会通过这些 NULL 指针获得无效的内存访问,这也有助于识别对原始数组(您可以修补)的访问。


编辑 - 阅读 OP 的第二个答案后进行编辑

死路?完全没有,您收集并发布了非常有价值的信息。

首先,您的 CFG_DB 构造函数的伪代码

void __thiscall sub_8EEFC0(void *this)
{
  void *v1 = this; // esi@1
  *(_DWORD *)this = &DATABASE_TABLE<CFG_ENTRY_500_unsigned_int>::_vftable_;

确认类结构体开头的4个字节实际上是指向类的虚函数表的指针。

其次,你的片段

.text:008EE639                 push    ebp
.text:008EE63A                 lea     ecx, [esp+50h+var_2C]
.text:008EE63E                 push    ecx
.text:008EE63F                 mov     ecx, ebx
.text:008EE637                 mov     eax, [ebx]
.text:008EE641                 call    dword ptr [eax+14h]
.text:008EE644 ;     ---------------------------------------------------------------------------         
.text:008EE644                 test    al, al ; HERE IS THE STACK REFERENCE

非常适合。(同样,我重新排列了汇编指令的顺序,这种方式不会改变它们的作用,但可以更清晰地理解它们)。还记得你的 vtable 有一个函数偏移量、一个 nullsub、3 个更多的函数偏移量,以及ReadCfgFile它的条目吗?由于每个都有 4 个字节,因此 vtable 中 ReadCfgFile 函数指针的偏移量为 20,即 14h。在那个代码片段中,ebx 是一个类指针;mov eax, [ebx]从类指针中获取 vtable 指针,并call dword ptr [eax+14h]在该偏移量处调用函数,即ReadCfgFile在此之前,它将this寄存器(ecx)初始化为ebx,并将2个参数压入堆栈。这似乎是对类方法的非常标准的调用。

接下来,您的构造函数以

unknown_libname_2673((char *)v1 + 4, 0x3E4u, 500, sub_8EEA00);

第一个参数 (v1+4) 是 Configuration 类中 ConfigurationEntries 数组的地址(我保留我旧发明的变量/类名),第二个参数 (0x3e4 == 996) 是每个数组条目的大小;第三个 (500) 条目数,以及一个回调函数。我几乎可以肯定这是各个 ConfigurationEntries 的构造函数。这意味着这是我说您需要找到的功能,并且应该将其更改为

unknown_libname_2673((char *)v1 + XXXXX, 0x3E4u, 1000, sub_8EEA00);

一旦我们为它分配空间,XXXXX 就是 newEntries 数组的偏移量。

接下来,重新考虑间接调用 ReadConfigFile 之前的代码片段的一部分,

.text:008EE62A                 mov     edx, [ebx]
.text:008EE62C                 mov     ecx, ebx
.text:008EE62E                 mov     [ebx+7A938h], ebp
.text:008EE634                 call    dword ptr [edx+4]

我们看到有一个移动到ebx+7A938h, ebx 是类指针,所以在这个偏移处似乎有另一个类成员。在元素计数 (79954h) 的偏移量之后这是相当多的内存- 因此该结构具有更多组件。好在你没有试图改变它们。访问this+502036, or的构造函数this+0x7a914将是另一个提示。(它也访问this+124503,但由于this是一个双字指针,这意味着 498012 字节,仍然小于 502036)。

接下来,你找到了地址0190BD08,这是一件非常好的事情。连同外部参照和数据定义,

.data:0190BD08 unk_190BD08     db    ? 

这意味着:

该地址的类结构不是动态分配的,也没有初始化为任何东西,而是一个未初始化的全局变量。在 C++ 中,它可能是一个

static Configuration myConfig;

您看到的所有外部参照都是对 myConfig 的引用。由于这些地方的组装说明是

mov     ecx, offset unk_190BD08

我几乎可以肯定对类成员函数的调用是在每个指令之后的一两条指令。恭喜,您刚刚找到了一种方法来捕获访问配置的许多实例。为了验证这一点,unk_190BD08 是配置的全局变量,您可以在构造函数(sub_8EEFC0)上使用断点运行程序,我敢打赌它只被调用一次,并且 190BD08 是它的参数。

配置类实例是静态变量而不是 new() 实例化的一个不好的地方是,我们不能只在 new() 调用中增加大小。相反,我们必须将它移动到地址空间的一部分,在那里它不会干扰其他任何东西——在当前未初始化数据段的末尾。找到数据段中的最后一个条目,然后在其后面选择一个合适的地址,并将所有外部参照重写为 unk_190BD08 到该新地址。然后,使用 ollydbg 运行程序,在 190BD08 上放置一个硬件断点以防万一,并检查您知道是类成员函数的函数和初始化的函数,是否都获得了新地址而不是 190BD08 作为它们的ecx(此)参数。完成后,您就可以实施第二部分了,this+4this+XXXXXXXX班级人数。

我们缺少对 Configuration 变量的 new() 调用这一事实意味着我们无法使用其参数来获取类大小。但是我们已经知道类的大小至少是 7A938h,所以静态变量占用的地址空间从 0x190BD08 到至少 0x1986640。幸运的是,您的 .data 段具有 unk_190BD08 标签,下一个标签位于 0x1986640 之后的地址,因此下一个标签是一些其他变量,两个标签之间的差异是 Configuration 实例的大小。

还有一件事要做 - 当您将配置变量移到其他所有内容之后时,您还必须增加 PE 文件中该数据段的大小。不幸的是,我在 windows PE 文件格式方面不是那么熟练,所以我必须做很多研究如何自己正确地做到这一点,所以也许你很幸运,其他人在这方面的经验比我有,可以帮你解决这个问题。


编辑 - 编辑以回答评论

  • 我如何计算 CFG_DB 类有多大以便在它的末尾附加一些东西?

原来的 C++ 程序会有类似的东西

int somevariable;
int someothervariable;
CFG_DB globalConfig;
int athirdvariable;

(以 int 为例,数据类型可以是任何类型)并且编译器应该将所有这些变量放入 .data 段的未初始化部分。对其中一个变量的任何访问都会使 IDA 在该内存地址处创建一个标签,并带有指向访问位置的 XREF。所以,如果你的 .data 转储看起来像这样

.data:0190BD08 unk_190BD08     db    ?        <-- along with some xrefs 
.data:0190BD09                 db    ?        <-- this repeats a lot of times
.data:01986670 unk_1986670     db    ?        <-- along with some xrefs 

并且您知道 0190BD08 是您的globalConfig,那么 01986670 是athirdvariable,并且globalConfig的大小是 0x01986670 - 0x0190BD08 = 0x7a968 = 502120 字节。

这不是 100% 万无一失的,因为如果有任何东西访问 globalConfig.somemember,其中 somemember 的结构偏移量为 500000 (=0x7a120),那么 IDA 也会在 0x190BD08+0x7a120=0x1985E28 处生成一个标签和一个 XREF。但幸运的是,程序的“其余部分”只会使用 globalConfig 变量作为成员函数的参数,其中引用是间接的,因此 IDA 不会使用它们来创建标签。

  • 如果其他类/方法/等与这个修改后的类交互会发生什么?

如果他们访问除 500 个单独的配置条目之外的任何内容,则什么都不会,因为这些条目并没有改变它们的偏移量。可能危险的是当他们访问条目时,因为这些访问应该被重写为 newEntries。您必须找出它们的位置(如果幸运的话,那只是在成员函数中)并在那时修补代码。(旧)条目 [0] 地址(在您的情况下为 0x190BD0C,结构开始 + 4)上的硬件断点应该对此有所帮助,因为任何可能访问任何条目的内容也可能访问条目 [0] . 因此,如果您遇到硬件断点,例如,,mov eax, [ebx+4]那么您就知道ebx+4正在访问旧地址,并且应该重写mov eax, [ebx+XXXXX]以使用新数组。

不幸的是,您无法创建一个覆盖整个结构的硬件断点来捕获任何访问。这就是空指针异常开始的地方。如果在更改后,您的程序在通常不会抛出的地方抛出 NPE,则可能是因为“某事”访问了旧数组,该数组现在只包含 NULL,而不是新数组. 在调试器中捕获 NPE,并检查导致它的反汇编/调用堆栈,应该会让您了解从旧数组中读取 NULL 指针的位置,以便您知道要更改哪条指令以指向新的指令。

因为我不知道ReadCfgEntry的原型和代码的结构,所以我的回答并不准确。

第一:如果缓冲区是动态分配的

由于您的函数使用 ECX 作为初始化寄存器:

.text:008F2886                 mov     esi, ecx

它是一个类函数,因此所有者类具有启动目标缓冲区的构造函数。您必须跟踪缓冲区并增加分配大小。

这不是找到构造函数的确切方法;但有效的方法之一是找到虚拟功能表

  • 通过在函数启动时跟踪 ECX。大多数情况下,目标缓冲区的第一个元素中包含一个VFTable

  • 通过在 IDA 中的外部参照(正如你所做的那样)

    ReadCfgFile 仅在此处被调用:“.rdata:0137A1EC dd offset ReadCfgFile”...

找到VFTable 后,大多数时候构造函数是最顶层的成员。

第二:如果缓冲区是静态分配的

正如我之前提到的,该函数是类的成员,因此如果您更改感兴趣的指针;它会做的工作。

if (!ReadCfgEntry((void*)(pointer + (996 * entriesCount) + 4), v5))

您应该插入一个存根以分配适当的内存并将其分配给类。

最后:如果程序是封闭源代码,则意味着开发人员不喜欢其他人修改它以供个人使用……确保没有违反许可证!