Getting the address of an objects constructor

22b3033832c5c699c856814b0cf80cb1
0
bladder 101 Mar 01, 2005 at 15:31

A while back I started a type of c++ quiz thread - here. One of the forum members - Nick, asked a question of how to get a pointer to an objects constructor. A solution to the *problem* was presented, but it wasnt actually getting the address of the objects constructor. Anyway, quite recently, I was playing around with one of the Code Spotlight submissions: Nick’s x86 Decoder, and with that, it is possible to get the actual address of an objects constructor. Now I dont really know how much use this actually is, but I figure it’s a pretty neat trick.

So anyway, first of all you need to modify the x86 Decoder a bit. And you need to be able to resolve jmp instructions for this to work in debug builds.

void* GetActualStartOfFunction( void* );
int NicksModifiedFunction( void*, unsigned char* );

struct SomeStruct
{
  SomeStruct() // we'll get the address of this ctor.
    :i_(3)
  {}

  int i_;
};

// You need a function that will create the object you want the address of the
// constructor of.
void CreateSomeStruct()
{
  SomeStruct ss;
}

void* GetAddrOfCtor( void (*creator_func)() )
{
  void* paddr = GetActualStartOfFunction((unsigned char*)creator_func);
  unsigned char* instr = (unsigned char*)paddr;

  while(1) // probably better put a limit on this...
  {
    int sz = NicksModifiedFunction(instr, 0);

    if( *instr == 0xE8 ) // the call instruction (should probably check for other versions of it)
    {
      paddr = (void*)(int(instr + sz) + *((int*)(instr + 1)));
      break;
    }

    instr += sz;
  }

  return paddr;
}

void main()
{
  void* addr = GetAddrOfCtor(CreateSomeStruct);
}

Next I present the modified x86 decoder, which now calculates the size of a single instruction and returns the actual opcode, and the GetActualAddrOfFunction which resolves the jmp instruction (in vc++7 builds at least) to get the actual address of a function.

void* GetActualAddrOfFunction( void* p )
{
  void* actual = p;
  unsigned char code = 0;
  int sz = NicksModifiedFunction( (unsigned char*)p, &code );

  switch( code )
  {
  case 0xeb:   // Jump short, relative, displacement relative to next instruction
          // 8 bit

    break;
  case 0xe9:   // Jump near, relative, displacement relative to next instruction
          // 32 or 16 bit.
    return (void*)(*(unsigned int*)instr + (unsigned int)p + sz);

    break;
  case 0xff:   // Jump far, absolute indirect, address given in r/m16/32:16/32
          // Jump near, absolute indirect, address given in m16/32:16/32
    break;
  case 0xea:   // Jump far, absolute, address given in operand
          // 32 or 16 bit
    break;
  }

  return actual;
}


