Hacking Android Game using Frida - Part3
Hacking Android Game with Frida (Part3)
In the previous blog post, we discussed how to create a NDK project to interact with an Android game. In this follow-up article, we will delve deeper into the game’s internals by call fucntions in libmain.so
.
Analyzing libmain.so
We have chosen a game for analysis because its libmain.so contains a multitude of interesting functions. To examine the symbols exported by the library, execute the following command:
1 | readelf -sW libmain.so |
In our case, the library exports over 36,000 symbols. In theory, we can call each exported symbol using C++. This presents an exciting opportunity for exploration.
Conducting Static and Dynamic Analysis of libmain.so
Before calling these functions from C++, it is essential to establish a goal. In our current scenario, our goal is relatively simple: we want to list all assets in the game, including their names and types. To accomplish this, we need to extract relevant class definitions from libmain.so
. For static analysis, we will employ Ghidra, while Frida will assist us in adding hooks to key functions for inspecting the game’s internal state and memory data. Please note that this process is complex and time-consuming, but the results are well worth the effort. While we won’t delve into the entire analysis process in this blog, we will share the final results. The following are the classes we extracted for listing all assets in the game:
Note: These class definitions are highly dependent on the version of libmain.so used. The MD5 hash of the libmain.so file we worked with is e721395e3e327899a5a55ea4fb422a1c
. Additionally, to ensure compatibility with a C compiler and facilitate analysis in Ghidra, I have utilized __cplusplus macros in the code. Ghidra supports C code, and by using these macros, we can import the code into Ghidra for easier analysis. This allows us to leverage Ghidra’s capabilities while working with the codebase.
VuAsset
1 | // VuAsset |
The VuAsset
class has a member variable, _name
, of type std::string at offset 0x08, used to store the names of assets. In the ARM64 platform, a pointer occupies 8 bytes.
VuAssetDB
1 | // VuAssetDB size 0xe0 |
The VuAssetDB class
contains a member variable of type std::map<std::string, std::vector<std::string>>
at offset 0x40, used to store the names of assets. The key of this variable is of type std::string
, storing asset type names, while the value is of type std::vector<std::string>
, storing asset names of the same type. We will utilize this class to print all asset names and types in the game.
VuAssetFactory
1 | // VuAssetFactory size 0x190 |
The VuAssetFactory
class includes a member variable of type std::vector<std::string>
at offset 0x38, used to store asset type names. We can utilize this member variable to print all asset type names. Additionally, this class has a member variable of type VuAssetDB*
at offset 0x68 to store the pointer to VuAssetDB. Furthermore, it contains a member variable of type std::unordered_map<unsigned int, VuAsset*>
at offset 0x78, used to store loaded assets. The key of this variable is of type unsigned int, representing the asset hash calculated by asset name, and the valueis of type VuAsset*
, storing the asset pointer. Lastly, the class includes a static member variable of type VuAssetFactory*
to store the global instance of VuAssetFactory. This class follows the singleton design pattern, allowing us to obtain the global VuAssetFactory instance using mpInterface.
Helper Function for Obtaining the Actual Class Name of VuAsset Children
Based on our analysis, we have observed that VuAsset
acts as a base class for other asset-related classes. To simplify our tasks, we have implemented a helper function that allows us to retrieve the actual class names. Please find the code snippet below:
1 | static const std::type_info& getTypeInfoOfInstance_ndk(void* p) |
Given that libgame.so
is compiled with the RTTI (Run-Time Type Information) option, we can utilize the getTypeInfoOfInstance_ndk
function to obtain the class information of instances. It’s important to note that we cannot directly use the typeid operator to retrieve runtime type information (RTTI) of an object. This is because libgame.so
contains the RTTI information, and when we write our C++ code in libmousebot.so
, the typeid operator will provide the RTTI information from libmousebot.so
, which may lead to incorrect results. Since we don’t have visibility into how the derived classes of VuAsset
are defined, the getTypeInfoOfInstance_ndk
function helps us retrieve the accurate class information from libgame.so
.
Print all assets names and types
Now, it’s time to rock! In this section, we will demonstrate how to print all asset names and types using functions provided by libgame.so. This will serve as a great starting point for exploring Frida’s capabilities and the powerful combination of Frida and C++.
Get global pointer to the instance of
VuAssetFactory
To begin, we need to obtain the global pointer to the instance ofVuAssetFactory
. This can be achieved with the following code snippet:1
auto* pVuAssetFactory = VuAssetFactory::mpInterface;
Get the instance of
VuAssetDB
Next, we retrieve the instance ofVuAssetDB
using the pointer obtained in the previous step. The code is as follows:1
auto* pVuAssetDB = pVuAssetFactory->_vuAssetDB;
List all assets in
VuAssetDB
In this step, we iterate through all the assets inVuAssetDB
and print their names and types. The code snippet below demonstrates this process:1
2
3
4
5
6
7for( auto it = pVuAssetDB->_assetNames.begin(); it != pVuAssetDB->_assetNames.end(); ++it){
auto& assetType = it->first;
auto& names = it->second;
for(auto it1=names.begin(); it1!=names.end(); ++it1){
LOG_INFOS(" %s : %s", assetType.c_str(), it1->c_str());
}
}Here, the
LOG_INFOS
macro is used to print the asset information, which internally calls the_frida_log
function.
The resulting output will be similar to the following:1
2
3
4
5<...>
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:104] VuTextureAsset : UI/Icons/Inventory_Prize
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:104] VuTextureAsset : UI/Icons/Inventory_Skin
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:104] VuTextureAsset : UI/Icons/KeyCard_A
<...>List All Loaded Assets in the Game
In this final step, we will list all the loaded assets in the game. AlthoughVuAssetDB
contains all the assets, it only provides basic information for each asset. During the game runtime, the actual asset data is loaded on demand.
The code snippet below demonstrates how to iterate through the loaded assets and print their information:1
2
3
4
5
6
7
8auto& v = pVuAssetFactory->_loadedAssets;
for(auto it = v.begin(); it != v.end(); ++it){
auto* pAsset = it->second;
auto& k = it->first;
auto& assetTypeInfo = getTypeInfoOfInstance_ndk(pAsset);
const char* assetTypeName = assetTypeInfo.name();
LOG_INFOS(" %x pAsset %p assetTypeName %s assetName %s", k, pAsset, assetTypeName, pAsset->_name.c_str());
}In this code, we use the
getTypeInfoOfInstance_ndk
function to retrieve the actual class name of each asset instance. The resulting output will be similar to the following:1
2
3
4
5<...>
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:118] e1fc0284 pAsset 0x77e86e1e40 assetTypeName 18VuStaticModelAsset assetName Env/Hall/Straight_16m
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:118] 93b383af pAsset 0x77c03b1f00 assetTypeName 15VuMaterialAsset assetName Paper/Cardboard_Wall
[/mnt/work/work.2023/frida-hackinggame/jni/mousebot.cc:118] 9f8e3cbb pAsset 0x77b8be9810 assetTypeName 15VuTemplateAsset assetName Tile_Hall_Hazard/Hall_Hazard_Stomper_2x_16m
<...>
You can find the complete source code in the repository
Conclusion
Congratulations! This marks the end of our exploration series. In this final log, we have successfully printed all asset information in the game using the functions provided by libgame.so
. This serves as a solid foundation for further exploration of Frida’s capabilities. The combination of Frida and C++ opens up a world of possibilities, allowing us toperform various interesting tasks. With the knowledge gained from this series, you can continue your journey of hacking and exploring the game.
Remember, Frida and C++ together are a powerful toolset that can enable us to accomplish a wide range of tasks. The ability to access and manipulate game assets opens up exciting possibilities for customization, analysis, and experimentation.
Happy hacking, and enjoy your adventures in the world of game exploration!