I’m not new to StackExchange, but I’m quite new to reverse engineering, so please be patient with me! 😛

At present I’m dealing with an executable that I would like to modify a little bit, for personal use; the source code of the application is not available so I can only modify it or die trying. I’m using both IDA Pro 6.1 and OllyDBG 2.0 as tools.

To be exact, I would just like to increase the amount of CFG_ENTRY that the application can read from 500 to 1000 in the method ReadCfgFile which, apparently, has a static memory region preallocated at compile-time:

.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

[EDIT 1 – All what I should have known since the beginning!]

After following the suggestions of the answer of @sealed…, I used a class inspector to detect the Virtual Function Table and I found the full class descriptor. Well… in fact there are two classes referring to my target method ReadCfgFile and no direct calls to it in the whole executable:

.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

[EDIT 2 – The adventure continues! Yay!]

After reading the answer of @Guntram Blohm, I investigated some more in order to collect and analyze the data he suggested. The first thing I did is to analyze the executable with PEiD and here is the information I got from it:

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

When I set a breakpoint on my ReadCfgFile method, here is what I get from OllyDBG stack:

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

And 008EE644 is a small part of the following method that, for what I can understand, looks for the configuration file and starts the routine for reading it, but without an explicit call to ReadCfgFile (offset highlighted):

.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

The, I digged a little bit more finding out the CFG_DB contructor, which looks like this (pseudo-code from 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);
}

So it looks like the “array” of CFG_ENTRY is being instantiated calling a method belonging to another library linked to the executable. Finally, I put a breakpoint at the beginning of my ReadCfgFile method in order to see if pointers passed to it can be of any help:

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

And following the address 0190BD08 I just stumbled upon this:

.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

It looks like a dead end to me…

shareimprove this question
I don’t see any references in your disassembly to a static buffer. Where’s the reference in the disassembly to the equivalent s_CfgEntries? –  Jason Geffner May 8 at 1:06
Well, as I said, I’m not really expert about disassembly, and the code I wrote is just a pseudo-code to see how the ASM could look like in human readable code. I can’t see any static buffer either but the metod ReadCfgFile is being called only here: “.rdata:0137A1EC dd offset ReadCfgFile”, “.rdata:0137A208 dd offset ReadCfgFile”. –  Zarathos May 8 at 1:14

2 Answers

It seems to me that the problem is a bit worse than you describe, and there’s no easy way to get around it.

First, @sealed seems to be right with the function being a class method, and your compiler passing the class pointer in ecx, because that’s the only way for

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

to make sense – other parameters are pushed on the stack, and the values of eax, edx and ebx seem not to be very meaningful, so the compiler doesn’t use some kind of parameters-in-registers fastcall abi.

Now, let me reorder the statements from the loop start to the call a bit (without changing the meaning, just to make a bit clearer what happens). The compiler spread the “increment the count by one” instructions between the others, probably to take advantage of pipelining within the processor:

-- 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

This seems like ReadCfgEntry is a class method as well, which gets its this pointer in cx. From the way the array index is calculated, i’d assume the original C++ class looked like this:

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

with ConfigurationEntry being a 996 byte class. Now, the bad news is: These two class members need 4+(500*996) bytes. This is equal to 498004, or 0x79954. So your entry count is directly behind the entries at 0x79954, with another variable at 0x79958:

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

Now, if the entries had been a pointer and allocated with new, it would have been easier to just modify the size parameter in that new from 500 to 1000. But in your case, you’d have to modify the new method of the Configuration class, and you’d have to modify ALL references to variables “behind” the configuration entries as well. You already mentioned that for the count variable at offset 0x79954, and the next variable at offset 0x79958, but there might be more of them that don’t get referenced in your reader function, so you’ll have a hard time finding them all.


I was just about to post this, when i saw your edit to the question.

As you realized in your edit, you need to change all accesses to structure components behind the array of entries that you want to increase. You need to do this in your class methods (which you can find easily as you have the vtables), but also in the rest of your program (as you don’t know which of the original class variables where public and might be accessed from outside the class itself). Unfortunately, you can’t really automate that, because, for every occurence of, say 0x79954, you’d have to check if it’s an index into your class, or something else.

