GameMaker CScript Manipulation

Post here (make sure thread doesn't exist first) any type of tutorials: text, images, videos or oriented discussions on specific games. No online-related discussions/posts OR warez!
Post Reply
aSwedishMagyar
Table Makers
Table Makers
Posts: 436
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 532

GameMaker CScript Manipulation

Post by aSwedishMagyar »

I have been tinkering around with gamemakers games for a little while now and I think I’ve come across some info that can actually be useful.

This is a part 1 meant for dealing with CScripts in GameMaker Studio v3.1.4 specifically though the concepts can be carried over to other versions.

No doubt many of you have had issues with finding good compare values or tried backtracing to find a function start with no such luck. Well that is due to the game using these CScript objects to determine the function structure.
What I have found is these CScript instances are stored at a location and indexed depending on which has been called.
To start here are some useful functions that you can use once you find the CScript table base address:

Code: Select all

function findCScript(scriptname)
	local base = getAddressSafe('[baseCodes]')
    local numCodes = readInteger(getAddressSafe('baseCodes+8'))-1
    for i = 0,numCodes do
    	local name = readString(readPointer(readPointer(readPointer(base+i*4)+0x8)+0x5C),100)
        if scriptname == name then return i end
    end
    return nil
end
function registerCScripts()
	local base = getAddressSafe('[baseCodes]')
    local numCodes = readInteger(getAddressSafe('baseCodes+8'))-1
    for i = 0,numCodes do
    	local name = readString(readPointer(readPointer(readPointer(base+i*4)+0x8)+0x5C),100)
		_G[name]=i
    end
end
function dumpCScripts()
	local base = getAddressSafe('[baseCodes]')
    local numCodes = readInteger(getAddressSafe('baseCodes+8'))-1
    for i = 0,numCodes do
    	local name = readString(readPointer(readPointer(readPointer(base+i*4)+0x8)+0x5C),100)
		print(name)
    end
end
Here is a basic table that hopefully will work for most games that this method is compatible with. It will find the CScript base address and you can use the Dump CScripts to get all of the script names:
Gamemaker_CScript_Helper.CT
20-04-2021
(12.39 KiB) Downloaded 78 times
Once you find this section below:

Image

You can register the CScript table base address (what is being moved into ECX) as baseCodes and run the dumpCScript()
function included at the top of this post which will print all of the gml_Script_... names that are currently in use.

Image

Do a short break and trace from here to get back to the main CScript Calling function:

Image

Use registerCScripts() and print the hex notation of the script instance you are looking for:

Image

Set the breakpoint as shown below:

Image

After it breaks on your condition, set another breakpoint at the location shown (the first push 00) and run until that point, take the contents of EDX (or whatever other register is used) and do a structure dissect:

Image

In this case (keep in mind I made the structure elements manually so you won't see the names like this) we can see that EDX contains a pointer to the function arguments where the damage number is at EDX+10 and the ID is at EDX+20. Using that info you can make a script to check the sign of EDX+20 and if not signed (as that is the indicator of whether it is enemy damage or not) you modify your damage.

Another example would be for spending gold where if you encounter the spend gold script, you set the first argument (gold cost) to 0. Basically, at this point you are doing a compare with which script is being called and then modify its input parameters.

In other cases, you may not want the function to run at all in which case you can set the vmBuffer value to 0, just be careful that the script you want to stop running does not return any important information. Here is an example of doing that:

Code: Select all

{$lua}
if syntaxcheck then return end
local off = findCScript('gml_Script_scr_mana_withdraw')
if off == nil then error("Script not found") end
off = string.format('%X',off)
local vmBufferBase = getAddressSafe('[[[baseCodes]+'..off..'*4]+8]+50')
[ENABLE]
origUseMana = readInteger(vmBufferBase)
writeInteger(vmBufferBase,0)
[DISABLE]
writeInteger(vmBufferBase,origUseMana)
Make sure you save it’s original value so you can restore it later as a disable. I have these examples and more in my Slormancer table here.


I hope you find this information helpful.

Happy hacking,
aSwedishMagyar
Last edited by aSwedishMagyar on Tue Apr 20, 2021 11:52 pm, edited 2 times in total.

User avatar
GreenHouse
GreenHouse!
GreenHouse!
Posts: 632
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 502

Re: GameMaker CScript Manipulation

Post by GreenHouse »

I used to use "Dissect Code" and then check the "Referenced Strings" to get the functions. I would say that probably your way is better, but it's another way of doing it too for those that don't understand all of your code, etc.
Regardless, this is a really good thing to have if you're dealing with GameMaker.

aSwedishMagyar
Table Makers
Table Makers
Posts: 436
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 532

Re: GameMaker CScript Manipulation

Post by aSwedishMagyar »

GreenHouse wrote:
Tue Apr 20, 2021 9:13 pm
I used to use "Dissect Code" and then check the "Referenced Strings" to get the functions. I would say that probably your way is better, but it's another way of doing it too for those that don't understand all of your code, etc.
Regardless, this is a really good thing to have if you're dealing with GameMaker.
That will work for games that have those functions separately registered but in the case of only finding gml_Script_ as a Referenced string this is your only option of modifying input parameters.

User avatar
GreenHouse
GreenHouse!
GreenHouse!
Posts: 632
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 502

Re: GameMaker CScript Manipulation

Post by GreenHouse »

aSwedishMagyar wrote:
Tue Apr 20, 2021 9:15 pm
That will work for games that have those functions separately registered but in the case of only finding gml_Script_ as a Referenced string this is your only option of modifying input parameters.
I see. Gotta save this one for the future then. Nice job :D

aSwedishMagyar
Table Makers
Table Makers
Posts: 436
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 532

Re: GameMaker CScript Manipulation

Post by aSwedishMagyar »

Added in a helper table that should get the base for all the CScripts and allow you to dump the names to the console.

aSwedishMagyar
Table Makers
Table Makers
Posts: 436
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 532

Re: GameMaker CScript Manipulation

Post by aSwedishMagyar »

An update to this info:

For games that these scripts are separately defined (as in using string references would find the correct function) you can actually quickly get the base addresses for each function:

Inside the instance of CScript:
Image


The referenced function:
Image


This table should find each function name and register it for use in scripts so you can use it in aobscanregion() and as the injection point if you just want to hook the start.
Attachments
Gamemaker_CScript_Finder.CT
06-05-2021
(6.92 KiB) Downloaded 8 times

Post Reply

Who is online

Users browsing this forum: No registered users