Log in

View Full Version : Reversing a particular function from ASM to C++


Dim77
July 13th, 2012, 11:07
I am trying to reverse the code below from ASM to C++. So far it partially works, but there is a problem with the first argument -> *Lrace_vtbl, which supposed to be a reference to this.

It takes the reference to vtbl in the
Code:
*(DWORD *)Lrace_vtbl = off_6E7234;
and supposed to pass this to the called function: VtblPtr_proc.

But it looks as if I screwed the references somewhere. It passes the address of Lrace_vtbl to VtblPtr_proc, but inside that function Lrace_vtbl address get's assigned wrong value.

Below are three pieces of code: Original ASM I am trying to reverse, IDA version of the reverse code, my version.

The question is, whether my reversing is correct and what to do with the first argument of the LraceIDInit_proc and VtblPtr_proc to make the reversed code work properly once compiled...

ASM

Code:
Entry point
CALL <JMP.&MSVCRT._EH_prolog> //Jump to msvcrt._EH_prolog
PUSH ECX
PUSH ESI
MOV ESI,ECX
XOR EAX,EAX
MOV DWORD PTR SS:[EBP-10],ESI
MOV DWORD PTR DS:[ESI+4],EAX
MOV DWORD PTR DS:[ESI+8],EAX
MOV DWORD PTR DS:[ESI+0C],EAX
MOV DWORD PTR DS:[ESI+10],EAX
MOV DWORD PTR DS:[ESI],OFFSET 006E7234
PUSH DWORD PTR DS:[7AB37C] //Arg4 = ASCII "LRace.dbf"
MOV DWORD PTR SS:[EBP-4],EAX
PUSH DWORD PTR SS:[EBP+8] //Arg3 => [ARG.EBP+8]
PUSH DWORD PTR SS:[EBP+0C] //Arg2 => [ARG.EBP+0C]
PUSH ESI //Arg1
CALL 0057ED2E //0057ED2E


IDA

Code:
int __thiscall sub_57EB99(int this, char *Dir, int a3)
{
int v3; // esi@1

v3 = this;
*(_DWORD *)(this + 4) = 0;
*(_DWORD *)(this + 8) = 0;
*(_DWORD *)(this + 12) = 0;
*(_DWORD *)(this + 16) = 0;
*(_DWORD *)this = &off_6E7234;
sub_57ED2E(this, a3, Dir, (char *)off_7AB37C);
return v3;
}



My interpretation

Code:
typedef int(__fastcall * LraceIDInit_proc)(DWORD*, DWORD *, char*, char*);
typedef void(__stdcall * VtblPtrPtr )(DWORD*, int*, char*, char*);

VtblPtrPtr VtblPtr_proc = (VtblPtrPtr)(0x0057ED2E);
DWORD off_6E7234 = (DWORD)(0x006E7234);


