0

I apologise for the length of this post, though I have taken the time to make it as readable as I can and included my ideas and thinking where possible so this post might be useful to other readers later down the track.

For context, I've started trying to learn some reverse engineering. After reading as much as I could find on the subject of reversing C++ and playing around with some trivial examples (that I wrote and compiled myself), I thought I would try my hand at a program I'm familiar with but don't have the source code for.

Loading that Win32 C++ program (compiled with Visual Studio if that helps) into IDA Pro I see it has the following WinMain function:

; int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
__stdcall WinMain(x, x, x, x) proc near

sub     esp, 0BCh
mov     eax, off_7CA000                           ; A pointer to some other string
push    ebx                                       ; char (IDA generated comment)
lea     ecx, [esp+0C0h+var_B4]
push    ecx                                       ; int (IDA generated comment)
mov     ecx, offset unk_8C16C0                    ; size_t (IDA generated comment)

...     ; local variable init starts, including:

mov     [esp+0C4h+var_B4], offset aSomeString     ; "Some String"
mov     [esp+0C4h+var_AC], eax

...     ; local variable init finishes

call    sub_407530
mov     dword_8C16C4, offset off_8C16BC
mov     dword_8C16C8, offset sub_242CE0
call    sub_4AF9C0
...                                               ; rest of WinMain(..)

Although the order of the ops is a little strange compared to what I have seen (local vars are initialised in between pushing a pointer onto the stack and ECX being initialised for use in a later sub routine call), I guess I have the following:

  1. var_B4 is pushed onto the stack
  2. offset unk_8C16C0 is placed into ECX
  3. Local vars are initialised (where we find out var_B4 is a char*)
  4. The sub routine sub_407530 is called

