-
-
Notifications
You must be signed in to change notification settings - Fork 13
Making mods #2: Disassembly & Hooking
Make sure you've set up the Mod Loader SDK
You'll want a disassembler. IDA Freeware is a really good candidate for what we're doing. The other disassemblers that you can use include Binary Ninja, Hopper, and objdump. In this tutorial, I'll be using IDA.
Load the bedrock_server in IDA and get yourself a coffee in the meanwhile (as it takes around ~10 minutes or so to load the file).
IDA has two (or three) important views you'll want to use: IDA View (the place with text) and Exports (or Functions in the window on the left, which will not contain static members, but only functions). As mostly everything is exported in the server, you can search in the Exports window just fine. You can also bring up the Names subview (View->Open subviews->Names), which is like Exports, but should show a few more symbols (you shouldn't need to care about it) and you can also show the super-useful Strings subview (View->Open subviews->Strings) which basically indexes the binary and lets you search for fragments of text in the binary. This is way faster than using the built-in search functions in the Search menu.
Make sure to be ready to learn some amd64 assembly in the process ;) Note that HexRays also has a "decompiler" product (I'd say the output is mediocre though), which is rather expensive, but it could assist you in reverse engineering the binary, especially if you're new to assembly code. If you manage to get a copy of it, there's a useful open-source extension for it called HexRays CodeXplorer which should greatly assist you in reverse engineering structures. Note that I (MrARM) do not use the decompiler and instead read the disassembly, however most of the other people in the community do use the decompiler.
First, make sure to read this page: Hooking API
The goal of this tutorial will be writing a simple mod that makes arrows explode.
First, we'll have to hunt for the functions we'll want to use. The first one is a difficult one - one called after an arrow hits a block.
ProTip: You can press the Name column in IDA to sort by name - this is pretty useful.
Let's look for ::onHit functions. This limits us to only a few functions. You probably may at first consider Throwable::onHit(HitResult const&) to be the one. However, if you look at the inheritance tree of Arrow (look for typeinfo for'Arrow) you will find the following:
You should be looking at the comment at the top of the screenshot - you can notice that Arrow extends AbstractArrow which extends Actor (which is how Entity is called in the Bedrock codebase - make sure to remember this!). Throwable is not mentioned in the tree, therefore we can more or less safely exclude it from our list of function of interest. Then there's the ProjectileComponent::onHit(HitResult const&). It sounds interesting, but there's no way to quickly check it in IDA (as it's created as a component from their JSON system), so let's simply try to hook it.
Note: You're probably wondering how to find the functions quickly. Well, there's no simple way. My first try was to filter the IDA Exports tab with Arrow::, but this didn't result in any related matches.
So how would we go about hooking ProjectileComponent::onHit(HitResult const&)?
First we'll need to obtain the mangled name using IDA. Let's double click the function in IDA. IDA will open a graph view (you can switch to a full-text mode using space). No matter which mode you use you will see something looking like this:
The string on the second and third line starting with _ZN is the mangled symbol name - in this case it's _ZN19ProjectileComponent5onHitERK9HitResult. This is the name you'll be using with the hooking framework. On the first line you can also see IDA trying to guess the return type and arguments. According to my experience however, this often fails badly, and don't ever assume that the information in the comment before the line with the mangled name is 100% correct. In this case, for example, the function return type is void, not __int64.
#include <modloader/log.h>
#include <modloader/statichook.h>
using namespace modloader;
#define TAG "ExplodingArrow"
class HitResult;
TClasslessInstanceHook(void, _ZN19ProjectileComponent5onHitERK9HitResult, HitResult const& hitResult) {
Log::verbose(TAG, "ProjectileComponent::onHit");
original(this, hitResult);
}Now let's compile the mod using the method of your choice (see the first part of the tutorial) and start the server using start_modloader.sh (see the homepage on how to set the modloader up). Join the game, pick up a bow and shoot an arrow. You should see Trace [ExplodingArrow] ProjectileComponent::onHit being printed to the console.

