4

I'm currently reversing two binaries compiled using the same exact C++ codebase. The two binaries were compiled on two different systems with different architectures: x64 on Windows and ARMv7 on Android. The Windows binary was compiled with MSVC, while the Android binary I believe was compiled with GCC (may also be Clang). Lastly, the Android binary has its symbol table completely intact, while the Windows binary has RTTI which I can use to name vtables.

I'm using the Android binary as reference to help reverse the Windows binary. What I've noticed so far is that vtable entries between the two binaries seem to be in the same order, i.e. the same function can be found at the same vtable index in both binaries. However, what I'm wondering is if it's safe to assume they will always be in the same order, or if the order changes depending on the compiler.

2

In General, with modern operating systems, the order won't depend on the compiler, but it may depend on the operating system.

Any modern operating system does not just define the operating system API, but also, lots of standard libraries that make it easier for programs to access that API. Also, there's a lot of libraries that offer services that don't translate directly to the OS API, but provide a generalized API for something else. Like QT for Linux, or MSVC for windows.

If you're a compiler vendor (for a relaxed definition of "vendor" as most compilers are free today), you want the code that your compiler generates to be able to interface with these standard libraries. If you try to get people to use "your" compiler, but tell them they can't use the interfaces the OS, or standard libraries, provide, you'll have a hard time convincing them. Which means, any compiler generating Windows code will make sure it adheres to the MSVC standards. Any compiler generating Linux code will adhere to the Linux/BSD standards of gcc . This is not just about layout of vtables, it encompasses name mangling, register and stack usage for function arguments/return values, handling of floating point, and lots of other stuff as well. The name for that is the ABI (application binary interface).

Note that this wasn't always the case; in the late days of DOS/early days of Windows, when Microsoft C(++) and Watcom C(++) were the (most important) competitors, they had hugely different ABIs. You couldn't (without going through a lot of extra hoops) link a program compiled with Watcom to a library compiled with MS, but that wasn't a big deal then, as the compilers included all libraries anyways, and open source libraries to be included in your program were (mostly) unheard of. (Watcom didn't even use vtables back then; they used function pointers within each object that got instantiated every time a new object was created). So, the farther you go back in time, the less probable it is that two compilers for the same OS share the same ABI.

Across operating systems, you don't have this neccesity to make libraries compatible, so there's more leeway in handling things differently. However, you'll typically make the same decicions when defining your ABI. Your class constructor will typically be the first vtable entry as every class needs a constructor. The destructor will typically be the second entry as every class needs a destructor. The other vtable entries will typically be next in the vtable, in the order they appear in the source code. Not because you try to be compatible with anything else; just because it's the easiest way to implement something you need to implement.

Which also means you can't rely on that. If there's a new company, let's call them Orange, designs a new device with an operating system they call O-IS (Orange Integration System), they might want to roll their own compiler. Maybe they decide that, in their ABI, method names should be sorted alphabetically in their vtables. Maybe they make decisions how to handle multiple inheritance and and virtual functions that are different from how MSVC and gcc handle these things. And when O-IS comes out, and the gcc people decide they want gcc to be able to compile for O-IS, they'll adjust their code generation to the new standard that Orange set for O-IS.

Sometimes, companies try to agree on an ABI for a new processor, like this for the ill-fated Itanium. Of course, these are recommendations, not laws, so an operating system/device vendor may or may not choose to adhere to them.

So, to compare vtables between different compilers and operating systems, you need to check ABI definitions of these compiler for these operating systems.

Wikipedia's entry shows (a part of) how gcc handles the Linux ABI, and while i wasn't able to find any official documentation by MS about how MSVC does it, openrce.org has an article about that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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