Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Mono implentation questions (Structs)

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
ShaRose
Cheater
Reputation: 0

Joined: 12 Jan 2010
Posts: 26

PostPosted: Fri Oct 23, 2020 1:24 am    Post subject: Mono implentation questions (Structs) Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 458

Joined: 09 May 2003
Posts: 25287
Location: The netherlands

PostPosted: Fri Oct 23, 2020 3:29 am    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
ShaRose
Cheater
Reputation: 0

Joined: 12 Jan 2010
Posts: 26

PostPosted: Fri Oct 23, 2020 10:33 am    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites