2

I need a little help from you guys! At present, I'm working on an IDA Pro Database in order to create a patch for an executable, and I'm totally stuck on editing the following sub:

.text:008EC420 ; char __thiscall sub_8EC420(MyClass *this)
.text:008EC420 sub_8EC420      proc near
.text:008EC420 var_8           = dword ptr -8
.text:008EC420 var_4           = dword ptr -4
.text:008EC420                 sub     esp, 8
.text:008EC423                 push    ebx
.text:008EC424                 mov     ebx, ecx
.text:008EC426                 cmp     dword ptr [ebx+79954h], 0
.text:008EC42D                 mov     [esp+0Ch+var_4], ebx
.text:008EC431                 mov     [esp+0Ch+var_8], 0
.text:008EC439                 jle     loc_8EC4F2
.text:008EC43F                 push    ebp
.text:008EC440                 push    esi
.text:008EC441                 push    edi
.text:008EC442                 lea     edi, [ebx+114h]
.text:008EC448                 jmp     short loc_8EC450
.text:008EC44A                 align 10h
.text:008EC450 loc_8EC450:
.text:008EC450                 lea     eax, [edi-0Ch]
.text:008EC453                 push    eax
.text:008EC454                 call    sub_A00890
.text:008EC459                 mov     esi, eax
.text:008EC45B                 add     esp, 4
.text:008EC45E                 cmp     esi, 0FFFFFFFFh
.text:008EC461                 jnz     short loc_8EC472
.text:008EC463                 mov     dword ptr [edi], 0
.text:008EC469                 mov     dword ptr [edi-4], 0FFFFh
.text:008EC470                 jmp     short loc_8EC480
.text:008EC472 loc_8EC472:
.text:008EC472                 push    esi
.text:008EC473                 call    sub_A00870
.text:008EC478                 add     esp, 4
.text:008EC47B                 mov     [edi], eax
.text:008EC47D                 mov     [edi-4], esi
.text:008EC480 loc_8EC480:
.text:008EC480                 xor     ebp, ebp
.text:008EC482                 cmp     [edi+0ECh], ebp
.text:008EC488                 jbe     short loc_8EC4D2
.text:008EC48A                 lea     esi, [edi+0BCh]
.text:008EC490 loc_8EC490:
.text:008EC490                 lea     ecx, [esi-0Ch]
.text:008EC493                 push    ecx
.text:008EC494                 call    sub_A00890
.text:008EC499                 mov     ebx, eax
.text:008EC49B                 add     esp, 4
.text:008EC49E                 cmp     ebx, 0FFFFFFFFh
.text:008EC4A1                 jnz     short loc_8EC4B2
.text:008EC4A3                 mov     dword ptr [esi], 0
.text:008EC4A9                 mov     dword ptr [esi-4], 0FFFFh
.text:008EC4B0                 jmp     short loc_8EC4C0
.text:008EC4B2 loc_8EC4B2:
.text:008EC4B2                 push    ebx
.text:008EC4B3                 call    sub_A00870
.text:008EC4B8                 add     esp, 4
.text:008EC4BB                 mov     [esi], eax
.text:008EC4BD                 mov     [esi-4], ebx
.text:008EC4C0 loc_8EC4C0:
.text:008EC4C0                 add     ebp, 1
.text:008EC4C3                 add     esi, 14h
.text:008EC4C6                 cmp     ebp, [edi+0ECh]
.text:008EC4CC                 jb      short loc_8EC490
.text:008EC4CE                 mov     ebx, [esp+18h+var_4]
.text:008EC4D2 loc_8EC4D2:
.text:008EC4D2                 mov     eax, [esp+18h+var_8]
.text:008EC4D6                 add     eax, 1
.text:008EC4D9                 add     edi, 3E4h
.text:008EC4DF                 cmp     eax, [ebx+79954h]
.text:008EC4E5                 mov     [esp+18h+var_8], eax
.text:008EC4E9                 jl      loc_8EC450
.text:008EC4EF                 pop     edi
.text:008EC4F0                 pop     esi
.text:008EC4F1                 pop     ebp
.text:008EC4F2 loc_8EC4F2:
.text:008EC4F2                 mov     al, 1
.text:008EC4F4                 pop     ebx
.text:008EC4F5                 add     esp, 8
.text:008EC4F8                 retn
.text:008EC4F8 sub_8EC420      endp

The MyClass entity looks like this:

struct MyClass // 502076 bytes
{
  void *VFT; // 4 bytes
  MyStruct Structs[500]; // 498000 bytes = sizeof(MyStruct)=996 * 500
  // more data...
};

