Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call this x86 ASM CALL in C++ with typedef or inline

I found this call sub_10636F0 in 5 different places trying to figure out how to call it from C++ DLL which is injected into the target application so It has full access to all the calls in that application.

I had a chart of all the places where it's called most of these call's are cut right after another call above it, to ensure it's completeness.

I read tons of questions on stackoverflow about this subject found a few good answers from Necrolis, saying if its a EDX then you could use __fastcall. I googled to find out about ECX and it seems to also be used to __fastcall so either ECX or EDX mean __fastcall.

But the function it calls uses the wrapper

sub     esp, 5F4h
add     esp, 5F4h
retn    8

I have no idea what this is about again doing tons of research I think SUB ESP, XXX at beginning and ADD ESP, XXX at end are used only for _cdecl conversions

My current code looks like this

typedef void(__fastcall *TThreeParamter)(int, int, int);
typedef void(__fastcall *TTwoParamter)(int, int);
typedef void(__fastcall *TOneParamter)(int);
typedef void(__fastcall *TZeroParamter)();

TTwoParamter sub_10636F0 = (TTwoParamter)(DWORD)GetModuleHandle(NULL) + 0xC636EF;

//the call
sub_10636F0(0x11223344, 0x55667788);


Don't ask me why the 0xC636EF is different from 10636F0 in the sub, I can tell you it's going into the correct sub upon inspection in a debugger, the sub's keep moving around everytime the program is re-launched it seems to be either a protection method, or possibly because this program loads over 50 dll's and the addresses need to move around.

I tried all different configurations, 2 int's, 3 int's nothing works..


IDA detects this method as being 3 parameters, but the last parameter isn't used anywhere in the decompiled pesudo-code, which I cannot figure out,

Pseudo-code looks like this ( I did heavy modifications to it, like change it to _fastcall from __thiscall )

Pseudo-code from IDA

//probably wrong.. packet is a variable not a parameter which will crash
void __fastcall sub_10636F0(int var1)
{
  __int128 v1; // xmm0@0
  int v2; // esi@1
  int v3; // ebx@1
  SOCKET v4; // ebp@1
  int v5; // eax@2
  int v6; // ecx@3
  int v7; // [sp+8h] [bp-5FCh]@7
  char a2a[1492]; // [sp+10h] [bp-5F4h]@2
  int v9; // [sp+5E4h] [bp-20h]@2
  int v10; // [sp+5E8h] [bp-1Ch]@2
  struct _FILETIME SystemTimeAsFileTime; // [sp+5F0h] [bp-14h]@2
  __int16 v12; // [sp+5F8h] [bp-Ch]@2
  int packet; // [sp+608h] [bp+4h]@0
  int to; // [sp+60Ch] [bp+8h]@0

  v2 = to;
  v3 = var1;
  v4 = *(_DWORD *)(packet + 220);
  if ( v4 != -1 )
  {
    //snipped lots of code
  }
}

//probably wrong.. packet is a variable not a paramter which will crash
void __fastcall sub_10636F0(int var1, int var2)
{
  __int128 v2; // xmm0@0
  int v3; // esi@1
  int v4; // ebx@1
  SOCKET v5; // ebp@1
  int v6; // eax@2
  int v7; // ecx@3
  int v8; // [sp+8h] [bp-5FCh]@7
  char a2a[1492]; // [sp+10h] [bp-5F4h]@2
  int v10; // [sp+5E4h] [bp-20h]@2
  int v11; // [sp+5E8h] [bp-1Ch]@2
  struct _FILETIME SystemTimeAsFileTime; // [sp+5F0h] [bp-14h]@2
  __int16 v13; // [sp+5F8h] [bp-Ch]@2
  int packet; // [sp+608h] [bp+4h]@0
  int to; // [sp+60Ch] [bp+8h]@0

  v3 = to;
  v4 = var1;
  v5 = *(_DWORD *)(packet + 220);
  if ( v5 != -1 )
  {
    //snipped lots of code
  }
}

//this looks the best, but still `to` isn't detected as paramter