Since i don’t know which compiler was used to write your program, i can’t tell much about how it stores the function vtable. With a bit of luck, the first entry (the one i called whatever earlier) is the vtable pointer. You can check this if you run the program with a debugger, and check if the whatever variable points to the vtable when it reaches the ReadConfigFile method. If it does, this is good, because we don’t have to care about overwriting the vtable when extending the structure.

Then, there must be some allocator function for your classes. Since you seem to have a class called DATABASE_TABLE, and a derived one called CFG_DB, the second one is probably larger. Try finding the initializing method of this second, larger class. It should call new or a similar memory-allocator, with a size that fits the structure size (so it’s probably somewhere between 79960h and 79a00h), and it should move the vtable pointer into the newly allocated memory, so you might be able to find it checking for cross-references of the vtable. Or, use a debugger and set a breakpoint on ReadConfigFile and check the stack for what called it, chances should be good you find a function that allocates the class instance first, then calls its ReadConfigFile member function.

After you find where the class is instantiated, i’d probably try not to increase the array size from 500 to 1000, but, instead, just allocate a larger array behind the structure. For example, if your current function allocates 79a00h bytes, add the 996000d bytes to it that 1000 entries need, resulting in 16cca0h bytes. Then, change the

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

in front of ReadCfgEntry to

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

That way, you’ve created another array behind the current structure instead of extending the current one. Which means none of your structure offsets, except the config items themselves, have changed.

Speaking in C++, we just changed the class to

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

and in the next step we have to re-write all accessed to entries to use newentries.

Check your member functions for accesses to the original array, and replace them as well. The most simple way to do this is probably

  • start the program with a debugger
  • set a breakpoint on the function you identified previously that allocates the structure
  • after the structure is allocated, set a hardware breakpoint on (structure address + 4)
  • continue the program, and whenever you hit the hardware breakpoints, you found a match

Since the hardware breakpoint is on entries[0], chances are good that every access to an entry hits the 0-th of it at some point.

Also, since ReadCfgEntry is probably a class method, chances are good that there is a loop somewhere that just allocates one class instance for each entry – something like

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

Your hardware breakpoint should catch this loop quite quickly. Patch the executable to change the 500 to 1000, and the entries[i] calculation to your new array. After that, your new array will get initialized, but the old one will hold nothing but NULL pointers. Which means, you might get invalid memory acceses through those NULL pointers in the future, which help identifying accessed to your original array (that you can patch) as well.


Edit – Edit after reading OP’s 2nd answer

Dead end? Not at all, you gathered and posted very valuable information.

First, your pseudo code of the CFG_DB constructor

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

confirms that the 4 bytes at the beginning of the class structure are actually a pointer to the virtual function table of the class.

Second, your snippet

.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

fits very well. (Again, i reordered the assembly instructions in a way that doesn’t change what they do, but makes it clearer to understand them). Remember your vtable that had a function offset, a nullsub, 3 more function offsets, and ReadCfgFile as its entries? Since each of these has 4 bytes, the offset of the ReadCfgFile function pointer in the vtable is 20, or 14h. In that code snippet, ebx is a class pointer; mov eax, [ebx] gets the vtable pointer from the class pointer, and call dword ptr [eax+14h]calls the function at that offset, namely ReadCfgFile. Before that, it initializes the this register (ecx) to ebx, and pushes 2 parameters on the stack. This seems to be a very standard call to a class method.

Next, your constructor ends in

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

with the first parameter (v1+4) being the address of the ConfigurationEntries array within the Configuration class (i’m keeping my old invented variable/class names), the second parameter (0x3e4 == 996) the size of each array entry; the third (500) the number of entries, and a callback function. I’m almost sure this is the constructor function for the individual ConfigurationEntries. Which means this is the function i said you need to find, and which should be changed to

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

with XXXXX being the offset of the newEntries array once we allocate space for it.

Next, reconsidering a part of the code snippet before the indirect call to ReadConfigFile,

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

we see that there is a move to ebx+7A938h, with ebx being the class pointer, so there seems to be another class member at this offset. This is quite a lot of memory after the offset of the element count (79954h) – so the structure has a lot more components. Good thing you’re not trying to shift them all. The constructor function, which accesses this+502036, or this+0x7a914, would have been another hint at that. (It also accesses this+124503, but with this being a dword pointer, this means 498012 bytes, which is still less than 502036).

Next, you found out the address 0190BD08, which is a very good thing. Along with the XREFs, and the data definition,

.data:0190BD08 unk_190BD08     db    ? 

