11

For learning (and fun) I have been analyzing a text editor application using IDA Pro. While looking at the disassembly, I notice many function calls are made by explicitly calling the name of the function. For example, I notice IDA translates most function calls into the following two formats.

call cs:CoCreateInstance

Or

call WinSqmAddToStream

But sometimes the format does not use a function name. The following example includes the code leading up to the line in question. The third line of code seem to be "missing" the function name. (The comments are my own.)

mov rcx, [rsp+128h+var_D8]    // reg CX gets the address at stack pointer+128h+var_D8 bytes 
mov r8, [rcx]                 // the address at reg CX is stored to reg r8
call qword ptr [r8 + 18h]     // at address rax+18h, call function defined by qword bytes 

My questions are as follows:

  1. How do I make the connection between call qword ptr <address> and a function in the disassembly?

  2. I understand that IDA cannot use a function name here since it does not know the value stored at the register R8... so what causes this? Was there a certain syntax or convention used by the developer? In other words, did the developer call the function WinSqmAddToStream in a different manner than the function at [r8+18h]?

2
  • These indirect calls are used to implement virtual functions (by using a vtable) in C++ for example.
    – Trass3r
    Commented Oct 4, 2020 at 14:00
  • Also there are plugins like github.com/0xgalz/Virtuailor to automate these tasks.
    – Trass3r
    Commented Oct 4, 2020 at 22:53

2 Answers 2

12

To connect an indirect call to its target (if you know it) you can do the following:

  1. Add a custom cross-reference - either with IDC/Python, or from the Cross References subview. If you use scripting, don't forget to add the XREF_USER flag so IDA does not delete it on reanalysis.

  2. Use the "callee" plugin (Edit→Plugins→Change the callee address, or Alt+F11). This will automatically add a cross-reference and also a comment next to the call.

As for why the explicit call is not present in the binary there can be many explanations. The snippet you're showing looks like a virtual function call, and they are usually done only in this manner to account for possibility of the method being overridden in a derived class.

2
  • Thanks for the answer. This will be very helpful once I know the target function. I still don't know how to determine the function that is being called. I'm going to edit the question to indicate that I'm more interested in that part of it.
    – clark
    Commented May 28, 2013 at 14:22
  • 1
    @clark Try putting a breakpoint on the instruction, running, and then stepping into / manually calculating the offset from the available registers? Or it has to be statically?
    – Martin
    Commented Oct 26, 2016 at 22:41
3

The trick is to find the object's constructor. Let's suppose the code looks like this:

a = new CFoo();
a->bar();

The compiler (I assume MSVC, 32bits) might produce something like this:

push 12h ; size_t
call ??2@YXYXY@Z  ; operator new(uint)
mov [ebp+var_8], eax
mov esi, eax
test esi, esi
jz loc_1
  mov ecx, esi
  call ??0CFoo@@AAAA@AA ; CFoo::CFoo(void)
  mov [ebp+var_8], eax
  jmp loc_2
loc_1:
  mov [ebp+var_8], 0
loc_2:
...
...
...
mov eax, [ebp+var_8]
mov ecx, [eax]
mov ebx, ecx
mov ecx, [ebp+var_8]
call dword ptr [ebx+08h]

Looking at ??0CFoo@@AAAA@AA, a.k.a. CFoo::CFoo():

...
mov esi, ecx
mov dword ptr [esi], unk_12345
...

unk_12345 is CFoo's virtual table offset:

unk_12345:
  dd offset sub_23456
  dd offset sub_34567
  dd offset sub_45678
  ...

And that sub_45678 at unk_12345+08h (which would be 3rd entry, in this case) is what gets called, i.e. CFoo::bar().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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