Jump to content
  • 0

Finding and changing the field offset using the game Guardian script


Question

Posted (edited)

Hello. I'm new to this and don't understand much. I have a class name (WeaponBalaceComponent) and a field offset (0x48), an anonymous region. With the help of field offset search scripts, everything is found and changed, but it takes a lot of time. How to write a Lua script using this data for Game Guardian. Can you show an example? This will help me a lot in the future. I am ready to provide any data. Игра Armor Attack. Dump.cs

Edited by Trews27

15 answers to this question

Recommended Posts

  • 0
Posted (edited)
On 12/28/2024 at 7:22 AM, Trews27 said:

Hello. I'm new to this and don't understand much. I have a class name (WeaponBalaceComponent) and a field offset (0x48), an anonymous region. With the help of field offset search scripts, everything is found and changed, but it takes a lot of time. How to write a Lua script using this data for Game Guardian. Can you show an example? This will help me a lot in the future. I am ready to provide any data. Игра Armor Attack. Dump.cs

Can only explain you how i do it personally, but perhaps someone can explain another way. I also don´t know how familiar you are with Lua. But you said you are new so i will assume that you don´t know Lua. Also since you been doing everything from scratch without code to help you, so i will provide you helper examples.

I will provide you more or less a building structure which is familiar to me and which you can modify how you want.

First you need to search the class name in the global-metadata.dat. For do that you need the start and end address of where your global-metadata.dat is located within the memory of your process.