In order to allow MyClass to parse and manage more MyStruct instances, I enlarged the .data segment of the executable by sizeof(MyStruct1)=996 * 1000 = 996000 bytes and I appended a new MyStruct[1000] at the end of it. Now, all what I need to do is to shift every memory pointer trying to access the MyClass.Structs memory region to the MyStruct[1000] memory region. Many modifications were pretty easy to achieve, but I really don't know how to deal with this one.

The method sub_8EC420 iterates through every MyStruct instance in MyClass.Structs getting a specific field and doing some operations with it:

char *v2 = &this->Structs[0].ValueAt272;
// offset = MyClass + 276 | MyClass->Structs + 272

do
{
    // operations...
    v2 += 996; // sizeof(MyStruct)
}
while ( ... );

In order to successfully redirect the memory pointer to the new MyStruct[1000] array located at the end of the .data segment, I must change the following instruction:

.text:008EC442                 lea     edi, [ebx+114h]
// offset = MyClass + 276 | MyClass->Structs + 272
// bytes = 8D BB 14 01 00 00

EBX is the register that points to my MyClass instance offset. But I can't transform it into this:

.text:008EC442                 mov edi, 2DABF84h; nop;

Because, as you can see, just after, the EDI register value is being used and this would lead to bad results:

.text:008EC450                 lea     eax, [edi-0Ch]

I also tried an edited LEA approach but it's not producing good results either:

.text:008EC442                 lea     edi, [ebx+14A027Ch]
// offset = MyStruct[1000] (0x02DABE74) - MyClass Instance (0x190BD08) + 272
// bytes = 8D BB 7C 02 4A 01

On the top of that, inserting additional bytes to this sub completly breaks my IDA Pro database. So... how should I deal with this?

  • did you try converting it to C completely, and then re-compiling, maybe your compiler ends up with shorter code than the original? then you could just replace the entire function – Willem Hengeveld May 26 '14 at 19:53
  • Well, the executable is something like 20Mb. And the pseudo-code that the decompiler produces is always almost impossible to recompile. – Zarathos May 26 '14 at 20:26
  • if it is only one function that needs modification it is an option to correct the decompiled code by hand. – Willem Hengeveld May 26 '14 at 20:58
  • How... exactly? – Zarathos May 26 '14 at 22:49
  • there is a file "defs.h" in the ida plugin directory, include that with your decompiler generated c code, and start fixing compiler errors. – Willem Hengeveld May 27 '14 at 8:21
3

One way to patch the instruction lea eax, [edi-0Ch], is to create an advanced detour inside the code to your own custom .text segment and then back again to the code.

For a regular mov reg32, imm32 opcode you need at least 5 bytes of space which will obviously never fit into the 3 byte opcode.

For a detour opcode sequence, you need to either craft your own relative jump (5 bytes), or use the shortest syntax for absolute jump (6 bytes):

PatchStart:
    push  loc_patch1  ;; push address of the patch code
    ret               ;; pop address and jump to it

So if the code originally looked like this:

.text:008EC44A                 align 10h
.text:008EC450 loc_8EC450:
.text:008EC450                 lea     eax, [edi-0Ch] ;; 3 bytes
.text:008EC453                 push    eax            ;; 1 byte
.text:008EC454                 call    sub_A00890     ;; 5 bytes
.text:008EC459                 

You can replace it with:

.text:008EC44A                 align 10h
.text:008EC450 loc_8EC450:
.text:008EC450                 push    loc_patch1     ;; 5 bytes
.text:008EC455                 ret                    ;; 1 byte
.text:008EC456                 nop                    ;; pad other 3 bytes
.text:008EC457                 nop  
.text:008EC458                 nop
.text:008EC459                 

And now for the patch itself:

           loc_patch1:
                           mov     eax, PatchedValue ;; lea eax,[edi-0Ch]
                           push    eax            
                           call    sub_A00890     
                           push    loc_8EC459     ;; jump back to source
                           ret

There are some performance penalties for long absolute jumps, but in the end it will get your executable patched without any tedious relocation needed.

If you wish to craft your own long relative jumps/calls then the opcodes (5 bytes) look like this:

;; call +0xCAFE
E8 FE CA 00 00
;; jmp  +0xCAFE
E9 FE CA 00 00

The bonus is that relative jumps require only 5 bytes. So if you create a new second text segment or replace some unused code inside the original .text segment, you'll be good to go.

1

You use a local unconditional jmp to another memory location then do what you want there. Ideally there's space before or after the function for you to put your instructions there. Otherwise you're looking at either relocating the entire function (a bit like a re-alloc, only in the .text section, which you can make bigger overall if you need to) and then long jmp to your code and long jmp back. Don't forget to make those jumps relative long jumps so that ASLR in it's various forms won't shaft you.

If you MUST do it inline then you're left optimising the code by enough bytes. Remember to keep locations for relative jumps the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.