this means:

The class structure at that address is NOT allocated dynamically, and it is NOT initialized to anything, instead, it is an uninitialized global variable. In C++, it would have probably been a

static Configuration myConfig;

with all the xrefs you’re seeing being a reference to myConfig. As the assembly instructions at those places are

mov     ecx, offset unk_190BD08

i’m almost sure the calls to a class member function are one or 2 instructions after each of these. Congractulations, you’ve just found a way to catch many of the instances where the configuration gets accessed. To verify this, unk_190BD08 being a global variable for the configuration, you could run the program with a breakpoint on the constructor function (sub_8EEFC0), i bet it’s called only once, and 190BD08 is the parameter to it.

The bad thing about the configuration class instance being a static variable, not a new()-instantiated one, is that we can’t just increase the size in the new() call. Instead, we’ll have to move it to a part of the address space where it doesn’t disturb anything else – at the end of the current uninitialized data segment. Find the very last entry in your data segment, then choose a nice address behind that, and rewrite all the xrefs to unk_190BD08 to that new address. Then, run the program using ollydbg, place a hardware breakpoint on 190BD08 just in case, and check that the functions you know to be class member functions, and the initialized, all get the new address instead of 190BD08 as their ecx (this) parameter. When you’ve finished, you’re ready to implement the 2nd part, change the accesses to this+4 to this+XXXX with XXXX being the class size.

The fact that we’re missing a new() call for the Configuration variable means we can’t use its parameter to get the class size. But we already know the class size is at least 7A938h, so the static variable occupies the address space from 0x190BD08 to at least 0x1986640. With a bit of luck, your .data segment has that unk_190BD08 label, with the next label being at an address a bit after 0x1986640, so the next label is some other variable, and the difference between both labels the size of the Configuration instance.

There’s one thing left to do – when you move the configuration variable behind everything else, you’ll also have to increase the size of that data segment in the PE file. Unfortunately, i’m not that skilled in the windows PE file format, so i’d have to do a lot of research how to do this correctly myself, so maybe you’re lucky and someone else, who has more experience in this than i have, can help you with this.


Edit – Edit to answer to comment

  • how can I calculate how big is the CFG_DB class in order to append something at the end of it?

The original C++ program will have had something like

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

(int as an example, the data type could be anything) and the compiler should place all of these varuables into the uninitialized part of the .data segment. Any access to one of the variables will make IDA create a label at that memory address, with an XREF to where it’s accessed. So, if your .data dump looks like this

.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 

and you know that 0190BD08 is your globalConfig, then 01986670 is athirdvariable, and the size of globalConfig is 0x01986670 – 0x0190BD08 = 0x7a968 = 502120 bytes.

This is not 100% foolproof, because if anything accesses globalConfig.somemember, with somemember having a structure offset of, say, 500000 (=0x7a120), then IDA will generate a label and an XREF at 0x190BD08+0x7a120=0x1985E28 ass well. But with a bit of luck, the “rest” of the program will just use the globalConfig variable as an argument to a member function, where references are indirect, so they won’t be used by IDA to create labels.

  • what will happen if other classes/methods/etc are interacting with this modified class?

If they access anything but the 500 individual config entries, nothing at all, because these haven’t changed their offset. The thing that might be dangerous is when they access the entries, because those accesses should be rewritten to newEntries. You have to find out where they are (if you’re lucky, that’s just in the member functions) and patch the code at that point. The hardware breakpoint on the (old) entries[0] address (which is 0x190BD0C in your case, structure start + 4) should help with this, because anything that’s likely to access any entry, is likely to access entry[0] as well. So, if you hit the hardware breakpoint, on, say, mov eax, [ebx+4], then you know ebx+4 is accessing the old address, and should be rewritten to mov eax, [ebx+XXXXX] to use the new array.

Unfortunately, you can’t create a hardware breakpoint that covers the whole structure to catch any access. That’s where the null pointer exceptions kick in. If, after the change, your program throws a NPE where it normally doesn’t, it’s probably because ‘something’ accessed the old array, which contains nothing but NULLs now, instead of the new one. Catching the NPE in the debugger, and checking the disassembly/call stack that lead to it, should give you an idea where the NULL pointer was read from the old array, so you know which instruction to change to point to the new one.

http://www.exploit-db.com/wp-content/themes/exploit/docs/18810.pdf

Advertisements