You can make a function similar like the one below that first locates the global-metadata.dat in memory (assuming it is not hidden) and takes the start and end address of that lib (see more info about the gg.getRangesList: https://gameguardian.net/help/classgg.html#a8bb9745b0b7ae43f8a228a373031b1ed😞

function metaDataOffsets() -- Will give you start and end range of the global-metadata lib which resides in region Other.
  startAddressDat = 0
  endAddressDat = 0
  local rangesDat = gg.getRangesList("global-metadata.dat")
  for i, v in ipairs(rangesDat) do
    if v.state == "O" then
      startAddressDat = v.start -- start
      endAddressDat   = rangesDat[i]["end"] -- end
      break
    end
  end
end

Then you make a function that searches for your class name in which to which the field belongs, but as well the AssemblyCSharp string, you will need that one in order to filter out unwanted pointers.

You search those strings in the address range of the global-metadata.dat, you just fetched it's the start and end addresses from your previous function metaDataOffsets()

For readability purposes and clarity we make a function where you initialize your string names (class names) and give them meaningful variable names which we can then later search with GG.

Note that you must search the class name as a string of bytes and append 00 to the front and end of the bytes which indicate the start and end of the string of bytes. You can use this website to convert ascii characters to their representing byte and as that create your string of bytes.

We put the "h" at the beginning of the string so we can let GG know that we are searching in Hex. See a example function that initialize the strings to their variables, so that we then can use in a later stage as a GG search:

-- string names
function stringNames()
  Class_AssemblyCSharp      = "h00417373656d626c792d4353686172702e646c6c00"
  Class_Weapons             = "h00576561706f6e7300"
end
stringNames()

We must perform pointer search on the second character of the string.

We can make a function that receives the string as a argument and will saves the address of where the second character is stored in memory. This function is useful if you want to access many classes, less repetitive code:

function searchString(className) -- takes in name of desired class, searches in the range of the global-metadata.dat
  gg.clearResults()
  gg.searchNumber(className, gg.TYPE_BYTE, nil, nil, startAddressDat, endAddressDat) -- start and end range
  local t = gg.getResults(2)
  tableMetadataOffsets = t[2].address -- stores address at variable tableMetadataOffset
  gg.clearResults()
  return tableMetadataOffsets -- returns that address to the calling function which will be used to perform pointer search.
end

Since i don´t know if your running the game on 32 bit or 64 bit we should include a function that checks if your running game on 32 bit or 64 bit. This is also helpful in case someone sees this post and want to do same as your doing. So it's a general solution. See example function(s):

function isProcess64Bit() -- we only call this function once. It will check if the final address that gg.getRangesList is more then 32 bits. If so your game is running on 64 bit. Else it´s 32.
  local regions = gg.getRangesList()
  local lastAddress = regions[#regions]["end"]
  return (lastAddress >> 32) ~= 0
end

In my case i only want to call isProcess64Bit once. So i store the result in a separate function, but this is up to creator choice. This function is from my old script and should be updates as there is better ways to write this function, but for example purposes it will be ok:

function validISA() -- we store result in the variable "instructionSetArchitecture" (Maybe given it a shorter name does not hurt, lol)
  instructionSetArchitecture = 0
	if isProcess64Bit() == true then -- calling isProcess64Bit()
		instructionSetArchitecture = 64
	else
		instructionSetArchitecture = 32
	end
  return instructionSetArchitecture
end

Now we can make a function where we initialize or variables with the values which are only dependable on the instruction set of the game. Separate the offsets in separate functions to not get to confused. For example a function that has the offsets relevant to the class and it's fields. And the a function that has offsets that you have to use in almost every function. For example:

function instructionsOffset()
  if instructionSetArchitecture == 32 then -- if true then 32 bit else 64 bit
    hexConvert = 0xFFFFFFFF -- You need this for safely perform pointer searches on 32 bit.
    dataType = 4 -- on 32 bit when performing pointer search you do it using gg.TYPE_DWORD (4).
    classOffset = 0x8 -- when performing pointer search on second character of string you must do a -0x8 to reach class pointer.
  else
    dataType = 32 -- on 62 bit when performing pointer search you do it using gg.TYPE_QWORD (32).
    classOffset = 0x10 -- when performing pointer search on second character of string you must do a -0x10 to reach class pointer.
  end
end
instructionsOffset()

Then a function where we put offsets only relevant to the class that we try to change it's fields of:

function weaponSettingsOffsets()
  if instructionSetArchitecture == 32 then
    weaponPointerToIdOffset = 0x8
    weaponPointerToAmmoOffset = 0x48
    weaponPointerToRecoilOffset = 0x78
  else
    weaponPointerToIdOffset = 0x10
    weaponPointerToAmmoOffset = 0x60
    weaponPointerToRecoilOffset = 0x90
  end
end
weaponSettingsOffsets()

Now we must make function that takes the second character of the AssemblyCSharp string and then use the address of the pointer pointing to that chracter it's address. We use it in future use cases for checking purposes of other class pointers. As i mentioned before:

function assemblyAddressCheck()
  gg.setRanges(gg.REGION_ANONYMOUS | gg.REGION_C_ALLOC | gg.REGION_OTHER) -- we enable the regions in which we allow GG to search
  searchString(Class_AssemblyCSharp) -- we call the function searchString and parse the string that we made at function stringNames(), it will return the second character of the string under the name tableMetadataOffsets.
  gg.searchNumber(tableMetadataOffsets, dataType) -- We perfoming pointer search on the second character.  dataType: 32bit = 4(dword) | 64bit = 32(qword)
  assembly = gg.getResults(1) -- we now have the pointer which we use to check if the class pointer you have is valid once or not.
  gg.clearResults()
  return assembly -- we can now use this variable every time for checking.
end
assemblyAddressCheck()

Now we can work on finding your field value as we setted up the main structure.

Make a function with the name of your class which will first call searchString in order to get the second character of the string, then perform pointer search on that second character. Then do offset - 0x8 or-0x10 depending on your instruction set architecture. Then check if the value at those addresses is equal to the value of assembly which you received from the function assemblyAddressCheck()

Then once that check is complete you can start working with the pointer that is equal to assembly. Perform a pointer search on that pointer. That will give you all the instances that point to your class pointer.

see an example:

function weaponsSettings()
  gg.clearResults()
  gg.setRanges(gg.REGION_ANONYMOUS | gg.REGION_C_ALLOC | gg.REGION_OTHER)
  searchString(Class_Weapons)
  gg.searchNumber(tableMetadataOffsets, dataType)
  a = gg.getResults(5)
  for i, v in ipairs(a) do
    v.address = v.address - classOffset -- classOffset: 32bit = 0x8 | 64bit = 0x10
  end
  a = gg.getValues(a)
  gg.clearResults()
  compareWeaponsToAssembly = {}
  for i,v in ipairs(a) do
    if instructionSetArchitecture == 32 then
      v.value = v.value & hexConvert -- hexConvert: 32bit = 0xFFFFFFFF
    end
    if v.value == assembly[1].address then -- if matches with assembly then store class pointer in compareWeaponsToAssembly (should ony be 1)
      compareWeaponsToAssembly[#compareWeaponsToAssembly + 1] = v
    end
  end
  gg.searchNumber(compareWeaponsToAssembly[1].address, dataType) -- perform pointer search on class pointer to get it's instances.
  weaponPointers = gg.getResults(1141) -- maybe you get to many instances and will need to do extra checks to get the correct instances.
  gg.clearResults()
  weaponId = {}
  for i, v in ipairs(weaponPointers) do --find your field by adding your field offset. It will do instance base address + field. Also add the desired data type of that field using the flags.
    weaponId[i] = {address = v.address - weaponPointerToIdOffset, flags = dataType}
    recoilTableOn[i] = {address = v.address + weaponPointerToRecoilOffset, flags = gg.TYPE_DOUBLE}
    maxAmmoCapacityTableOn[i] = {address = v.address + weaponPointerToAmmoOffset, flags = gg.TYPE_DWORD}
  end
  gg.toast('recoil, reload, max ammo, accumulation ready')
end
weaponsSettings()

I hope this more or less explains it. sadly i don't have a simple script but i can provide you some, you take your time to understand they might make sense. All my scripts are written in similar way.

 

games.burny.playdoku.block.puzzle.lua com.gameinsight.gobandroid (1).lua

Also have a look at this video:

 

Edited by nok1a
typos
  • 0
Posted (edited)

Thanks for your answer. Any manipulation ends with this error

 luaj.o: /storage/emulated/0/jjjjjjjjjjjjj.lua:101

` gg.searchNumber(compareWeaponsToAssembly[1].address, dataType) -- perform pointer search on class pointer to get it's inst...`

attempt to index ? (a nil value) with key 'address' (field '1')

level = 1, const = 38, proto = 0, upval = 1, vars = 9, code = 128

GETTABLE v1 v1 "address"

 ; PC 75 CODE 00C3C047 OP 7 A 1 B 1 C 271 Bx 783 sBx -130288

stack traceback:

 /storage/emulated/0/jjjjjjjjjjjjj.lua:101 in function 'weaponsSettings'

 /storage/emulated/0/jjjjjjjjjjjjj.lua:112 in main chunk

 [Java]: in ?

 at luaj.LuaValue.f(src:989)

 at luaj.LuaValue.c(src:2864)

 at luaj.LuaValue.i(src:2767)

 at luaj.LuaValue.w(src:1094)

 at luaj.LuaClosure.a(src:363)

 at luaj.LuaClosure.l(src:160)

 at luaj.LuaClosure.a(src:533)

 at luaj.LuaClosure.l(src:160)

 at android.ext.Script.d(src:6056)

 at android.ext.Script$ScriptThread.run(src:5785)

Edited by Trews27
  • 0
Posted (edited)

WeaponBalanceComponent

reloadIterationTime; // 0x48

Game Armor Attack

Screenshot_20241230-151127.png

Edited by Trews27
  • 0
Posted
2 hours ago, Trews27 said:

WeaponBalanceComponent

reloadIterationTime; // 0x48

Game Armor Attack

Screenshot_20241230-151127.png

Yeah oke i also forgot that this script was made before i knew about the searchPointer() function.

I removed the assembly check thing as we don't need it.

 

function metaDataOffsets() -- Will give you start and end range of the global-metadata lib which resides in region Other.
  startAddressDat = 0
  endAddressDat = 0
  local rangesDat = gg.getRangesList("global-metadata.dat")
  for i, v in ipairs(rangesDat) do
    if v.state == "O" then
      startAddressDat = v.start -- start
      endAddressDat   = rangesDat[i]["end"] -- end
      break
    end
  end
end
metaDataOffsets()

-- string names
function stringNames()
  Class_WeaponBalanceComponent  = "h00576561706f6e42616c616e6365436f6d706f6e656e7400"
end
stringNames()

function searchString(className) -- takes class name, searches in the range of the global-metadata.dat, and returns class pointer
  gg.clearResults()
  gg.searchNumber(className, gg.TYPE_BYTE, nil, nil, startAddressDat, endAddressDat) -- start and end range
  gg.searchPointer(0)
  local a = gg.getResults(5)
  for i, v in ipairs(a) do
    v.address = v.address - classOffset -- classOffset: 32bit = 0x8 | 64bit = 0x10
  end
  gg.loadResults(a)
end

function isProcess64Bit() -- we only call this function once. It will check if the final address that gg.getRangesList is more then 32 bits. If so your game is running on 64 bit. Else it´s 32.
  local regions = gg.getRangesList()
  local lastAddress = regions[#regions]["end"]
  return (lastAddress >> 32) ~= 0
end

function validISA() -- we store result in the variable "instructionSetArchitecture" (Maybe given it a shorter name does not hurt, lol)
  instructionSetArchitecture = 0
	if isProcess64Bit() == true then -- calling isProcess64Bit()
		instructionSetArchitecture = 64
	else
		instructionSetArchitecture = 32
	end
  return instructionSetArchitecture
end
validISA()

function instructionsOffset()
  if instructionSetArchitecture == 32 then -- if true then 32 bit else 64 bit
    hexConvert = 0xFFFFFFFF -- You need this for safely perform pointer searches on 32 bit.
    dataType = 4 -- on 32 bit when performing pointer search you do it using gg.TYPE_DWORD (4).
    classOffset = 0x8 -- when performing pointer search on second character of string you must do a -0x8 to reach class pointer.
  else
    dataType = 32 -- on 62 bit when performing pointer search you do it using gg.TYPE_QWORD (32).
    classOffset = 0x10 -- when performing pointer search on second character of string you must do a -0x10 to reach class pointer.
  end
end
instructionsOffset()

-- put here function for field offsets of your specific class
function offset_weaponBalanceComponent()
  if instructionSetArchitecture == 32 then
    -- offset_reloadIterationTime = 0x48
  else
    offset_reloadIterationTime = 0x48
  end
end
offset_weaponBalanceComponent()
-- the function for your class which will load all instances and it's field. You might need to add some filtering option
function WeaponBalanceComponent()
  gg.clearResults()
  searchString(Class_WeaponBalanceComponent)
  gg.searchPointer(0)
  local instances_WeaponBalanceComponent = gg.getResults(gg.getResultsCount())
  gg.clearResults()
  local WeaponBalanceComponent_reloadIterationTime = {}
  for i, v in ipairs(instances_WeaponBalanceComponent) do
    WeaponBalanceComponent_reloadIterationTime[i] = {address = v.address + offset_reloadIterationTime, flags = gg.TYPE_FLOAT}
  end
  gg.loadResults(WeaponBalanceComponent_reloadIterationTime)
end
WeaponBalanceComponent()

 

  • 0
Posted (edited)

Works. Super. You are the best. This is what I wanted. As far as I understand, this method can be applied to other games? If it's not difficult. Can you show an example of this script, only with subsequent filtering of values and changing them ?

Edited by Trews27
  • 0
Posted
2 hours ago, Trews27 said:

Works. Super. You are the best. This is what I wanted. As far as I understand, this method can be applied to other games? If it's not difficult. Can you show an example of this script, only with subsequent filtering of values and changing them ?

Yes, i mean that you only keep the values that are most likely not gone make the game crash when editing. For example these values here you most likely don't want to edit because they are pointers, and editing them wrong might cause a crash:
image.thumb.png.f2ed25da52623e75bafe398e35c0efd5.png

On the other hand you probably want to edit these values that have a reasonable range to represent your gun values:
image.thumb.png.00b89d41defb5e70c8c0c83ba51eb0b6.png

So you need to write code that like filter out the values which are not higher then 500 but more then 0.0001 for example:

function WeaponBalanceComponent()
  gg.clearResults()
  searchString(Class_WeaponBalanceComponent)
  gg.searchPointer(0)
  local instances_WeaponBalanceComponent = gg.getResults(gg.getResultsCount())
  gg.clearResults()
  local WeaponBalanceComponent_reloadIterationTime = {}
  for i, v in ipairs(instances_WeaponBalanceComponent) do
    WeaponBalanceComponent_reloadIterationTime[i] = {address = v.address + offset_reloadIterationTime, flags = gg.TYPE_FLOAT}
  end
  
  local sortedFields = {}
  local filter = gg.getValues(WeaponBalanceComponent_reloadIterationTime)
  for i, v in ipairs(filter) do
    if (v.value <= 500) and (v.value >= 0.0001) then
      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}
    end
  end
  gg.loadResults(sortedFields)
end
WeaponBalanceComponent()

 

3 hours ago, Trews27 said:

As far as I understand, this method can be applied to other games?

Yes, the solution i have you can be applied to any game i guess that doesn't has weird protection, like hidding global-metadata.dat or scrambling the symbols all over the memory.

  • 0
Posted

Reloading the weapon I need takes 11 seconds. 

 

  for i, v in ipairs(filter) do

 

    if (v.value <= 12) and (v.value >= 11) then

 

      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}

 

Finds 2 values that are needed. Works as it should. Is it possible to add value changes to the script after filtering ?

 

  • 0
Posted
2 hours ago, Trews27 said:

Reloading the weapon I need takes 11 seconds. 

 

  for i, v in ipairs(filter) do

 

    if (v.value <= 12) and (v.value >= 11) then

 

      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}

 

Finds 2 values that are needed. Works as it should. Is it possible to add value changes to the script after filtering ?

 

https://gameguardian.net/help/classgg.html#a5f859e6f707b2336152411b19fea7603

  • 0
Posted

If I understand correctly, there are several ways to change this. I tried to do it myself, but it didn't work out well. Help please.

  • 0
Posted
5 hours ago, Trews27 said:

If I understand correctly, there are several ways to change this. I tried to do it myself, but it didn't work out well. Help please.

  local sortedFields = {}
  local filter = gg.getValues(WeaponBalanceComponent_reloadIterationTime)
  for i, v in ipairs(filter) do
    if (v.value <= 500) and (v.value >= 0.0001) then
      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}
    end
  end
  gg.loadResults(sortedFields)
  gg.editAll('15', gg.TYPE_FLOAT)

 

  • 0
