Chapter 2.4: Comparing Against Android
Android GD is special for one important reason - it has symbols! What this means is that every function name is present in the code, just like Cocos2d on Windows - and not only their names, but their parameter types aswell! And because GD is a cross-compiled game from a single codebase, this means that we can compare against Android GD to aid reverse engineering other platforms.
Android GD’s binary is called libcocos2dcpp.so
- it contains GD, Cocos2d, and all other dependencies in a single package. However, getting your hands into it is not as easy as Windows, since you need to extract it from the APK - ask some other modder for their copy of it 😉
⚠️ ARMv8 support in Ghidra broke in version 11, so you’d preferably get the ARMv7 version.
Once you have acquired the Android binary, import it to Ghidra the same way you imported Windows, except make sure to disable the Non returning functions: discovered
option for Analysis.
So, let’s verify the REing we did in the last chapter!
Open up the class list on Android and look up LevelSettingsLayer
:
Whoa, that’s a lot of functions… thanks, symbols! The first function we found in the last chapter was the constructor, so let’s navigate straight to it!
Well, hm. There doesn’t seem to be any function named LevelSettingsLayer
here? The destructors are there but no constructor? Suspicious… let’s open up create
instead to see what’s up:
Ah, so that’s what happened - the constructor got inlined. This means that its code was inserted to its call sites and the function definition itself got removed.
Well, if we look at the end of the create
function, we can see the call to init
is there:
Let’s follow the call and compare this to the LevelSettingsLayer::init
we found on Windows:
(Left is Android, right is Windows)
Hmm, alright, both have the call to FLAlertLayer::init
at the start. And both make calls to CCObject::retain
, CCDirector::sharedDirector
, and the CCRect
constructor at similar times… Oh, and there’s a string literal! Both have a call to CCLabelBMFont::create
with the same parameters. String literals are a really good way to compare functions, so this confirms it - we definitely found the real LevelSettingsLayer::init
on Windows.
So, let’s copy the signature from Android to Windows:
Note that the Android function signatures aren’t perfect - they don’t have parameter names, and more crucially they don’t have a return type. You may notice that Ghidra has inferred the type of LevelSettingsLayer::init
to be a void, however we know better than Ghidra that init
functions don’t return void! Manually fixing the return type makes Ghidra add the missing return false
:
So let’s get back to adding the correct parameter types on Windows. The first one is a LevelSettingsObject*
, so let’s add it-
Oh, Ghidra has no clue what a LevelSettingsObject
is. Well, we could manually define the type using the Data Type Manager, but it’s better to let Ghidra make the definition for us by finding some LevelSettingsObject
member function and typing it as __thiscall
:
(LevelSettingsObject::init
was found using the same method as the previous chapters, however replicating it is left as an exercise for the reader!)
Repeat the same for LevelEditorLayer
, and now Ghidra lets us use them as parameter types:
⚠️ The reason the calling convention of
LevelSettingsLayer::init
is__thiscall
will be explained in a later chapter - not all functions have the same calling convention!
And there we go - we can now be pretty much certain that we have found the correct LevelSettingsLayer::init
, and that our signature is right!