目前,我正在处理一个可执行文件,我想对其进行一些修改以供个人使用;该应用程序的源代码不可用,因此我只能对其进行修改或尝试。我同时使用IDA Pro 6.1和OllyDBG 2.0作为工具。
准确地说,我想将应用程序可以从
500
读取到CFG_ENTRY的数量增加到1000
方法中显然,在编译时已预先分配了一个静态内存区域:.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
,并且在整个可执行文件中没有直接调用它:继续!是的!] 阅读@Guntram Blohm的答案后,我进行了更多研究,以收集和分析他建议的数据。我做的第一件事是使用PEiD分析可执行文件,这是我从可执行文件中获得的信息:
当我在
ReadCfgFile
方法上设置断点时,这里这是我从OllyDBG堆栈中获得的内容:读取它的例程,但没有显式调用
ReadCfgFile
(突出显示了偏移量):看起来像这样(来自IDA Pro的伪代码):.rdata:0137A1D4 ; class DATABASE_TABLE<CFG_ENTRY,500,unsigned int> [SI] O: 0, A: 0
.rdata:0137A1D4 dd offset ??_R4?$DATABASE_TABLE@UCFG_ENTRY@@Compiler: Microsoft Visual C++ 7.0 Method2 [Debug]
Entropy: 6.24 (Not Packed)
Linker Info: 7.10
BPE@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@@CPU Stack
Address Value ASCII Comments
0018B2C0 [008EE644 D�. ; RETURN to myapp.008EE644
BPE@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
因此,看起来好像正在实例化
008EE644
的“数组”,该方法调用的是另一个链接到可执行文件的库的方法。它可能有任何帮助:.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
然后跟随地址
ReadCfgFile
我偶然发现了这个: />对我来说似乎是死路一条... #1 楼
在我看来,这个问题比您描述的要糟一些,并且没有轻松的方法来解决它。您的编译器在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是一个类方法,以及在cx中获取其
this
指针的方法。从计算数组索引的方式来看,我假设原始的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
,只需将新参数的size参数从500修改为1000会更容易。但是,根据您的情况,您必须修改Configuration类的新方法,并且必须修改对“后面的变量”的所有引用。以及配置条目。您已经提到过,偏移量为0x79954的count变量,偏移量为0x79958的下一个变量,但是在阅读器函数中可能有更多未引用的变量,因此很难找到所有变量。 当我看到您对问题的编辑时,我正要发布此内容。
您在编辑中意识到,您需要更改对要在条目数组后面的结构组件的所有访问权限增加。您需要在类方法中执行此操作(使用vtable可以很容易找到它),但在程序的其余部分中也需要执行此操作(因为您不知道哪些原始类变量中的哪些是公共变量,可以从中访问在课程本身之外)。不幸的是,您无法真正实现自动化,因为每次出现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字节。然后,将ReadCfgEntry前面的lea ecx, [esi+eax*4+4]
更改为
lea ecx, [esi+eax*4+16cca0h]
这样,您已经创建了当前结构后面的另一个数组,而不是扩展当前结构。这意味着除了配置项本身之外,您的所有结构偏移都没有发生变化。 />,并在下一步中,我们必须重新编写对
entries
的所有访问,以使用newentries
。最简单的方法可能是用调试器启动程序
在您先前确定的函数上设置一个断点,该断点将在结构之后分配结构。分配结构,在(结构地址+ 4)上设置硬件断点
继续执行程序,每当碰到硬件断点时,您就会找到匹配项
,因为硬件断点位于条目[0],每次访问条目的机会在某个时候都达到其0的机会。只是为每个条目分配一个类实例的地方-像
class Configuration {
int whatever;
ConfigurationEntry entries[500];
int entryCount;
int somethingelse;
... ??? ...
ConfigurationEntry newEntries[1000];
}
您的硬件断点应很快捕获此循环。修补可执行文件以将500更改为1000,并将entrys [i]计算更改为新数组。之后,新数组将被初始化,而旧数组将仅保留NULL指针。这意味着,将来您可能会通过这些NULL指针获得无效的内存访问权限,这也有助于标识对原始数组的访问(可以修补)。
编辑-之后进行编辑阅读OP的第二个答案
死胡同?根本没有,您收集并发布了非常有价值的信息。首先,您对CFG_DB构造函数的伪代码类结构开头的字节实际上是指向该类的虚函数表的指针。
其次,您的代码段
for (i=0; i<500; i++) {
entries[i]=new ConfigurationEntry()
}
非常适合(再次,我对汇编说明进行了重新排序,但不会改变它们的功能,但是使它们更易于理解。还记得您的vtable包含函数偏移,nullsub,3个其他函数偏移以及
ReadCfgFile
作为其条目吗?由于每个都有4个字节,因此vtable中ReadCfgFile函数指针的偏移量为20或14h。在该代码段中,ebx是一个类指针; mov eax, [ebx]
从类指针获取vtable指针,而call dword ptr [eax+14h]
在该偏移量处调用函数,即ReadCfgFile
。在此之前,它将this
寄存器(ecx)初始化为ebx,并将2个参数压入堆栈。这似乎是对类方法的非常标准的调用。接下来,您的构造函数以
void __thiscall sub_8EEFC0(void *this)
{
void *v1 = this; // esi@1
*(_DWORD *)this = &DATABASE_TABLE<CFG_ENTRY_500_unsigned_int>::_vftable_;
结尾第一个参数(v1 + 4)是Configuration类中的ConfigurationEntries数组的地址(我保持旧的发明的变量/类名),第二个参数(0x3e4 == 996)每个数组条目的大小;第三(500)个条目的数量,以及一个回调函数。我几乎可以确定这是各个ConfigurationEntries的构造函数。这意味着我需要找到该函数,并且应将其更改为
.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
,一旦我们为空间分配了空间,XXXXX是newEntries数组的偏移
接下来,在间接调用ReadConfigFile之前重新考虑一部分代码片段,
unknown_libname_2673((char *)v1 + 4, 0x3E4u, 500, sub_8EEA00);
我们看到有一个动作到
ebx+7A938h
,其中ebx是类指针,因此在此偏移量处似乎还有另一个类成员。元素数量偏移后(79954h),这是很多内存-因此结构具有更多的组件。好消息是您并没有试图将它们全部转移。访问this+502036
或this+0x7a914
的构造函数可能是对此的另一个提示。 (它也访问this+124503
,但是this
是dword指针,这意味着498012字节,仍然小于502036)。 。连同XREF和数据定义一起,这意味着:该地址处的类结构不是动态分配的,并且它没有初始化为任何东西,而是一个未初始化的全局变量。在C ++中,它可能是一个
unknown_libname_2673((char *)v1 + XXXXX, 0x3E4u, 1000, sub_8EEA00);
,您看到的所有外部参照都是对myConfig的引用。因为在这些地方的组装说明是
.text:008EE62A mov edx, [ebx]
.text:008EE62C mov ecx, ebx
.text:008EE62E mov [ebx+7A938h], ebp
.text:008EE634 call dword ptr [edx+4]
我几乎可以肯定,对这些成员函数的调用在每条指令之后是一条或两条指令。顺便说一句,您刚刚找到了一种捕获许多访问配置的实例的方法。为了验证这一点,unk_190BD08是配置的全局变量,您可以在构造函数(sub_8EEFC0)上使用断点运行程序,我敢打赌它只会被调用一次,而190BD08是它的参数。 />关于配置类实例是静态变量而不是实例化的new()的坏事是,我们不能仅仅增加new()调用的大小。相反,我们必须将其移动到地址空间的一部分,在当前未初始化的数据段的末尾,它不会干扰其他任何内容。在数据段中找到最后一个条目,然后在其后面选择一个合适的地址,然后将unk_190BD08的所有外部参照重写为该新地址。然后,使用ollydbg运行程序,以防万一,在190BD08上放置一个硬件断点,并检查您知道是类成员函数的函数以及已初始化的函数是否都获得了新地址而不是190BD08作为其
ecx
(此)参数。完成后,您就可以实现第二部分了,将对this+4
的访问权限更改为this+XXXX
,将XXXX
作为类的大小。我们缺少了new()调用用于Configuration变量意味着我们无法使用其参数来获取类大小。但是我们已经知道类的大小至少为7A938h,因此静态变量占用的地址空间从0x190BD08到至少0x1986640。幸运的是,您的.data段具有该unk_190BD08标签,下一个标签位于0x1986640之后的某个地址,因此下一个标签是其他某个变量,并且两个标签之间的差额为Configuration实例的大小。
剩下要做的一件事-当您将配置变量移到其他所有内容之后时,还必须增加PE文件中该数据段的大小。不幸的是,我不是Windows PE文件格式的熟练者,所以我必须自己做很多研究以正确地做到这一点,所以也许您很幸运,并且还有其他人在此方面比我有,可以为您提供帮助。
编辑-编辑评论的答案
我如何计算CFG_DB类的大小为了在其末尾添加内容?
原始的C ++程序将具有类似
.data:0190BD08 unk_190BD08 db ?
(以int为例) ,数据类型可以是任何类型)。
编译器应将所有这些变量放入.data段的未初始化部分。对变量之一的任何访问都将使IDA在该内存地址创建一个标签,并在其访问位置处使用XREF。因此,如果您的.data转储看起来像这样
static Configuration myConfig;
,并且您知道0190BD08是
globalConfig
,那么01986670是athirdvariable
,并且globalConfig的大小是0x01986670-0x0190BD08 = 0x7a968 = 502120字节。这不是100%可靠的方法,因为如果有任何东西访问globalConfig.somemember,并且某成员的结构偏移为500000(= 0x7a120),则IDA将生成标签以及0x190BD08 + 0x7a120 = 0x1985E28 as的XREF。但是幸运的是,程序的“其余部分”将仅使用globalConfig变量作为成员函数的参数,该成员函数是间接引用,因此IDA不会使用它们创建标签。
如果其他类/方法/等与此修改过的类进行交互,会发生什么?
如果他们访问除500个单独的配置条目以外的任何内容,那么什么都没有,因为这些都没有更改其偏移量。当他们访问条目时,可能会很危险,因为这些访问应重写为newEntries。您必须找出它们的位置(如果幸运的话,这只是在成员函数中),然后在那儿修补代码。 (旧)条目[0]地址(在您的情况下为0x190BD0C,结构开头+ 4)上的硬件断点应对此有所帮助,因为可能访问任何条目的任何内容也可能访问条目[0] 。因此,如果您遇到了硬件断点,例如
mov eax, [ebx+4]
,那么您知道ebx+4
正在访问旧地址,应该将其重写为mov eax, [ebx+XXXXX]
以使用新阵列。 •创建一个覆盖整个结构的硬件断点以捕获任何访问。那就是空指针异常发生的地方。如果在更改之后,您的程序在正常情况下不抛出NPE,则可能是因为“某物”访问了旧数组,该数组现在只包含NULL,而不是新数组。 。在调试器中捕获NPE,并检查导致该NPE的反汇编/调用堆栈,应该使您了解从旧数组读取NULL指针的位置,因此您知道要更改的指令指向新的。 br />评论
感谢您的非常非常宝贵的答案。我用模式信息编辑了答案,这可能会对比我更熟练的人有所帮助...
–萨拉索斯
14年5月8日在16:38
更新了我的答案,也回复了您的第二编辑。
–贡特拉姆·布洛姆(Guntram Blohm)
2014年5月8日18:32
@guntram我没有消化答案,但是在您的建议中,如果可以在可执行文件的末尾而不是在某个部分的末尾添加空间,可以很容易地添加空间,甚至还有实用程序可以自动为您执行此操作,并且可以在末尾添加任意数量的空间iirc santmants ZeroAdd是一个这样的实用程序(自从我使用它以来已经很长时间了,但是应该在woodmanns工具仓库中可用
– blabb
2014年5月8日19:22
哇@GuntramBlohm,您的回答真是太神奇了。如果没有您的帮助,我将永远找不到!因此,事实上,要恢复一点,我必须:1)将所有XRef移至unk_190BD08到我的数据空间的底部,以避免覆盖2)将500更改为1000,将“ v1 + 4”更改为新的CFG_ENTRY []构造函数中unknown_libname_2673的调用中的地址但是,我仍然不了解两件事:如何计算CFG_DB类的大小,以便在其末尾添加内容?而且...如果其他类/方法/等与此修改过的类进行交互会发生什么?
–萨拉索斯
2014年5月8日20:34
似乎某处某处正在直接访问globalConfig.entries。这将使IDA在其中放置标签以及外部参照。
–贡特拉姆·布洛姆(Guntram Blohm)
2014年5月10日在18:23
#2 楼
因为我不知道ReadCfgEntry的原型和代码的结构,所以我的答案是不正确的。作为初始化的寄存器:.text:008F2886 mov esi, ecx
它是一个类函数,因此所有者类具有一个初始化目标缓冲区的构造函数。您必须跟踪缓冲区并增加分配大小。
这不是查找构造函数的确切方法;但是一种有效的方法是在函数启动时通过跟踪ECX查找虚拟功能表。大多数情况下,目标缓冲区的第一个元素中包含VFTable。通过XRef在IDA中对其进行引用(如您所做的那样)。 “ .rdata:0137A1EC dd偏移量
ReadCfgFile” ...
找到VFTable之后,大多数时候构造函数是最高成员。
第二:如果缓冲区是静态分配的
如前所述,该函数是类的成员,因此如果您更改感兴趣的指针,则该函数是类的成员。
if (!ReadCfgEntry((void*)(pointer + (996 * entriesCount) + 4), v5))
您应该插入一个存根以分配适当的内存,并将其分配给类。
最后:如果该程序是封闭源代码,这意味着开发人员不喜欢其他人对其进行修改以供个人使用...请确保未违反许可证!
评论
我正在编辑此应用程序仅供个人使用,不会造成伤害,也不会公开发布这些修改……它只是一个非常老的应用程序,不再开发中,但是对于我。同时,我在编辑问题时添加了最近的发现,因此现在您可能会更清楚地了解所有内容。
–萨拉索斯
2014年5月8日在12:31
评论
我在您的反汇编中没有看到对静态缓冲区的任何引用。反汇编中对等效s_CfgEntries的引用在哪里?嗯,正如我所说,我并不是反汇编专家,我编写的代码只是一个伪代码,用以了解ASM在人类可读代码中的样子。我也看不到任何静态缓冲区,但仅在此处调用了方法ReadCfgFile:“。rdata:0137A1EC dd偏移量ReadCfgFile”,“。rdata:0137A208 dd偏移量ReadCfgFile”。