Posted
8 hours ago, nok1a said:
  local sortedFields = {}
  local filter = gg.getValues(WeaponBalanceComponent_reloadIterationTime)
  for i, v in ipairs(filter) do
    if (v.value <= 500) and (v.value >= 0.0001) then
      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}
    end
  end
  gg.loadResults(sortedFields)
  gg.editAll('15', gg.TYPE_FLOAT)

 

On 12/31/2024 at 12:42 AM, nok1a said:

 

Скрипт завершен:

Ошибка скрипта: luaj.o: /storage/emulated/0/gg.lua/yfgdfhju.lua:104

` gg.editAll('0', gg.TYPE_FLOAT)`

You must call gg.getResults before calling gg.editAll. (field 'editAll')

level = 1, const = 21, proto = 0, upval = 1, vars = 12, code = 72

CALL v4..v6

 ; PC 70 CODE 0180411D OP 29 A 4 B 3 C 1 Bx 1537 sBx -129534

stack traceback:

 /storage/emulated/0/gg.lua/yfgdfhju.lua:104 in function 'WeaponBalanceComponent'

 /storage/emulated/0/gg.lua/yfgdfhju.lua:106 in main chunk

 [Java]: in ?

 at android.ext.Script$editAll.b(src:3704)

 at android.ext.Script$ApiFunction.a_(src:1393)

 at luaj.lib.VarArgFunction.a(src:62)

 at luaj.LuaClosure.a(src:535)

 at luaj.LuaClosure.l(src:160)

 at luaj.LuaClosure.a(src:533)

 at luaj.LuaClosure.l(src:160)

 at android.ext.Script.d(src:6056)

 at android.ext.Script$ScriptThread.run(src:5785)

  • 0
