Who crashed?

Last week I was investigating a crash originating somewhere in a code that looked like this (GPF in this C++ line):

obj->GetFoo()->GetBar().Call(player->GetCat(), this, &MyType::SomeFunc, moreArgs

We could discuss the number of indirections or the fact that if this code had been split into multiple lines it’d be obvious, but that’s not the main point here. I didn’t have this luxury, I had to find out which pointer exactly was NULL here.

Let’s take a look at the assembly first, this should give us more info:

 1lea         rdx,[rsp+58h]  
 2mov         rcx,qword ptr [rax]  
 3mov         rax,qword ptr [r14+0B20h]  
 4mov         rdi,qword ptr [rcx]  
 5mov         rcx,qword ptr [rax]  
 6call        Object::GetFoo (07FF7A08AA2C0h)  
 7mov         rcx,rdi  
 8mov         rdx,qword ptr [rax]  
 9mov         rax,qword ptr [rdi]  
10mov         rbx,qword ptr [rdx]  
11call        qword ptr [rax+0E8h]  *** crash here
12mov         rdx,qword ptr [rbx]  
13mov         rcx,rbx  
14mov         rdi,rax  
15call        qword ptr [rdx+1E0h]  
16mov         dword ptr [rsp+38h],3  
17lea         r9,[MyType::SomeFunc (07FF7A0DE7C6)]  
18mov         byte ptr [rsp+30h],0  
19mov         rcx,rax  
20mov         dword ptr [rsp+28h],3  
21mov         r8,r14  
22mov         rdx,rdi  
23mov         qword ptr [rsp+20h],r15  
24call        Mgr::Call(07FF7A0DD87C0h)  

At the first glance, it might seem like the result of Object::GetFoo is null, as GPF occurs almost immediately after the call. Looking closer at the assembly, it doesn’t look like the result (RAX) is being used there, though. Is it possible it’s actually player pointer that’s invalid? We could analyze the assembly in more depth, but another way would be to determine the types of our pointers. We seem to be calling a bunch of virtual functions here, so if we know what method resides under [rax+0xE8], we’d be able to tell the type. If it’s GetBar() then it’s indeed result of GetFoo causing issues, if it’s GetCat -> it’s player.

How do we find the vtable for a given type, though? I’m not sure how to do it in Visual Studio, to be honest (my best bet would be to search memory for an address of one of the virtual methods, but the search memory command seems to be removed/well hidden in 2015), but it’s trivial in my old friend WinDbg:

0:000> x *!Foo*`vftable'
00007ff7`a1c70f78 App_x64!Foo::`vftable' = <no type information>
x = examine symbols, it display symbols that match the specified patterns. Now we know the vtable for the Foo type resides at 0x00007ff7a1c70f78.

All we have to do is to check what’s at 0xE8:

:000> dqs (00007ff7a1c70f78 + 0xE8) L1
00007ff7`a1c71060  00007ff7`a0e90b60 App_x64!Foo::GetAnimationMgr
dqs = display qwords (8b) at given address (L1 = we only want 1 entry)

Hmm, OK, doesn’t seem like it’s Foo that’s null then, if it was we’d see GetBar at offset 0xE8. Let’s try Player class now:

0:000> x *!Player*`vftable'
00007ff7`a1bd2870 App_x64!Player::`vftable' = <no type information>
0:000> dqs 00007ff7a1bd2870+0x0E8 L1
00007ff7`a1bd2958  00007ff7`a0909310 App_x64!Player::GetCat

Boom, that’s what we wanted to see. Application has crashed while trying to jump to GetCat using Player’s vtable, so it was the player pointer that was null. Now to find wht it’s the case, but that’s another story…

More Reading