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: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

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 248 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: 662
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 524

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: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

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: 662
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 524

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: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

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: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

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 177 times

roosen5
What is cheating?
What is cheating?
Posts: 2
Joined: Tue May 25, 2021 9:40 pm
Reputation: 0

Re: GameMaker CScript Manipulation

Post by roosen5 »

Super cool stuff! I was able to find all the scripts. However, maybe I'm too noob to understand you but I couldn't follow the step
"Do a short break and trace from here to get back to the main CScript Calling function:", I just don't know how I am supposed to initiate that from here, what start and end condition and I have to initiate it from the memory viewer instead of this dissect window correct?

I would really like to somehow get to the calling code of these functions and set a breakpoint somehow on calling that function but I cannot get to the calling code stage :)

aSwedishMagyar
Table Makers
Table Makers
Posts: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

Re: GameMaker CScript Manipulation

Post by aSwedishMagyar »

roosen5 wrote:
Tue May 25, 2021 9:50 pm
Super cool stuff! I was able to find all the scripts. However, maybe I'm too noob to understand you but I couldn't follow the step
"Do a short break and trace from here to get back to the main CScript Calling function:", I just don't know how I am supposed to initiate that from here, what start and end condition and I have to initiate it from the memory viewer instead of this dissect window correct?

I would really like to somehow get to the calling code of these functions and set a breakpoint somehow on calling that function but I cannot get to the calling code stage :)
There will be an address printed when you enable the table if it is compatible with that Gamemaker game. Set a break-and-trace at that address and give it a length of 10 instructions to get to the calling function. From there you can setup your hook to check which script has been called and modify the input parameters accordingly.

roosen5
What is cheating?
What is cheating?
Posts: 2
Joined: Tue May 25, 2021 9:40 pm
Reputation: 0

Re: GameMaker CScript Manipulation

Post by roosen5 »

But that's the thing, the break-and-trace says nothing and even when setting a breakpoint there it is not being hit.

Could you confirm that it should be hit in this case?

Image

aSwedishMagyar
Table Makers
Table Makers
Posts: 537
Joined: Mon Jul 06, 2020 3:19 am
Reputation: 715

Re: GameMaker CScript Manipulation

Post by aSwedishMagyar »

roosen5 wrote:
Fri May 28, 2021 10:13 pm
But that's the thing, the break-and-trace says nothing and even when setting a breakpoint there it is not being hit.

Could you confirm that it should be hit in this case?

...
It might not be used, try using dump cscripts and see if there is an output. That will tell you if the game is compatible. If the one in the main post doesn't work, try using this one and dumping the script names.

Also what game are you trying to use this on?

Post Reply

Who is online

Users browsing this forum: No registered users