void __fastcall sub_10636F0(int var1, int var2, int var3)
{
  __int128 v3; // xmm0@0
  int v4; // esi@1
  int v5; // ebx@1
  SOCKET v6; // ebp@1
  int v7; // eax@2
  int v8; // ecx@3
  int v9; // [sp+8h] [bp-5FCh]@7
  char a2a[1492]; // [sp+10h] [bp-5F4h]@2
  int v11; // [sp+5E4h] [bp-20h]@2
  int v12; // [sp+5E8h] [bp-1Ch]@2
  struct _FILETIME SystemTimeAsFileTime; // [sp+5F0h] [bp-14h]@2
  __int16 v14; // [sp+5F8h] [bp-Ch]@2
  int to; // [sp+60Ch] [bp+8h]@0

  v4 = to; //still doesn't detect this..
  v5 = var1; //okay this isn't bad another parameter
  v6 = *(_DWORD *)(var3 + 220); //like this detects this as parameter class atleast 
  if ( v6 != -1 )
  {
    //snipped lots of code
  }
}

This is the code IDA recommends by default

char __userpurge sub_10636F0@<al>(int a1@<ecx>, __int128 a2@<xmm0>, int a3, int a4)
{
  int v4; // esi@1
  int v5; // ebx@1
  SOCKET v6; // ebp@1
  int v7; // eax@2
  int v8; // ecx@3
  int v9; // eax@8
  char v11; // [sp+8h] [bp-5FCh]@7
  int v12; // [sp+10h] [bp-5F4h]@4
  int v13; // [sp+24h] [bp-5E0h]@2
  int v14; // [sp+28h] [bp-5DCh]@2
  int v15; // [sp+2Ch] [bp-5D8h]@2
  int v16; // [sp+30h] [bp-5D4h]@2
  char v17; // [sp+34h] [bp-5D0h]@2
  signed int v18; // [sp+5E4h] [bp-20h]@2
  int v19; // [sp+5E8h] [bp-1Ch]@2
  int v20; // [sp+5F0h] [bp-14h]@2
  __int16 v21; // [sp+5F8h] [bp-Ch]@2

  v4 = a4;
  v5 = a1;
  v6 = *(_DWORD *)(a3 + 220);
  if ( v6 == -1 )
    return 0;

   //Snipped code

  if ( v9 >= 0 && v9 == *(_DWORD *)(v4 + 1492) )
    return 1;
  return 0;
}



Function in ASM

.text:010636F0 ; void __fastcall sub_10636F0(int var1, int var2, int var3)
.text:010636F0 sub_10636F0     proc near               ; CODE XREF: sub_1062960+E0p
.text:010636F0                                         ; sub_10637E0+D4p ...
.text:010636F0
.text:010636F0 a2              = byte ptr -5F4h
.text:010636F0 var_20          = dword ptr -20h
.text:010636F0 var_1C          = dword ptr -1Ch
.text:010636F0 SystemTimeAsFileTime= _FILETIME ptr -14h
.text:010636F0 var_C           = word ptr -0Ch
.text:010636F0 var_4           = dword ptr -4
.text:010636F0 packet          = dword ptr  4
.text:010636F0 to              = dword ptr  8
.text:010636F0 test            = dword ptr  0Ch
.text:010636F0
.text:010636F0                 sub     esp, 5F4h
.text:010636F6                 mov     eax, ___security_cookie
.text:010636FB                 xor     eax, esp
.text:010636FD                 mov     [esp+5F4h+var_4], eax
.text:01063704                 push    ebx
.text:01063705                 push    ebp             ; a5
.text:01063706                 push    esi             ; a4
.text:01063707                 mov     esi, [esp+600h+to]
.text:0106370E                 push    edi             ; a3
.text:0106370F                 mov     edi, [esp+604h+packet]
.text:01063716                 mov     ebx, ecx
.text:01063718                 mov     ebp, [edi+0DCh]
.text:0106371E                 cmp     ebp, 0FFFFFFFFh
.text:01063721                 jz      loc_10637BB
        SNIP TONS OF CODE HERE
