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 Method Region Scanner AutoAssembler Function

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions
View previous topic :: View next topic  
Author Message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Thu Dec 23, 2021 6:04 pm    Post subject: Mono Method Region Scanner AutoAssembler Function This post has 1 review(s) Reply with quote

I recently ran into an issue/limitation of Cheat Engine where you are not able to return a define from a custom registered auto-assembler function and then pass that defines symbol into specific premade CE AA functions such as 'aobscanregion'. The AoB scan calls are performed before your custom Lua defines are created/registered.

So to deal with this, I made a function that will allow you to find a specific method held within a specific assembly and class. It allows for signature checking in case methods are overloaded as well.

Once the method is found, it will be jitted then the jitted code address and size are used to scan specifically within its own memory region making for an extremely fast setup to find and hook/patch the desired methods.

This helper contains localized functions to help with its internal requirements, so no additional external methods are needed to keep the code footprint small. The only thing required is a newer version of CE (7.0+ is recommended.) and having the Mono tools enabled. (Mono > Activate Mono Features)

Code:

--[[
* Returns the address of the found pattern within the desired Mono method.
*
* @param {string} params - String containing the arguments of the AA function call.
* @param {boolean} isSyntaxCheck - Flag that states if the call is used for syntax checking or actual usage.
* @return {string|nil,string} The replacement define if found, nil+string on error.
*
* (c) 2021 atom0s
*
* Contact: https://atom0s.com/
* Contact: https://discord.gg/UmXNvjq - atom0s#0001
* Support: https://paypal.me/atom0s
* Support: https://github.com/sponsors/atom0s
* Support: https://patreon.com/atom0s
--]]
local function aa_aobscanmono(params, isSyntaxCheck)
    if (params == nil or #params == 0) then
        return nil, 'Invalid parameters for function: aobscanmono';
    end

    -- Parses the incoming parameters into a table.
    local function paramsplit(args)
        local ret = {};
        for arg in args:gmatch('[^,]+') do
            ret[#ret + 1] = arg:gsub('\'', ''):gsub('^%s*(.-)%s*$', '%1');
        end
        return ret;
    end

    -- Finds a Mono assembly by its name.
    local function find_assembly(name)
        if (name == nil or string.len(name) == 0) then
            return nil, 'Invalid name given; cannot locate Mono assembly.';
        end
        for k, v in pairs(mono_enumAssemblies()) do
            local i = mono_getImageFromAssembly(v);
            local n = mono_image_get_name(i);
            if (n:lower() == name:lower()) then
                return { image = i, name = n, };
            end
        end
        return nil, 'Failed to locate desired Mono assembly: ' .. tostring(name);
    end

    -- Finds a Mono class inside the given assembly.
    local function find_class(asm, className)
        for k, v in pairs(mono_image_enumClasses(asm.image)) do
            local n = mono_class_getFullName(v.class);
            if (n:lower() == className:lower()) then
                return { class = v, name = n, };
            end
        end
        return nil, 'Failed to locate desired Mono class: ' .. tostring(className);
    end

    -- Finds a Mono method inside the given class.
    local function find_method(cls, methodName, signature)
        for k, v in pairs(mono_class_enumMethods(cls.class.class)) do
            local n = mono_method_getName(v.method);
            local s = mono_method_getSignature(v.method);
            if (n:lower() == methodName:lower()) then
                if ((signature == nil or signature == 'nil') or (#signature == 0 and #s == 0) or s:lower():match(signature)) then
                    return { name = n, signature = s, method = v, };
                end
            end
        end
        return nil, 'Failed to locate desired Mono method: ' .. tostring(methodName);
    end

    -- Parse the incoming arguments..
    local p = paramsplit(params);
    if (#p ~= 6) then
        return nil, 'Invalid argument count for function: aobscanmono ' .. tostring(#p);
    end

    -- Check if the call was for syntax checking..
    if (isSyntaxCheck) then
        return string.format('define(%s, 0)\n', p[1]);
    end

    -- Find the desired Mono assembly and class..
    local asm, err = find_assembly(p[2]);
    if (asm == nil) then
        return nil, err;
    end
    local cls, err = find_class(asm, p[3]);
    if (cls == nil) then
        return nil, err;
    end

    -- Find the desired Mono method..
    local m, err = find_method(cls, p[4], p[5]);
    if (m == nil) then
        return nil, err;
    end

    -- Compile the method..
    local addr = mono_compile_method(m.method.method);
    if (addr == nil) then
        return nil, 'Failed to compile the desired method: ' .. tostring(m.name);
    end

    -- Obtain the methods JIT information..
    local jit = mono_getJitInfo(addr);
    if (jit == nil) then
        return nil, 'Failed to obtain the method JIT information: ' .. tostring(m.name);
    end

    -- Scan for the pattern within the given JIT code range..
    local ms = createMemScan(false);
    ms.setOnlyOneResult(true);
    ms.firstScan(soExactValue, vtByteArray, rtRounded, p[6], nil, jit.code_start, jit.code_start + jit.code_size, '*X*C*W', fsmNotAligned, '', true, true, false, false);
    ms.waitTillDone();

    -- Obtain the result and cleanup..
    local res = ms.Result;
    ms.destroy();

    if (res == nil or res == 0) then
        return nil, 'Failed to locate the given pattern within the desired method: ' .. tostring(m.name);
    end

    return string.format('define(%s, 0x%016X)\n', p[1], res);
end

-- Register the Mono AoB scanner function to the AutoAssembler..
registerAutoAssemblerCommand('aobscanmono', aa_aobscanmono);


aobscanmono - Scans for an array of bytes pattern within a specific methods memory space. Uses the given assembly and class name to locate the desired method.

Parameters:
  • defineName - string - The name of the define to create which will be used as the local symbol name.
  • assemblyName - string - The name of the Mono assembly that has the class and method within it.
  • className - string - The name of the class that holds the method.
  • methodName - string - The name of the method.
  • methodSignature - string - The parameter signature of the method. (If nil, then the first method with the matching name is returned and used.)
  • pattern - string - The array of bytes pattern to scan for within the methods memory space.


And as an example usage:

Code:

[ENABLE]
aobscanmono(pDecreaseDurabilityPtr, 'Assembly-CSharp', 'ItemInstance', 'DecreaseDurability', 'int', '83 F8 FF 0F 84 ?? ?? ?? ?? 48 8D 47 68 48 63 08')

pDecreaseDurabilityPtr:
  db 90 90 90

[DISABLE]
pDecreaseDurabilityPtr:
  db 83 F8 FF

_________________
- Retired.


Last edited by atom0s on Fri Dec 24, 2021 1:22 am; edited 1 time in total
Back to top
View user's profile Send private message Visit poster's website
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Fri Dec 24, 2021 1:20 am    Post subject: Reply with quote

Small update to this, I adjusted the signature check to do a bit more specific checking to ensure the desired method is selected under certain conditions. At times, the no-param method may not be first, and thus, passing an empty string would originally fail to find that proper method if needed.

To give some more examples in how this works, take the following for example:



In this instance, we have an overloaded method with three different instances of itself. The following can be used to target the various versions of the function:


Code:

// Targets: DecreaseDurability() : void
aobscanmono(pDecreaseDurabilityPtr1, 'Assembly-CSharp', 'ItemInstance', 'DecreaseDurability', '', '??')

// Targets: DecreaseDurability(int) : void
aobscanmono(pDecreaseDurabilityPtr2, 'Assembly-CSharp', 'ItemInstance', 'DecreaseDurability', 'int', '??')

// Targets: DecreaseDurability(float) : void
aobscanmono(pDecreaseDurabilityPtr3, 'Assembly-CSharp', 'ItemInstance', 'DecreaseDurability', 'single', '??')


You'll notice with the last one that the param type is 'single' in the aobscanmono call, but is seen as 'float' in the ILSpy output. This is normal and expected for how .NET treats the type internally. You should use the expected internal type and not the 'flavour' types that are just shortcuts to the system types.

By passing a single ?? byte for the pattern, this will return the start of the function as the pointer. Otherwise, pass the full/proper pattern you wish to find within the method.

You can also pass 'nil' as the signature now directly and it will resume the original behavior of finding the first matching method by name and returning that immediately. Such as:

Code:

aobscanmono(pDecreaseDurabilityPtr1, 'Assembly-CSharp', 'ItemInstance', 'DecreaseDurability', nil, '??')


Which will find 'DecreaseDurability() : void' in this instance because it is the first method in the class method enum.

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions 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