So perhaps offset unk_8C16C0 is a this pointer for some global class (global because we're so early on in WinMain) and sub_407530 is a __thiscall to a member function of the class pointed to by offset unk_8C16C0 that takes a single char* parameter.

Checking the cross references to offset unk_8C16C0 I see two, so perhaps one is a constructor and the other a destructor. One of them is:

sub_687C30      proc near                        ; DATA XREF: .rdata:006BF608

var_C   = dword ptr -0Ch
var_4   = dword ptr -4

mov     eax, large fs:0
push    0FFFFFFFFh
push    offset SEH_687C30
push    eax
mov     large fs:0, esp
mov     ecx, offset unk_8C16C0                   ; this
mov     [esp+0Ch+var_4], 0
call    Concurrency::details::_ReaderWriterLock::_ReaderWriterLock(void)
push    offset sub_6BA2B0                        ; void (__cdecl *)()
call    _atexit
mov     ecx, [esp+10h+var_C]
mov     large fs:0, ecx
add     esp, 10h
retn
endp

Great, my suspicions seem validated, offset unk_8C16C0 seems to point to a global reader_writer_lock object, the above block initialises it and registers sub_6BA2B0 (the only other reference to offset unk_8C16C0) to run on exit via a call to _atexit, so that is likely our destructor.

But it's at this point I start to have some doubts about my suspicions ... the above block is referring to a details namespace, which upon a little more research seems to be more related to the internals of reader_writer_lock. I'm also not sure why this initialisation block is so complicated. And finally, the only reference to this block is in the executables read only data section which itself has no references, so I feel like I'm at a bit of a dead end here.

Nevertheless, moving forward to the call to sub_407530 in WinMain, I see:

; int __thiscall sub_407530(size_t, int)
sub_407530      proc near        ; CODE XREF: WinMain(x,x,x,x)

arg_0   = dword ptr  8

push    ebp
mov     ebp, esp
push    esi
push    37628                    ; size_t
mov     esi, ecx
call    operator new(uint)
add     esp, 4
test    eax, eax
jz      short loc_407550
mov     ecx, eax
call    sub_406B30
jmp     short loc_407552

loc_407550:                      ; CODE XREF: sub_407530+15
xor     eax, eax

loc_407552:                      ; CODE XREF: sub_407530+1E
mov     [esi], eax
mov     dword_DC32B8, eax
mov     eax, [ebp+arg_0]
mov     ecx, [esi]
push    eax                      ; int
push    esi                      ; size_t
call    sub_407360
pop     esi
pop     ebp
retn    4
sub_407530      endp

The first half seems straight forward enough, 37628 bytes are allocated for a new object and sub_406B30 is a constructor for that object:

sub_406B30      proc near                       ; CODE XREF: sub_407530+19
xor     eax, eax
push    esi
mov     esi, ecx
mov     dword ptr [esi], offset const Blah1::Blah2::`vftable'
mov     [esi+10h], eax
mov     [esi+8], eax
mov     [esi+0Ch], eax
mov     dword ptr [esi+4], offset const    Something::List<Blah1::Blah2::Blah3,-1>::`vftable'
mov     [esi+24h], eax
...

Back to sub_407530(size_t, int), from loc_407552 down it's not clear to me what's happening:

  1. The newly created object seems to be replacing the _ReaderWriterLock object (whose reference was moved to ESI earlier in the sub-routine), which doesn't make sense to me. It certainly doesn't seem like this would be a member function as I suspected.
  2. The new object (as a this pointer via ECX), an explicit pointer to the new object, and the char* from WinMain are being passed to a new sub routine. I don't understand why two pointers to the same object would be passed, although not incredibly important on its own it adds to my doubts.

I'm sure I'm interpreting this incorrectly, so this brings me to my question:

Broadly speaking, is my thinking on track? Are there any obvious errors in my analysis so far?

More specifically, what is happening with offset unk_8C16C0 / sub_687C30 at the start, and how is loc_407552 in sub_407530(size_t, int) related and interacting with it? Thanks for taking the time to read.

  • It's pretty hard to answer without knowing both what the executable is about and what is your objective. Are you trying to understand in-deep the mechanisms of global objects for this version of your compiler, or trying to understand what the program is doing? – Dillinur Nov 26 '15 at 22:46
  • I think an in depth knowledge of the mechanisms of global objects is going to serve me better. I basically know what the program is doing at this point, so I'm not worried about that, I am just struggling to "decompile" ... piecing together these pieces to understand how it does it. – Benjamin Tillman Nov 26 '15 at 23:34
  • Oh, and the executable is an old game executable. – Benjamin Tillman Nov 26 '15 at 23:35
  • In that case, _ReaderWriterLock constructor is likely a false positive, since it only appeared in VS2010 or so. So you may want to inspect its code too. – Igor Skochinsky Nov 28 '15 at 14:06
1

call 407530 tries to allocates memory
if memory allocation succeeded 406b30 is called
this call initializes unk_804xxxx with stuff
if memory allocation failed unk_804xxxx is intialised with 0

    test    eax, eax
 |<--jz      short loc_407550
 |   mov     ecx, eax  <----------- address of newly allocated memory
 |   call    sub_406B30 
 |
 |-->407550: xor     eax, eax <--------- else 0 

both paths success and failure of memory allocation are lead to 407552
if failure eax holds 0
if success it holds the return from 406b30 (possibly the return from new();

 407552:mov     [esi], eax   

esi holds the offset_unk_80xxx here as well as inside the call
so ultimately offset_unk_80xxx is initialised with either 0
or address of memory allocation

  • Thanks for the reply Blabb. It's clear to me that some memory is being allocated and an object (a C++ class) is being initialised by 406B30 as you said. What isn't clear to me is that, if I'm reading things correctly, offset unk_8C16C0 has already been initialised by 687C30 and then loc_407552 seems to replace unk_8C16C0 with the mov [esi], eax. – Benjamin Tillman Nov 27 '15 at 14:46
  • i don't see the connection why does 687xxx come into picture in the flow you post ? winmain has 2 calls 407350,4af9c0 407350 has 3 calls new, 406b30,407360 406b30 doesnt have any calls so what is the connection to that 687xxx none apparently decipherable from your post – blabb Nov 27 '15 at 16:54
  • offset unk_8C16C0 is referenced in three places, in WinMain, and also by sub_687C30 (posted in the question) and sub_6BA2B0 (which you can see being called by sub_687C30). I assumed that offset unk_8C16C0 was pointing to a global object (constructed by sub_687C30 and destructed by sub_6BA2B0) based on the code in those two sub routines. I did try to explain this in my question so I apologise if that wasn't clear. – Benjamin Tillman Nov 27 '15 at 17:04
  • 406b30 pushes esi ( the unk_) so it is likely to pop it back the call also xors eax,eax so it likely has a mov eax, some result so on returning from that function unk_xx is initialised with eax or 0 – blabb Nov 27 '15 at 18:55

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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