.text:010637BB
.text:010637BB loc_10637BB:                            ; CODE XREF: sub_10636F0+31j
.text:010637BB                                         ; sub_10636F0+BDj ...
.text:010637BB                 xor     al, al
.text:010637BD
.text:010637BD loc_10637BD:                            ; CODE XREF: sub_10636F0+C9j
.text:010637BD                 mov     ecx, [esp+604h+var_4]
.text:010637C4                 pop     edi
.text:010637C5                 pop     esi
.text:010637C6                 pop     ebp
.text:010637C7                 pop     ebx
.text:010637C8                 xor     ecx, esp
.text:010637CA                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:010637CF                 add     esp, 5F4h
.text:010637D5                 retn    8
.text:010637D5 sub_10636F0     endp ; sp-analysis failed



Calls to this function in ASM

.text:010638B0                 push    esi             ; packet
.text:010638B1                 push    ebx             ; this
.text:010638B2                 mov     ecx, ebp        ; this
.text:010638B4                 call    sub_10636F0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text:01062A2E                 mov     byte ptr [esi+5E9h], 1
.text:01062A35
.text:01062A35 loc_1062A35:                            ; CODE XREF: sub_1062960+C1j
.text:01062A35                 add     dword ptr [esi+5D4h], 2
.text:01062A3C                 push    esi             ; packet
.text:01062A3D                 push    edi             ; this
.text:01062A3E                 mov     ecx, ebx        ; this
.text:01062A40                 call    sub_10636F0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text:01063AF4                 mov     eax, [ebx+1128h]
.text:01063AFA                 mov     [esp+1A4h+var_AC], eax
.text:01063B01                 push    esi             ; packet
.text:01063B02                 lea     eax, [esp+1A8h+to]
.text:01063B06                 push    eax             ; this
.text:01063B07                 mov     ecx, ebx        ; this
.text:01063B09                 mov     [esp+1ACh+var_4], 1
.text:01063B14                 call    sub_10636F0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text:01089145 loc_1089145:                            ; CODE XREF: sub_10890B0+4Fj
.text:01089145                                         ; sub_10890B0+67j
.text:01089145                 mov     ecx, [edi+110h] ; this
.text:0108914B                 push    esi             ; packet
.text:0108914C                 push    edi             ; this
.text:0108914D                 call    sub_10636F0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text:01089CBA                 mov     ecx, [esi+110h] ; this
.text:01089CC0                 push    edi             ; packet
.text:01089CC1                 push    esi             ; this
.text:01089CC2                 call    sub_10636F0
like image 643
user3435580 Avatar asked Dec 13 '25 21:12

user3435580


1 Answers

I have no idea what this is about again doing tons of research I think SUB ESP, XXX at beginning and ADD ESP, XXX at end are used only for _cdecl conversions

No, it's used for ALL functions that use local variables (with minor variations as to exactly how it's done, but stack space needs to be allocated by subtracting from ESP, and "freed" by adding the same amount to the stack pointer.

However, the RET 8 does indeed indicate that the calling convention is NOT _cdecl, but one where the stack is cleaned up by the callee. There are a few different calling conventions that match this, but I have a feeling it's C++ code and a member function, which would make it thiscall - that does make it a little hard to simulate, since you want this in ECX.

The ret 8 says that the function has 8 bytes worth of arguments, so two int or void * variables.

I'm far from convinced there is a simple way to do this. You may be able to do something like this. Create a class X with a virtual function that takes two arguments:

class X
{
   virtual void Func(int x, int y) { }
};

Then figure out where the compiler put the vtable, and modify the vtable for func to point at your target function, rather than the empty implementation of the class.

Now you can use X to create an instance:

 X* p = new X;

and then call func.

 p->func(1, 2);

However, if you are unlucky, the compiler doesn't realize that you have messed with the vtable, and end up calling the function directly. So you may need to do some trickery with separate compilation and other stuff.

In other words, you have your work cut out. But then reverse engineering wouldn't be any fun at all if you didn't have to trick around a bit.

Of course, the cheaters method is to just write a few lines of inline assembler, like such:

void CallMyFunc(void *func, int a, int b, int c)
{
    __asm(mov ecx, a
          push b
          push c
          call *func);
}

[It's about 10 years since I last wrote Windows inline assembly code, so apologies if the syntax isn't quite right - consider it a "rough sketch" and do modify it until it actually compiles...]

like image 64
Mats Petersson Avatar answered Dec 15 '25 11:12

Mats Petersson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!