int NicksModifiedFunction( void* instr, unsigned char* outCode )
{
  unsigned char* func = (unsigned char*)instr; 
  int total = 0; 

  int operandSize = 4; 
  int FPU = 0; 
  while(*func == 0xF0 || *func == 0xF2 || *func == 0xF3 || (*func & 0xFC) == 0x64 || 
      (*func & 0xF8) == 0xD8 || (*func & 0x7E) == 0x62)
  { 
    if(*func == 0x66) 
      operandSize = 2; 
    else if((*func & 0xF8) == 0xD8) 
    { 
      FPU = *func++; total++;
      break; 
    } 
    func++; total++;
  }

  bool twoByte = false; 
  if(*func == 0x0F) 
  { 
    twoByte = true; 
    func++; total++;
  } 

  unsigned char opcode = *func++; total++;
  unsigned char modRM = 0xFF; 

  if(FPU) 
  { 
    if((opcode & 0xC0) != 0xC0)
      modRM = opcode; 
  } 
  else if(!twoByte) 
  { 
    if((opcode & 0xC4) == 0x00 || (opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || 
(opcode & 0x09) == 0x9) || 
      (opcode & 0xF0) == 0x80 || (opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02 || 
      (opcode & 0xFC) == 0xD0 || (opcode & 0xF6) == 0xF6) 
    { 
      modRM = *func++; total++;
    } 
  } 
  else 
  { 
    if((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D || 
      (opcode & 0xF0) == 0x30 || opcode == 0x77 || (opcode & 0xF0) == 0x80 || 
      (opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02 || (opcode & 0xF8) == 0xC8) 
    { 
      // No mod R/M byte 
    } 
    else 
    { 
      modRM = *func++; total++;
    } 
  } 

  // Skip SIB
  if((modRM & 0x07) == 0x04 &&
    (modRM & 0xC0) != 0xC0)
  {
    func += 1; total += 1;
  }

  // Skip displacement
  if((modRM & 0xC5) == 0x05) {func += 4;total+=4;}  // Dword displacement, no base 
  if((modRM & 0xC0) == 0x40) {func += 1;total+=1;}  // Byte displacement 
  if((modRM & 0xC0) == 0x80) {func += 4;total+=4;}  // Dword displacement 

  // Skip immediate 
  if(FPU) 
  { 
    // Can't have immediate operand 
  } 
  else if(!twoByte) 
  { 
    if((opcode & 0xC7) == 0x04 || (opcode & 0xFE) == 0x6A || (opcode & 0xF0) == 0x70 ||  // Jcc 
      opcode == 0x80 || opcode == 0x83 || (opcode & 0xFD) == 0xA0 ||  // MOV 
      opcode == 0xA8 || (opcode & 0xF8) == 0xB0 || (opcode & 0xFE) == 0xC0 || 
      opcode == 0xC6 || opcode == 0xCD || (opcode & 0xFE) == 0xD4 ||  // AAD/AAM 
      (opcode & 0xF8) == 0xE0 || opcode == 0xEB || opcode == 0xF6 && (modRM & 0x30) == 0x00)  // TEST 
    { 
      func += 1; total += 1;
    } 
    else if((opcode & 0xF7) == 0xC2) 
    { 
      func += 2; total += 2; // RET 
    } 
    else if((opcode & 0xFC) == 0x80 || (opcode & 0xC7) == 0x05 || (opcode & 0xF8) == 0xB8 ||
        (opcode & 0xFE) == 0xE8 || (opcode & 0xFE) == 0x68 || (opcode & 0xFC) == 0xA0 || 
        (opcode & 0xEE) == 0xA8 || opcode == 0xC7 || opcode == 0xF7 && (modRM & 0x30) == 0x00) 
    { 
      func += operandSize; total += operandSize;
    } 
  } 
  else 
  { 
    if(opcode == 0xBA || opcode == 0x0F || (opcode & 0xFC) == 0x70 ||  // PSLLW 
      (opcode & 0xF7) == 0xA4 || opcode == 0xC2 || opcode == 0xC4 || 
      opcode == 0xC5 || opcode == 0xC6) 
    { 
      func += 1; total += 1;
    } 
    else if((opcode & 0xF0) == 0x80) 
    { 
      func += operandSize; total += operandSize; // Jcc -i 
    } 
  } 

  if( outCode ) *outCode = opcode;
  return total;
}

6 Replies

Please log in or register to post a reply.

6e0b32504d31ae07efc17f3322cdb147
0
SnprBoB86 101 Mar 01, 2005 at 19:49

I might be ignorant, but why is this useful? Or is not? And just nifty?

99f6aeec9715bb034bba93ba2a7eb360
0
Nick 102 Mar 01, 2005 at 22:45

Very creative! :D

SnprBoB86, indeed the usefulness is practically zero. A static function can just create any object you need and you can take the pointer of the static function. Anyway, ideas like these can become useful in rare situations like when writing your own debugger or profiling tool, or it can be used to fool crackers, etc. So it’s valuable as an excercise and source of inspiration.

22b3033832c5c699c856814b0cf80cb1
0
bladder 101 Mar 02, 2005 at 01:51

@SnprBoB86

I might be ignorant, but why is this useful? Or is not? And just nifty? [snapback]16368[/snapback]

@bladder

Now I dont really know how much use this actually is, but I figure it’s a pretty neat trick.

;)

6e0b32504d31ae07efc17f3322cdb147
0
SnprBoB86 101 Mar 02, 2005 at 05:50

lol, by the time I skimmed the code to the bottom, I had forgotten reading that.

I’m a dope :) hehe

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Mar 03, 2005 at 10:38

There is always the __ctor and __dtor, but MSVC won’t allow me to take the address of them…

Time fer some hacking :devil:

19019fced8ce0b1cad7d2f22a6946447
0
_FluffysWhole_ 101 Sep 19, 2005 at 22:35

or as nick hinted you could do the following:

typedef (void)pTor*(void*);

template< typename Type >
class FactoryThing
{
public:
  static void ctor( Type* pType )
  {  new ( pType ) Type();
  }
  static void dtor( Type* pType )
  {  reinterpret_cast< Type* >( pType )->~Type();
  }
  static void reset( Type* pType )
  {  dtor( pType ); ctor( pType );
  }

public:
  static pTor GetCtorPtr( void )
  {  return reinterpret_cast< pTor >( &ctor );
  }
  static pTor GetDtorPtr( void )
  {  return reinterpret_cast< pTor >( &dtor );
  }
  static pTor GetResetPtr( void )
  {  return reinterpret_cast< pTor >( &reset );
  }
};

used as:

    pTor pFruitCtor( FactoryThing<Fruit>::GetCtorPtr() );
    pFruitCtor( NULL ); //<- hopefully bang in fruit::ctor :)

or:

    FactoryThing<Fruit>::dtor( NULL ); //<- bang in fruit::dtor :)