AurieFramework / YYToolkit

The definitive internal modding tool for GameMaker games
GNU Affero General Public License v3.0
72 stars 10 forks source link

Script callback not running #53

Closed PippleCultist closed 11 months ago

PippleCultist commented 12 months ago

I put a PrintMessage at the beginning of the ScriptCallback function in the Chapter2_EnableDebug example plugin and tried running it with Holocure, but the ScriptCallback function was never called.

Archie-osu commented 12 months ago

The script callbacks only work on VM games, they are never invoked on YYC due to the compiler linking and calling the functions directly.

To intercept such scripts, use the MmGetScriptData export (might not work on all GM versions, but try your luck), then use MinHook or some other hooking framework to intercept the target function.

PippleCultist commented 12 months ago

How do I get the function address from a CScript? I tried using s_pFunc, but none of them were executable addresses. Also, I did notice that the s_code->i_pCode was showing a completely different code event from the the script I was trying to get.

Archie-osu commented 12 months ago

Are you subtracting 100000 from the ID before calling MmGetScriptData?

PippleCultist commented 12 months ago

Yeah. I get 107480 when I look for gml_Script_SpawnMob_gml_Object_obj_MobManager_Create_0 which I then get the script for by querying with index 7480. The s_pFunc ends up being a nullptr in this case. I also tried looking for a different script gml_Script_CalculateScore which gave me 101589, and this one had an s_pfunc. The hook was giving me non executable code error when I tried to hook the pScriptFunc/pFunc though. Both of their s_script showed the correct names for the script, but the s_code->i_pCode is showing a different code event from where the script is actually located (Eg. gml_Script_SpawnMob_gml_Object_obj_MobManager_Create_0 showing gml_Object_obj_MobManager_Other_10 instead of gml_Object_obj_MobManager_Create_0)

Archie-osu commented 12 months ago

Curious, can you show the code you have that exhibits this issue?

PippleCultist commented 12 months ago
int spawnMobIndex = -1;
int calculateScoreIndex = -1;

CallBuiltin(Result, "asset_get_index", nullptr, nullptr, { "gml_Script_SpawnMob_gml_Object_obj_MobManager_Create_0" });

//Ends up being 7480
spawnMobIndex = static_cast<int>(Result) - 100000;
PrintMessage(CLR_DEFAULT, "spawnMobIndex: %d", spawnMobIndex);

CallBuiltin(Result, "asset_get_index", nullptr, nullptr, { "gml_Script_CalculateScore" });

//Ends up being 1589
calculateScoreIndex = static_cast<int>(Result) - 100000;
PrintMessage(CLR_DEFAULT, "calculateScoreIndex: %d", calculateScoreIndex);

CScript* spawnMobCScript = nullptr;
CScript* calculateScoreCScript = nullptr;

MmGetScriptData(spawnMobCScript, spawnMobIndex);

//Prints out "gml_Script_SpawnMob_gml_Object_obj_MobManager_Create_0 gml_Object_obj_MobManager_Other_10 0"
PrintMessage(CLR_DEFAULT, "spawnMobCScript: %s %s %08x", spawnMobCScript->s_script, spawnMobCScript->s_code->i_pCode, spawnMobCScript->s_pFunc);

MmGetScriptData(calculateScoreCScript, calculateScoreIndex);

//Prints out "gml_Script_CalculateScore gml_GlobalScript_input_binding_get_verbs (non-zero address)"
PrintMessage(CLR_DEFAULT, "calculateScoreCScript: %s %s %08x", calculateScoreCScript->s_script, calculateScoreCScript->s_code->i_pCode, calculateScoreCScript->s_pFunc);
Archie-osu commented 12 months ago

The code doesn't seem wrong, will have a closer look tomorrow, thanks for reporting this - might be a bug on YYTK's end.

PippleCultist commented 11 months ago

Getting the pScriptFunc from the code event and hooking based off of that seems to work. It seems like it's an issue with MmGetScriptData/CScript.

Archie-osu commented 11 months ago

Try replacing CScript with this struct

struct alignedTo(4) CScript
{
    void** pVMT;
    // CStream* s_text; // not in newer runners
    CCode* s_code;
    YYGMLFuncs* s_pFunc;
    CInstance* s_pStaticObject;

    union
    {
        const char* s_script;
        int s_compiledIndex;
    };

    const char* s_name;
    int s_offset;
};
PippleCultist commented 11 months ago

Commenting s_text makes s_name match s_pFunc->pName for the script name, but trying to hook s_pFunc->pScriptFunc is still giving non executable errors. I compared that address that the CScript is giving for pScriptFunc with the CodeEvent for the same script name, and it seems like there's around a 0x2000 difference.

Archie-osu commented 11 months ago

That's two pages of difference - mind querying the non-executable page and listing what protection it has applied, and the owner module of the page (ie. is it the game's memory)?

PippleCultist commented 11 months ago

Sorry, I was looking at the wrong pScriptFunc for the code event. The issue was that I was trying to use a lambda function as the detour function. The hooks work after changing them to regular functions.