Posted
8 hours ago, Trews27 said:

Скрипт завершен:

Ошибка скрипта: luaj.o: /storage/emulated/0/gg.lua/yfgdfhju.lua:104

` gg.editAll('0', gg.TYPE_FLOAT)`

You must call gg.getResults before calling gg.editAll. (field 'editAll')

level = 1, const = 21, proto = 0, upval = 1, vars = 12, code = 72

CALL v4..v6

 ; PC 70 CODE 0180411D OP 29 A 4 B 3 C 1 Bx 1537 sBx -129534

stack traceback:

 /storage/emulated/0/gg.lua/yfgdfhju.lua:104 in function 'WeaponBalanceComponent'

 /storage/emulated/0/gg.lua/yfgdfhju.lua:106 in main chunk

 [Java]: in ?

 at android.ext.Script$editAll.b(src:3704)

 at android.ext.Script$ApiFunction.a_(src:1393)

 at luaj.lib.VarArgFunction.a(src:62)

 at luaj.LuaClosure.a(src:535)

 at luaj.LuaClosure.l(src:160)

 at luaj.LuaClosure.a(src:533)

 at luaj.LuaClosure.l(src:160)

 at android.ext.Script.d(src:6056)

 at android.ext.Script$ScriptThread.run(src:5785)

