AHK скрипт Реализация Mcode на примере AOB сканера

Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
141
Реакции[?]
33
Поинты[?]
4K
Мотиувацию я поднял и решил, всё-таки, разобраться с Mcode (Machine code). В прошлом гайде про Fcode, я показывал костыль с загрузкой .dll в AutoHotkey скрипт. Все работало замечательно, но костыли - это не хорошо, нужно исправлять ситуацию. Для ленивых, готовый скрипт со всеми моими функциями в самом конце.

-Почему костыль?
-Лишний .dll файл, который оставляет след.

И так, напомню, Fcode - это .dll файл на языке C++, в котором прописана логика сканирования байт модуля процесса или всех модулей и родительского процесса в целом. Чтобы эта шарманка работала, данную .dll приходилось загружать в AutoHotkey скрипт путём #DllLoad "*i Fcode64.dll" и вызывать с помощью DllCall("Fcode64\module". Теперь эту гадость можно выкинуть на помойку, ибо есть Mcode, который в разы удобнее в использовании, так как для его использования не нужно загружать сторонние .dll и другие библиотеки, а скорость, как всегда, осталась прежней, потому что код написан на языке C.

Как говорит
Пожалуйста, авторизуйтесь для просмотра ссылки.
- "Сценарии AHK образуют противоположность скомпилированного кода. В сценарии AHK интерпретатор просматривает текст сценария, а затем выполняет соответствующий код.
На компилируемом языке (например. C++) компилятор создает такой Код напрямую.
ЦП непосредственно способен читать такой код.
MCode - это такой скомпилированный код, который можно использовать внутри скрипта AHK."


Вот как выглядит код на C, сканирующий байты на нужную нам последовательность:
AOB:
#include <stdio.h>
#include <string.h>

int compareBytes(char *buffer, char *pattern, int bufferLength, int patternLength, int extra) {
    int j;
    for (int i = 0; i <= bufferLength - patternLength; i++) {
        for (j = 0; j < patternLength; j++) {
            if (pattern[j] != -1 && buffer[i + j] != pattern[j]) {
                break;
            }
        }
        if (j == patternLength) {
            return i + extra;
        }
    }
    return -100;
}
Вот как вызывать эту функцию поиска с помощью AutoHotkey:
AutoHotkey v2:
result := DllCall(MCode("2,x86:VVdWU4tEJCCLbCQci1wkGCnFeEqLdCQUMf+NtgAAAAAx0oXAfiqNtCYAAAAAjXYAD7YME4D5/3QFOgwWdRKDwgE50HXri0QkJFteAfhfXcM50HTxg8cBg8YBOf19wlu4nP///15fXcM=,x64:VlNIidZIicsx0kUpyHhOSYnyMclFhcl+Nw8fgAAAAABFD7YaQYD7/3QLjQQRSJhEOhwDdRuDwQFJg8IBQTnJdd+LRCQ4AdBbXsNmDx9EAABBOcl07IPCAUE50H2yuJz///9bXsM="), "Ptr", byte, "Ptr", create_array_cpp, "Int", byte.Size, "Int", create_array_cpp.Size, "Int", EXTRA, "Cdecl")
MsgBox(result)