extern "C" __declspec(dllexport) int __fastcall LraceIDInit_proc(DWORD *Lrace_vtbl, DWORD *a1, char *Dir, int *some_address)
{

int aLraceId_human = 0;
int aLraceId_dwarf = 0;
int aLraceId_heretic = 0;
int aLraceId_undead =0;
int aLraceId_neutral =0;
int aLraceId_elf =0;

*(DWORD *)Lrace_vtbl = off_6E7234;

VtblPtr_proc(Lrace_vtbl, some_address, Dir, "LRace.dbf";

aqrit
July 14th, 2012, 10:54
I haven't really looked too close
this is just a quick mock up for you to see:
Code:

class CRace
{
DWORD vtable;
DWORD human;
DWORD dwarf;
DWORD heretic;
DWORD undead;
DWORD neutral;
DWORD elf;
};

CRace* __stdcall sub_57EB99_helper( CRace* thisptr, char* szDir, int* some_pointer )
{
thisptr->vtable = 0x006E7234;
thisptr->human = 0;
thisptr->dwarf = 0;
thisptr->heretic = 0;
thisptr->undead = 0;
thisptr->neutral = 0;
sub_57ED2E( thisptr, some_pointer, szDir, "LRace.dbf" );
return thisptr;
}

void __cdecl __declspec(naked) sub_57EB99( ... )
{
// convert thiscall to stdcall
__asm{
pop eax
push ecx
push eax
jmp sub_57EB99_helper
};
}

I don't have the game in question ... Note: your not supposed to your identify your targets on this board

Dim77
July 14th, 2012, 12:22
Quote:
[Originally Posted by aqrit;92905]I haven't really looked too close
this is just a quick mock up for you to see:


Thanks a lot. I haven't done proper convert from thiscall to stdcall in asm before... also I didn't think on defining a class.

As you can see, I tried to implement sub_57EB99 with fastcall, there the first arg1 is a pointer to this and it is stored in ECX, EDX is a second arg2 in my function and it is not used, Arg3 and Arg 4 are stack.

I will change my implementation in lines with what you have outlined and will post the result.

Quote:
[Originally Posted by aqrit;92905]Note: your not supposed to your identify your targets on this board


My bad. I have removed the reference now. BTW, the game is more than 10 years old and I am not trying to break it, just to write an extension.

aqrit
July 16th, 2012, 01:57
the __fastcall thing is a good idea
I guess I avoided it cause fastcall isn't standardized. However, intel, mscv, and gcc all do it the same way (now)... so it is no longer a real issue

you could also just declare your own __thiscall virtual functions

or try to track down where the thisptr is coming from and retrieve it from memory instead of from ecx

Dim77
July 16th, 2012, 14:25
Quote:
[Originally Posted by aqrit;92919]the __fastcall thing is a good idea

Yeah, but it didn't work unfortunately...

It seems that:
Code:
*(DWORD *)thisptr = off_6E7234;


is not the same as:
Code:
thisptr->vtable = 0x006E7234;

I wonder, whether I should be using array for thisptr here...

Quote:
you could also just declare your own __thiscall virtual functions

You mean like that:
Code:
typedef int(__thiscall * sub_57EB99)(int*, char*, int*);
?

I tried it, but I didn't know how to pass thisptr to it. Only two arguments are passed to sub_57EB99 through stack, thisptr comes through ecx... So I needed a function which would take ecx, thus fastcall

Quote:
or try to track down where the thisptr is coming from and retrieve it from memory instead of from ecx

Interesting. Worth trying, I know the address of thisptr.

19/07/2012 update: It works now with the class solution. Thanks again! .
There is one unresolved issue now. Original function was a little bigger:
Code:
int __thiscall sub_57EB99(int this, char *Dir, int a3)
{
int v3; // esi@1

v3 = this;
*(_DWORD *)(this + 4) = 0;
*(_DWORD *)(this + 8) = 0;
*(_DWORD *)(this + 12) = 0;
*(_DWORD *)(this + 16) = 0;
*(_DWORD *)this = &off_6E7234;
sub_57ED2E(this, a3, Dir, (char *)off_7AB37C);

sub_57ECE9(this); // this is a function of type __thiscall according to IDA

return v3;
}


sub_57ECE9(this) is a __thiscall function. Replacing it with:

Code:


LRace* __stdcall sub_57ECE9_helper = (LRace*)(0x0057ECE9);

void __cdecl __declspec(naked) sub_57ECE9 (LRace* thisptr)
{
// convert thiscall to stdcall
__asm{
pop eax
push ecx
push eax
jmp sub_57ECE9_helper
};
}

LRace* __stdcall sub_57EB99_helper( LRace* thisptr, char* szDir, int* some_pointer )
{
thisptr->vtable = 0x006E7234;
thisptr->human = 0;
thisptr->dwarf = 0;
thisptr->heretic = 0;
thisptr->undead = 0;
thisptr->neutral = 0;
sub_57ED2E( thisptr, some_pointer, szDir, "LRace.dbf" );

sub_57ECE9(thisptr);

return thisptr;
}

is not helping, I suppose because this function is not re-written and therefore is not redefined. It looks as if stack is messed up, when it is called. Not sure, what to do with it, other than redefine...

aqrit
July 20th, 2012, 02:36
working code for giggles
Code:


enum race_id
{
HUMAN = 0,
UNDEAD,
HERETIC,
DWARF,
NEUTRAL,
ELF,
};

struct SomeClass
{
void* p; // 0x00
DWORD UnknownA; // 0x04
DWORD UnknownB; // 0x08
DWORD UnknownC; // 0x0C
DWORD UnknownD; // 0x10
};

struct __declspec( align( 16 ) ) SomeOtherClass
{
void* p; // 0x00
SomeClass* sc; // 0x04
race_id id; // 0x08
};

#define DATA( name, type, address ) static type& name = (*((type*)address));
#define FUNC( name, address, ret_type, call_convention, args ) ret_type(call_convention * const name)args = (ret_type(call_convention *)args) address;
#define PFNC( name, address, ret_type, call_convention, args ) static ret_type (call_convention *& name)args = *( (ret_type(call_convention **)args)address);

namespace Game
{
DATA( undead, SomeOtherClass, 0x00837290 );
DATA( elf, SomeOtherClass, 0x008372A0 );
DATA( neutral, SomeOtherClass, 0x008372B0 );
DATA( human, SomeOtherClass, 0x008372C0 );
DATA( heretic, SomeOtherClass, 0x008372D0 );
DATA( dwarf, SomeOtherClass, 0x008372E0 );

FUNC( sub_0057ED2E, 0x0057ED2E, DWORD, __stdcall, ( SomeClass*, void*, char*, char* ) );
FUNC( sub_0057EDA6, 0x0057EDA6, DWORD, __stdcall, ( SomeOtherClass*, SomeClass*, char*, char* ) );
}

SomeClass* __fastcall my_0057EB99( SomeClass* thisptr, DWORD unused, char* szDir, void* some_pointer )
{
char szLRacedbf[] = { "LRace.dbf" };
thisptr->p = (void*) 0x006E7234;
thisptr->UnknownA = 0;
thisptr->UnknownB = 0;
thisptr->UnknownC = 0;
thisptr->UnknownD = 0;
Game::sub_0057ED2E( thisptr, some_pointer, szDir, szLRacedbf );
Game::sub_0057EDA6( &Game::undead, thisptr, "L_UNDEAD", szLRacedbf );
Game::sub_0057EDA6( &Game::elf, thisptr, "L_ELF", szLRacedbf );
Game::sub_0057EDA6( &Game::neutral, thisptr, "L_NEUTRAL", szLRacedbf );
Game::sub_0057EDA6( &Game::human, thisptr, "L_HUMAN", szLRacedbf );
Game::sub_0057EDA6( &Game::heretic, thisptr, "L_HERETIC", szLRacedbf );
Game::sub_0057EDA6( &Game::dwarf, thisptr, "L_DWARF", szLRacedbf );
return thisptr;
}


note: missing the exception handler
note: lets hope the exe is loaded at the preferred address

Dim77
July 22nd, 2012, 02:59
Quote:
[Originally Posted by aqrit;92929]working code for giggles


It works. Thanks a lot for this. I understand that praise from a giggle might not be very valuable, but it looks to me as a very elegant solution. It is difficult to find these kind of practical examples on the net. When I was reading Eilam the examples he was giving were not wide enough at times. Although I will consider reading him again now to remind myself of how he identified the classes\structures.

BTW, your code repeats the original after compiling line by line

If you don't mind then could you please explain of how did you identify the SomeOtherClass structure?

_genuine
July 23rd, 2012, 23:30
in response to finding the thisptr it is retreivable in the vtable pointer i believe. if my memory doesnt elude me.

Dim77
July 25th, 2012, 01:11
Quote:
[Originally Posted by _genuine;92952]in response to finding the thisptr it is retreivable in the vtable pointer i believe.

So, simply going by the fact that there is a vtable pointer, which indicates that there is a sub-class involved. Also, I suppose the fact that first arg of some of the internal functions belongs to that sub-class comes from understanding that that argument contains value, which is going to be updated as a result of function execution.