ah, it only works with gg.getResults().

  gg.loadResults(sortedFields)
  gg.getResults(gg.getResultsCount())
  gg.editAll('15', gg.TYPE_FLOAT)

 

 

  • 0
Posted (edited)
46 minutes ago, nok1a said:

ah, it only works with gg.getResults().

  gg.loadResults(sortedFields)
  gg.getResults(gg.getResultsCount())
  gg.editAll('15', gg.TYPE_FLOAT)

I wrote it like this

local sortedFields = {}

  local filter = gg.getValues(WeaponBalanceComponent_reloadIterationTime)

  for i, v in ipairs(filter) do

    if (v.value <= 12) and (v.value >= 11) then

      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}

    end

  end

 end

revert = gg.getResults(100000, nil, nil, nil, nil, nil, nil, nil, nil)

gg.editAll("0", gg.TYPE_FLOAT)

Thank you very much. Everything works

 

18 hours ago, nok1a said:
  local sortedFields = {}
  local filter = gg.getValues(WeaponBalanceComponent_reloadIterationTime)
  for i, v in ipairs(filter) do
    if (v.value <= 500) and (v.value >= 0.0001) then
      sortedFields[#sortedFields + 1] = {address = v.address, flags = gg.TYPE_FLOAT}
    end
  end
  gg.loadResults(sortedFields)
  gg.editAll('15', gg.TYPE_FLOAT)

 

On 12/31/2024 at 12:42 AM, nok1a said:

 

 

Edited by Trews27
  • 0
Posted
36 minutes ago, nok1a said:

ah, it only works with gg.getResults().

  gg.loadResults(sortedFields)
  gg.getResults(gg.getResultsCount())
  gg.editAll('15', gg.TYPE_FLOAT)

I wrote it like this

revert = gg.getResults(100000, nil, nil, nil, nil, nil, nil, nil, nil)

gg.editAll("0", gg.TYPE_FLOAT

Thank you very much. Everything works)

 

 

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.