|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
ShaRose Cheater Reputation: 0
Joined: 12 Jan 2010 Posts: 26
|
Posted: Fri Oct 23, 2020 1:24 am Post subject: Mono implentation questions (Structs) |
|
|
So, I've been messing around with almost exclusively using the mono lua helpers for messing with Phasmophobia (Offline solo only, thank you very much!), but I've run into issues that I can't seem to work around.
I can't see to figure out how to read structs. The existing functions treat them like classes, but the memory offsets don't seem to line up: Even if I read them as values and not pointers to classes, if I attempt to read, say, an int off of a struct, it returns invalid information. Trying to do anything on a more complex thing, like an enum, also works poorly.
I was going to leave this alone until I tried my code out on the beta: It's IL2CPP, but that shouldn't really bother my stuff. Turns out IL2CPP doesn't let you use mono_class_getStaticFieldAddress because it doesn't expose statics like that. Looked at the github, and saw there are some new functions that will help me out: And also include a helper function for reading strings, which is awesome.
Now, with the knowledge of "how to read strings without having to call string.ToString() like a pleb", I tried to poke at structs again, because now I have a way to test if it works without instantly crashing the game (Turns out, trying to call ToString on a memory value that isn't actually a string is a bad time), and found... It didn't work.
I'm able to get the offsets for the struct, (The ghost name is 32), I'm able to get an instance of the struct (at least, the offsets for everything else work), but the struct just seems to be stored in memory differently somehow.
Now, for code! I've got a lot of the extra fluff removed for brevity, but here's my enable AA script. I left in the read functions so you know what I'm actually calling.
Code: | [ENABLE]
{$lua}
if syntaxcheck then return end
if LaunchMonoDataCollector() == 0 then return end
------------------------------------------------
--------------- Helper Functions ---------------
------------------------------------------------
-- I'm not sure why I've seen guides unregister before registering symbols, but ok
function addSymbol(desc, value)
unregisterSymbol(desc)
registerSymbol(desc,value)
print("Set symbol: ",desc," Value: ",value)
end
-- This parses a class and saves symbols for the provided field names
function parseClass(classID, names)
local fields = mono_class_enumFields(classID)
local className = mono_class_getName(classID)
for i=1,#fields do
for j=1,#names do
if fields[i].name == names[j] then
addSymbol(className.."."..names[j],fields[i].offset)
end
end
end
end
-- This loops down through symbols to get a base pointer.
function getPointerAddress(basepointer,path)
local currentAddress = readPointer(getAddressSafe(basepointer))
if currentAddress == nil or currentAddress == 0 then return nil end
for i=1,#path do
local offset = getAddressSafe(path[i])
if offset == nil then return nil end
currentAddress = readPointer(currentAddress + offset)
if currentAddress == nil or currentAddress == 0 then return nil end
end
return currentAddress
end
-- Same as above, but assumes the end will be a value, not a pointer.
function getPointerAddressValue(basepointer,path)
local currentAddress = readPointer(getAddressSafe(basepointer))
if currentAddress == nil or currentAddress == 0 then return nil end
for i=1,#path do
local offset = getAddressSafe(path[i])
if offset == nil then return nil end
currentAddress = currentAddress + offset
if i ~= #path then
currentAddress = readPointer(currentAddress)
end
if currentAddress == nil or currentAddress == 0 then return nil end
end
return currentAddress
end
-- This just abstracts parseClass more
function getClassInfo(namespace, classname, arguments, static)
local classid = mono_findClass(namespace, classname)
parseClass(classid,arguments)
if static == true then
addSymbol(classname,mono_class_getStaticFieldAddress(mono_enumDomains()[1], classid))
end
end
------------------------------------------------
-------- LevelController Based Pointers --------
------------------------------------------------
--getPointerAddress("LevelController",{})
getClassInfo("", "LevelController", {"currentGhost","fuseBox","currentGhostRoom"}, true)
--getPointerAddress("LevelController",{"LevelController.currentGhost"})
getClassInfo("", "GhostAI", {"activityMultiplier","ghostInfo","ghostActivity"}, false)
--getPointerAddress("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo"})
getClassInfo("", "GhostInfo", {"favoriteRoom","ghostTraits"}, false)
--getPointerAddress("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo","GhostInfo.ghostTraits"})
getClassInfo("", "GhostTraits", {"ghostType","ghostName"}, false)
[DISABLE]
|
Now, after running that, I should be able to use the below code (It's a 32 bit binary) in the lua window to see the address and length of the string (It should be 20 in this case).
Code: | if syntaxcheck then return end
if LaunchMonoDataCollector() == 0 then return end
local stringobject = getPointerAddressValue("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo","GhostInfo.ghostTraits","GhostTraits.ghostName"})
print("object",stringobject)
length=readInteger(stringobject+0x8)
print("len",length) |
It proceeds to output the following:
object 184683593764
len
Well, the object address looks sane, until you convert to hex: 2B00000024. So it's not valid.
I've tried a few other things on this as well (getting the value of GhostTraits, and then manually shifting to where ghostName should be, for example) but it doesn't seem like anything works.
So, am I missing something obvious here?
|
|
Back to top |
|
|
Dark Byte Site Admin Reputation: 458
Joined: 09 May 2003 Posts: 25287 Location: The netherlands
|
Posted: Fri Oct 23, 2020 3:29 am Post subject: |
|
|
check if the offset is 0 as well.
besides that not sure. try following the path manually in memoryview's hexview and check where it goes wrong
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
|
ShaRose Cheater Reputation: 0
Joined: 12 Jan 2010 Posts: 26
|
Posted: Fri Oct 23, 2020 10:33 am Post subject: |
|
|
After adding a lot of guard code to mono_string_readString, I tried to brute force it with the following code: All invalid results, though.
Code: | if syntaxcheck then return end
if LaunchMonoDataCollector() == 0 then return end
local stringOffset = getAddressSafe("GhostTraits.ghostName")
local ghostTraits = getPointerAddressValue("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo"})
print("GhostTraits base:",ghostTraits," string Offset: ",stringOffset)
for i=0,100 do
local trait = ghostTraits + (i * 4)
print("Value, Ts Ss, offset ",i * 4, " result ", mono_string_readString(trait + stringOffset))
print("Value, Ts Sp, offset ",i * 4, " result ", mono_string_readString(readPointer(trait + stringOffset)))
trait = readPointer(trait)
print("Value, Tp Ss, offset ",i * 4, " result ", mono_string_readString(trait + stringOffset))
print("Value, Tp Sp, offset ",i * 4, " result ", mono_string_readString(readPointer(trait + stringOffset)))
end |
Here's the guard-code addled readString too.
Code: | function mono_string_readString(stringobject)
if stringobject == nil or stringobject <= 100000 then
return "INVALID NILSTART"
end
local length,stringstart
if targetIs64Bit() then
length=readInteger(stringobject+0x10)
stringstart=stringobject+0x14
else
length=readInteger(stringobject+0x8)
stringstart=stringobject+0x10
end
if length == nil or stringstart == nil then
return "INVALID NIL LEN OR STRINGSTART"
end
--print("String Debug: Length is ",length," start is ",stringstart)
-- Sanity Checks, since I'm testing
if length <= 4 or length > 60 then
return "INVALID LENGTH - "..length
end
local returnString = readString(stringstart,length*2,true)
if returnString == nil then
return "INVALID NIL RETURN"
end
local retLen = string.len(returnString)
if retLen > 60 or retLen < 4 then
return "INVALID RETLEN "..retLen
else
return returnString
end
end |
It only hit invalid retlen twice: both 0.
Edit: I thought to try a double loop in case it was reading the string offset wrong. Code for that brute force:
Code: | if syntaxcheck then return end
local ghostTraits = getPointerAddressValue("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo"})
print("GhostTraits base:",ghostTraits)
for i=0,100 do
for j=0,25 do
local trait = ghostTraits + (i * 4)
local soff = j * 4
print("Value, Ts Ss, offset",i * 4, "soff", soff, "result", mono_string_readString(trait + soff))
print("Value, Ts Sp, offset",i * 4, "soff", soff, "result", mono_string_readString(readPointer(trait + soff)))
trait = readPointer(trait)
print("Value, Tp Ss, toff",i * 4, "soff", soff, "result", mono_string_readString(trait + soff))
print("Value, Tp Sp, offset",i * 4, "soff", soff, "result", mono_string_readString(readPointer(trait + soff)))
end
end |
And after it finished, I found a hit:
Value, Tp Sp, offset 0 soff 56 result John Anderson
Thing is, here's the offsets according to mono:
Set symbol: GhostInfo.ghostTraits Value: 40
Set symbol: GhostTraits.ghostName Value: 32
So, I'm not sure where the 40 is supposed to come from: And I'm even less sure why there's a random 24 byte offset for struct fields. I tried out the following code:
Code: | if syntaxcheck then return end
local ghostTraits = getPointerAddress("LevelController",{"LevelController.currentGhost","GhostAI.ghostInfo"})
print("GhostTraits base:",ghostTraits)
structoffset = 24
print("GhostTraits.ghostType:",readInteger(ghostTraits + (getAddress("GhostTraits.ghostType") + structoffset)))
print("GhostTraits.ghostAge:",readInteger(ghostTraits + (getAddress("GhostTraits.ghostAge") + structoffset)))
print("GhostTraits.isMale:",readInteger(ghostTraits + (getAddress("GhostTraits.isMale") + structoffset)))
print("GhostTraits.isShy:",readInteger(ghostTraits + (getAddress("GhostTraits.isShy") + structoffset)))
print("GhostTraits.deathLength:",readInteger(ghostTraits + (getAddress("GhostTraits.deathLength") + structoffset)))
print("GhostTraits.ghostName:",mono_string_readString(readPointer(ghostTraits + (getAddress("GhostTraits.ghostName") + structoffset)))) |
and indeed it was able to print out the ghost's name, age, death length, and type (it's just stored as an int) correctly.
So that's usable, but it still begs the question why the offset is needed: It should be included in the mono field addresses and the like.
|
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|