MCode(mcode)
{
    global
    e := [[0x00000004], [0x00000001]]
    c := (A_PtrSize == 8) ? "x64" : "x86"

    if (!RegExMatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", &m))
    {
        return
    }
    if (!DllCall("crypt32\CryptStringToBinary", "Str", m[3], "UInt", 0, "UInt", e[m[1]][1], "Ptr", 0, "UInt*", &s := 0, "Ptr", 0, "Ptr", 0))
    {
        return
    }
    p := DllCall("GlobalAlloc", "UInt", 0, "Ptr", s, "Ptr")
    if (c == "x64")
    {
        DllCall("VirtualProtect", "Ptr", p, "Ptr", s, "UInt", 0x40, "UInt*", &op := 0)
    }
    if (DllCall("crypt32\CryptStringToBinary", "Str", m[3], "UInt", 0, "UInt", e[m[1]][1], "Ptr", p, "UInt*", s, "Ptr", 0, "Ptr", 0))
    {
        return p
    }
    DllCall("GlobalFree", "Ptr", p)
}
Допишем немного логики уже внутри AHK, и на выходе получаем функцию поиска паттерна внутри определенного модуля:
FAST_SEARCH_SIGNATURE_IN_MODULE:
;Примеры для получения смещений в CS2
;OFFSETS
dwLocalPlayerController := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8B 05 ?? ?? ?? ?? 48 85 C0 74 4F", 3)) + TempAddress - Client + 4
dwLocalPlayerPawn       := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8D 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 83 EC ?? 8B 0D", 3)) + TempAddress - Client + 0x118 + 4
dwEntityList            := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8B 0D ?? ?? ?? ?? 48 89 7C 24 ?? 8B FA C1 EB", 3)) + TempAddress - Client + 4

;NETVARS
m_fFlags                := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("F6 80 ?? ?? ?? ?? ?? 75 ?? 48 8B 0D", 2))
m_iHealth_Controller    := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("8B 81 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC 40 57", 2))
m_iHealth_Pawn          := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("8B 81 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC 40 53 48 83 EC ?? 33 C9 E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 84", 2))

FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    static MEM_COMMIT                  := 0x1000
    static PAGE_NOACCESS               := 0x01
    MEMORY_BASIC_INFORMATION := Buffer(A_PtrSize ? 48 : 28, 0)

    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }

    RegionSize := 0
    while RegionSize <= modBaseSize
    {
        if(DllCall("VirtualQueryEx", "Ptr", ProcessHandle, "Ptr", START_ADDRESS += RegionSize, "Ptr", MEMORY_BASIC_INFORMATION, "Ptr", MEMORY_BASIC_INFORMATION.Size))
        {
            BaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
            RegionSize  := NumGet(MEMORY_BASIC_INFORMATION, (A_PtrSize = 8 ? 24 : 12), "Ptr")
            Protect     := NumGet(MEMORY_BASIC_INFORMATION, (A_PtrSize = 8 ? 36 : 20), "Ptr")
            if (Protect && MEM_COMMIT and not Protect & PAGE_NOACCESS)
            {
                byte := Buffer(RegionSize, 0)
                if (DllCall("Kernel32\ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", BaseAddress, "Ptr", byte, "Ptr", RegionSize, "UInt*", 0))
                {
                    result  := DllCall(MCode("2,x86:VVdWU4tEJCCLbCQci1wkGCnFeEqLdCQUMf+NtgAAAAAx0oXAfiqNtCYAAAAAjXYAD7YME4D5/3QFOgwWdRKDwgE50HXri0QkJFteAfhfXcM50HTxg8cBg8YBOf19wlu4nP///15fXcM=,x64:VlNIidZIicsx0kUpyHhOSYnyMclFhcl+Nw8fgAAAAABFD7YaQYD7/3QLjQQRSJhEOhwDdRuDwQFJg8IBQTnJdd+LRCQ4AdBbXsNmDx9EAABBOcl07IPCAUE50H2yuJz///9bXsM="), "Ptr", byte, "Ptr", create_array_cpp, "Int", byte.Size, "Int", create_array_cpp.Size, "Int", EXTRA, "Cdecl")
                    if (result != -100)
                    {
                        return Format("0x{:02X}", BaseAddress + result)
                    }
                }
            }
        }
    }
    return -100
}
Инструмент, который я использовал для создания скомпилированного кода, называется
Пожалуйста, авторизуйтесь для просмотра ссылки.
, а компилятор, который я использовал, -
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad).

Как и обещал, моя база, которой пользуюсь лично, работает только в 64-битном режиме, и никак не доходят руки исправить работоспособность в 32-битном режиме. Кто решит эту проблему, с меня цьом в лобик (не в лобок).

Сама база:
AutoHotkey v2:
#SingleInstance Force
CONNECTING_TO_GAME("cs2.exe", "client.dll")

