Enfusion Engine's scripting language ships with Proto definitions. These connect the C++ code in the engine to the scripting language. A clear example of this is the KickCauseCodeAPI class.
Adding C++ functionality via custom proto functions would be a powerful tool for complex mods. There exists a closed-source way of adding these in interceptAR. I, of course, wanted to learn how intercept was achieving this, so I wanted to build my own! Before getting into the post, you can browse my open-source version of intercept called Infinity on Github.
I broke this challenge into a few steps. I only want to cover a few of them in this post. All of these steps are fully implemented in Infinity.
- Load a library into the server at startup
- Figure out how classes are registered
- Register my own class
- Register my own class functions
- Build out a plugin system
Enfusion Engine is nice enough to provide an easy way to load libraries via CVE-2016-480. This exploit exists in libcurl and causes the game to load secur32.dll from the game directory before looking in c:\system32. I used an open-source project DLLProxyGenerator to create a Proxy Dll of secur32. This will let me build a library that loads into ArmaReforgerServer and ArmaReforgerWorkbench without much effort.
That's step one. Now comes the hard part of learning how classes are registered. To do this, we'll pull open IDA Pro and start reversing Reforger.
I came across these routines that register KickCauseCodeAPI and its functions. The dream goal is to recreate this registration routine for our custom class definitions.
Going further, we'll find the virtual table that contains our routine.
Following xrefs to vftable_KickCauseCodeScriptAPIRegistrator we get to the constructor for this object. With some cleanup, we learn about a global registration linked list. This routine pushes our registrator to the front of the list.
We can find other routines that add registrators to the end of the list.
Exploring the global registration table xrefs, we come across the routine that runs our register method in the registration object.
So far, we have learned that if we can insert our own objects into the registration table, the engine will call our registration routine automatically during script compilation. This is likely very similar to the approach interceptAR takes.
It is time to build our own registrator object that can safely sit inside the global table. I don't want to fill the page with much more code, so I plan to cut it short. I highly recommend reading the implementation of the script registrator here. Here are the class definitions for our custom ScriptRegistrator:
Please note that the virtual functions defined must be created to align with the one shown for KickCauseCodeScriptAPIRegistrator. For derived registrators, we'll need to override the Register routine as BaseScriptRegistrator showcases.
As well, we can now nearly copy+paste from IDA the registration routine.
All that is left is constructing our BaseScriptRegistrator and sticking it into the global linked list. This is super simple, so I won't describe it 🙂.
That explains the complex parts of registering custom script functions. With the knowledge above, the registration of custom classes can be approached in numerous ways. I think the way Infinity and interceptAR do this is the most intuitive.
Update: Infinity has been deprecated. InfinityC is the continuation of the project.