Simple script injection – OnEachFrame

So I learned about OnEachFrame the other day https://community.bistudio.com/wiki/onEachFrame and thought I’d take a look at it.


As you can see, all it does is take the ArmaString pointer from the argument object and put it in a global GameValue object. That’s it – that’s all OnEachFrame is. It’s a global GameValue object that has its text, if there is any, executed on each frame. So I figured, why not make a really simple executor from it?

</div>
<div>// A2 allocator.  Not used for everything.</div>
<div class="alt2" dir="ltr">typedef void *(WINAPI* A2Malloc)(SIZE_T);
typedef void (WINAPI* A2Free)(void*);
A2Malloc MemAlloc = *(A2Malloc*)0xDBF2A0;
A2Free MemFree = *(A2Free*)0xDBF2A4;
// To get with sig:
/*
DWORD mallocObject = DUtils::findPatternPlusBytes((DWORD)a2oaModule, (DWORD)a2LastModuleByte, "FF 15 ? ? ? ? 8B F8 85 FF 75 54", 2);
MemAlloc = *(A2Malloc*)mallocObject;
MemFree = *(A2Free*)(mallocObject + 4);*/


class ArmaString {
public:
int References;
int StringLength;
char AString[1];

// Call this to create a new ArmaString.  We're basically using it as a constructor without setting up an actual Allocator for A2Malloc
static ArmaString* CreateArmaString(const char* text) {
if (!text) return 0; // you're retarded

int length = strlen(text);
ArmaString *newArmaString = (ArmaString*)MemAlloc(length + 9);
if (!newArmaString) return nullptr;        // Shouldn't happen, if your MemAlloc pointer is wrong you'll crash on the call unless you get REALLY lucky
newArmaString->References = 1;
newArmaString->StringLength = length;
memcpy(&newArmaString->AString, text, length + 1);

return newArmaString;
}
};
// Kept the inheritance model in case you guys want to do things with scalars or some shit.
class GameData {
public:
void* GameDataTypeVTable;
int References;
void* DebugValueVTable;
};

class GameDataString : public GameData {
public:
ArmaString* Data;

// Calling the engine's constructor is going to be easier than manually doing our own.
typedef GameData* (__thiscall* GDConstructor)(GameData* thisptr, ArmaString* initialValue);
static GDConstructor Constructor;
GameDataString(ArmaString* initialValue) {
if (Constructor != nullptr) // If your constructor pointer is null, your object is going to be empty and you're going to crash
Constructor(this, initialValue);
References++;
}
};
// 0x9D51CB.  No unique sig, other ways to find it programatically at runtime
GameDataString::GDConstructor GameDataString::Constructor = (GameDataString::GDConstructor)0x9D51CB;

class GameValue {
public:
void* GameValueVTable;
GameData* Value;
};

GameValue* OnEachFrame = (GameValue*)0xDB0614;

void InjectScript(const char* scriptText) {
ArmaString* script = ArmaString::CreateArmaString(scriptText);
GameDataString* data = new GameDataString(script);
OnEachFrame->Value = data;
}

void ExecuteScriptFile(std::string filePath) {
// execVM "filePath"; onEachFrame{};
// gotta clear the onEachFrame event since I know you tards won't do it yourselves
std::string scriptText = "execVM \"" + filePath + "\"; onEachFrame{};";
InjectScript(scriptText.c_str());
} 


Super simple, able to be done externally without any nasty CreateThread function calls from the outside (pump up the reference count on your strings and GameData objects so they don’t get freed by Arma), and very stable.  Will port to A3 and SA very soon.

Advertisements

One thought on “Simple script injection – OnEachFrame

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s