;OFFSETS
dwLocalPlayerController := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8B 05 ?? ?? ?? ?? 48 85 C0 74 4F", 3)) + TempAddress - Client + 4
dwLocalPlayerPawn       := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8D 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 83 EC ?? 8B 0D", 3)) + TempAddress - Client + 0x118 + 4
dwEntityList            := READ_INT32(TempAddress := FAST_SEARCH_SIGNATURE_IN_MODULE("48 8B 0D ?? ?? ?? ?? 48 89 7C 24 ?? 8B FA C1 EB", 3)) + TempAddress - Client + 4

;NETVARS
m_fFlags                := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("F6 80 ?? ?? ?? ?? ?? 75 ?? 48 8B 0D", 2))
m_iHealth_Controller    := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("8B 81 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC 40 57", 2))
m_iHealth_Pawn          := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("8B 81 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC 40 53 48 83 EC ?? 33 C9 E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 84", 2))

CONNECTING_TO_GAME(NAME_GAME, DLL_GAME)
{
    global PID, Client, ProcessHandle
    static PROCESS_VM_READ              := 0x0010
    static PROCESS_QUERY_INFORMATION    := 0x0400
    static PROCESS_ALL_ACCESS           := 0x1F0FFF
    if (!PID        := ProcessWait(NAME_GAME, 60))
    {
        MsgBox("Игра не найдена!")
        ExitApp()
    }
    ProcessHandle   := DllCall("OpenProcess", "UInt", PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, "UChar", 0, "UInt", PID)
    Client          := GET_DLL_BASE(DLL_GAME, PID)
}
GET_DLL_BASE(DLL_GAME, PID)
{
    static TH32CS_SNAPPROCESS   := 0x00000002
    static TH32CS_SNAPMODULE    := 0x00000008
    static TH32CS_SNAPMODULE32  := 0x00000010
    global modBaseSize, hModule
    while hSnapshot := DllCall("CreateToolhelp32Snapshot", "UInt", (TH32CS_SNAPPROCESS | TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32), "UInt", PID)
    {
        MODULEENTRY32 := Buffer(A_PtrSize = 8 ? 568 : 548, 0)
        NumPut("UInt", MODULEENTRY32.Size, MODULEENTRY32, 0)
        result := DllCall("Module32First", "UInt", hSnapshot, "Ptr", MODULEENTRY32.Ptr)
        while result
        {
            if (DLL_GAME == StrGet(MODULEENTRY32.Ptr + (A_PtrSize = 8 ? 48 : 32), 256, "CP0"))
            {
                modBaseSize := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "Ptr")
                hModule     := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 40 : 28), "Ptr")
                DllCall("CloseHandle", "UInt", hSnapshot)
                return modBaseAddr := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "Ptr")
            }
            result := DllCall("Module32Next", "UInt", hSnapshot, "Ptr", MODULEENTRY32.Ptr)
        }
        DllCall("CloseHandle", "UInt", hSnapshot)
    }
}
FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    static MEM_COMMIT                  := 0x1000
    static PAGE_NOACCESS               := 0x01
    MEMORY_BASIC_INFORMATION := Buffer(A_PtrSize ? 48 : 28, 0)

    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }

    RegionSize := 0
    while RegionSize <= modBaseSize
    {
        if(DllCall("VirtualQueryEx", "Ptr", ProcessHandle, "Ptr", START_ADDRESS += RegionSize, "Ptr", MEMORY_BASIC_INFORMATION, "Ptr", MEMORY_BASIC_INFORMATION.Size))
        {
            BaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
            RegionSize  := NumGet(MEMORY_BASIC_INFORMATION, (A_PtrSize = 8 ? 24 : 12), "Ptr")
            Protect     := NumGet(MEMORY_BASIC_INFORMATION, (A_PtrSize = 8 ? 36 : 20), "Ptr")
            if (Protect && MEM_COMMIT and not Protect & PAGE_NOACCESS)
            {
                byte := Buffer(RegionSize, 0)
                if (DllCall("Kernel32\ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", BaseAddress, "Ptr", byte, "Ptr", RegionSize, "UInt*", 0))
                {
                    result  := DllCall(MCode("2,x86:VVdWU4tEJCCLbCQci1wkGCnFeEqLdCQUMf+NtgAAAAAx0oXAfiqNtCYAAAAAjXYAD7YME4D5/3QFOgwWdRKDwgE50HXri0QkJFteAfhfXcM50HTxg8cBg8YBOf19wlu4nP///15fXcM=,x64:VlNIidZIicsx0kUpyHhOSYnyMclFhcl+Nw8fgAAAAABFD7YaQYD7/3QLjQQRSJhEOhwDdRuDwQFJg8IBQTnJdd+LRCQ4AdBbXsNmDx9EAABBOcl07IPCAUE50H2yuJz///9bXsM="), "Ptr", byte, "Ptr", create_array_cpp, "Int", byte.Size, "Int", create_array_cpp.Size, "Int", EXTRA, "Cdecl")
                    if (result != -100)
                    {
                        return Format("0x{:02X}", BaseAddress + result)
                    }
                }
            }
        }
    }
    return -100
}
MCode(mcode)
{
    global
    e := [[0x00000004], [0x00000001]]
    c := (A_PtrSize == 8) ? "x64" : "x86"

    if (!RegExMatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", &m))
    {
        return
    }
    if (!DllCall("crypt32\CryptStringToBinary", "Str", m[3], "UInt", 0, "UInt", e[m[1]][1], "Ptr", 0, "UInt*", &s := 0, "Ptr", 0, "Ptr", 0))
    {
        return
    }
    p := DllCall("GlobalAlloc", "UInt", 0, "Ptr", s, "Ptr")
    if (c == "x64")
    {
        DllCall("VirtualProtect", "Ptr", p, "Ptr", s, "UInt", 0x40, "UInt*", &op := 0)
    }
    if (DllCall("crypt32\CryptStringToBinary", "Str", m[3], "UInt", 0, "UInt", e[m[1]][1], "Ptr", p, "UInt*", s, "Ptr", 0, "Ptr", 0))
    {
        return p
    }
    DllCall("GlobalFree", "Ptr", p)
}
READ_BYTE(ADDRESS)
{
    if (DllCall("Kernel32\ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "UChar*", &BYTE := 0, "UInt", 1, "UInt*", 0))
        return BYTE
}
READ_INT32(ADDRESS)
{
    if (DllCall("Kernel32\ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "UInt*", &INT32 := 0, "UInt", 4, "UInt*", 0))
        return INT32
}
READ_INT64(ADDRESS)
{
    if (DllCall("Kernel32\ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "Ptr*", &UINT64 := 0, "UInt", 8, "UInt*", 0))
        return UINT64
}
WRITE_BYTE(ADDRESS, VALUE)
{
    return DllCall("Kernel32\WriteProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "UChar*", VALUE, "UInt", 1, "UInt *", 0)
}
WRITE_INT32(ADDRESS, VALUE)
{
    return DllCall("Kernel32\WriteProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "UInt*", VALUE, "UInt", 4, "UInt *", 0)
}
WRITE_INT64(ADDRESS, VALUE)
{
    return DllCall("Kernel32\WriteProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "Ptr*", VALUE, "UInt", 8, "UInt *", 0)
}
WRITE_FLOAT(ADDRESS, VALUE)
{
    return DllCall("Kernel32\WriteProcessMemory", "Ptr", ProcessHandle, "Ptr", ADDRESS, "float*", VALUE, "UInt", 4, "UInt *", 0)
}

INS:: Pause -1
END:: ExitApp
Осталось организовать поиск по всем модулям для тех, кому это нужно, но по моим наблюдениям, поиск внутри модуля более удобен. Всем мира!
 
vk.com/ahkcsgocheat
Пользователь
Статус
Оффлайн
Регистрация
21 Апр 2020
Сообщения
378
Реакции[?]
64
Поинты[?]
2K
Норм конечно придумано.
А как ты получаешь нетвары через поиск сигнатур? Помню нетвары так нельзя получать. Хотя кс может изменилась уже.
 
Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
141
Реакции[?]
33
Поинты[?]
4K
Норм конечно придумано.
А как ты получаешь нетвары через поиск сигнатур? Помню нетвары так нельзя получать. Хотя кс может изменилась уже.
Я делаю так, по-моему принцип остался таким же, как и в CS:GO.

Давайте рассмотрим на примере - m_fFlags.

Для начала найдем сигнатуру для m_fFlags:
Мы знаем, что когда мы стоим на земле (стоя), значение равно 65665, а когда мы в воздухе (стоя), значение равно 65664. Также мы знаем настоящий смещение для m_fFlags, которое равно 0x3С8.

Ищем это с помощью Cheat Engine:
Найдено 2 адреса (в моем случае). Нажмем дважды на каждый из них, и теперь они находятся внизу в Cheat Engine. Теперь выберем, например, первый адрес и нажмем на него левой кнопкой мыши, затем правой кнопкой и выберем "Find out what accesses this address".

Мы видим, что первый адрес подходит, так как значения m_fFlags совпадают с настоящим значением - 0x3С8:

1697979382562.png

Нажимаем на Show disassembler и видим инструкцию rax + 3C8, а также видим байты ведущие к ней:

1697986584134.png

Мы видим байты F6 80 C8 03 00 00 20 75 59 48 8B 0D. Теперь нам нужно создать маску для поиска, и для этого мы можем воспользоваться плагином, предназначенным для создания готового паттерна в Cheat Engine. В данном случае, я использую x64 версию этого
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad).

Установка плагина:
  1. Скачайте x64 версию плагина.
  2. Перенесите скачанный плагин в папку "C:\Program Files\Cheat Engine 7.5\plugins".
  3. Запустите Cheat Engine.
  4. Перейдите в меню ''Настройки'', затем выберите ''Плагины''.
  5. Нажмите ''Добавить новый''.
  6. Перейдите в папку с установленным плагином.
  7. Выберите плагин и установите галочку рядом с ним.
  8. Нажмите ''ОК''.
  9. Готово.

Выбираем нашу инструкцию, кликаем по ней левой кнопкой мыши, затем правой кнопкой, и в выпадающем списке выбираем 'Generate signature'. После этого нажимаем ''ОК''.

1697984402291.png


Теперь у нас в буфере обмена хранится готовый паттерн:

Pattern:
0x7ffd64578284

client.dll + 0x638284

f6 80 ?? ?? ?? ?? ?? 75 ?? 48 8b 0d

\xf6\x80\xae\xae\xae\xae\xae\x75\xae\x48\x8b\x0d xx?????x?xxx

static constexpr std::array< std::uint8_t, 12 > client_dll_638284{
    0xf6, 0x80, 0xae, 0xae, 0xae, 0xae, 0xae, 0x75,
    0xae, 0x48, 0x8b, 0x0d
};
Мне лично нужен - f6 80 ?? ?? ?? ?? ?? 75 ?? 48 8b 0d.

И так, теперь внимательно - F6 80 это RAX (локальный игрок), он нам не нужен, по этому мы пропускаем 2 байта - F6 80
-Как именно пропускаем?
-К результату поиска F6 80 ?? ?? ?? ?? ?? 75 ?? 48 8B 0D добавляем + 2 (байта). На выходе получаем адрес m_fFlags в модуле client.dll
Теперь читаем этот адрес как INT32, на выходе получаем наш m_fFlags, который равен 0x3С8.

Пример из моего скрипта:
m_fFlags := READ_INT32(FAST_SEARCH_SIGNATURE_IN_MODULE("F6 80 ?? ?? ?? ?? ?? 75 ?? 48 8B 0D", 2))
Результат = 0x3C8

Как-то так делаю я.
 
vk.com/ahkcsgocheat
Пользователь
Статус
Оффлайн
Регистрация
21 Апр 2020
Сообщения
378
Реакции[?]
64
Поинты[?]
2K
Т.е. любой нетвар можно найти в инструкциях ассемблера?
 
Сверху Снизу