Здравствуйте, гость ( Вход | Регистрация )

> lua, readmem, unicode, Считать из памяти строку в формате UTF-16
фонарик
сообщение 19.2.2024, 14:37
Сообщение #1


****

Apprentice
Сообщений: 146
Регистрация: 23.11.2011
Группа: Пользователи
Наличность: 2
Пользователь №: 14.305



Здравствуйте. Не знаю правильно ли я думаю, но пилот не умеет понимать строку в юникоде. Из интернетов были добыты эльфийские инструменты плагины, которые (если судить по светящимся надписям на них) как раз созданы магами-чародеями программистами для таких задач. Но тут вспоминается басня Крылова "Мартышка и очки". На рабочем столе уже не осталось места для размещения очередного артефакта. И вот в печалях, в довлеющем ощущении тяжести и бренности бытия, спешу я снова к вам в лавку алхимиков на форум за советами и помощью.

Вот, например, у одного человека, создаваемый велосипед заканчивается чтением памяти:
Код
Name = readmem ("0x07CCFFD8", "S", 16)
В Name записывется какое-то нечто, вообще не связанное с реальностью. По данному адресу находится:
Код
1D 04 30 04 31 04 3E 04 40 04
что в переводе с древнего наречья с UTF-16 читается как слово "Набор".

То ли я где-то прочёл, то ли кто-то сказал, но я почему-то знаю что необходимо обзавестись синей изолентой плагином, который нужно правильно причесать, положить в правильное место и создать заклинание на ассемблере правильную функцию, которая и считает из памяти всё в правильном виде. Но без вашей помощи мне не справится, я вообще даже не понимаю, правильно ли я в Воронеж ехал сделал ли я хоть что-то правильное в своих попытках. Пожалуйста, поделитесь советом, как же всё-таки данные из памяти правильно приготовить? Легенды гласят, доподлинно известно, что это строка в кодировке UTF-16.
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
 
Ответить в эту темуОткрыть новую тему
Ответов
DarkMaster
сообщение 13.3.2024, 18:12
Сообщение #2


***********

Модератор UOPilot
Сообщений: 9.480
Регистрация: 2.12.2008
Группа: Супермодераторы
Наличность: 27963
Пользователь №: 11.279



Цитата
пока я только понял что ты просто не хочешь выделять несколько раз память и используешь один буффер под разные данные.

опыт с финдимиджем показал, что malloc столько жрет за вызов, что я теперь готов на любые извращения лишь бы не выделять память лишний раз) Вопрос правда уже не в этом был =)

Цитата
чем копировать - не знаю, вряд ли есть что-то быстрее memcpy в терминах си. наверняка у луа есть такой аналог

вообще вроде как можно через string.dump, но чет сомнительно, сделал через ffi.copy. Там спецом под строки есть вариант синтаксиса, но советовали точно не через него. Правда не думаю, что там будет какая-то фатальная разница. ffi.copy насколько я понимаю чуток обернутый memcopy и strcopy.

Собственно поделка.
mem = require"memory"
mem.read(address, data_type[[, lenght], codepage] - чтение памяти процесса
mem.write(data, address, data_type[, codepage]) - запись в память процесса
mem.set_buffer_size(size) - установить размер буфера.
mem.buffer_raw - указатель C на данные.

read(address, data_type[[, lenght], codepage])
Считывает данные из памяти процесса. Возвращает первый считаный элемент. Если считывалася string или unicode - вернет полную строку. Для поэлементного/посимвольного доступа используйте указатель buffer_raw.

address - адрес откуда производить чтение..

data_type - тип данных. Си тип, указывается в виде строки с учетом регистра (INT и int - два разных типа). Если вы объявили какой-либо пользовательский тип, то тоже будет будет его читать. Со структурами могут быть проблемы.
Из коробки знает:

char
short
int
long
и т.д.
допускается использование unsigned

Объявленные типы:
BOOL
BYTE
WORD
DWORD
LPVOID
LPCVOID
LPDWORD
PVOID
HANDLE
HWND
LONG_PTR
SIZE_T
UINT
WCHAR
LPWSTR
LPCCH
LPCWCH
CHAR
LPSTR
LPBOOL // must be "typedef BOOL far *LPBOOL;". far = error.

Так же отдельно создана возможность читать строки. Для этого необходимо указать тип:
"string" - для ASCII строки.
"unicode" - для строки в UTF-16. Строка будет преобразована в стандартный lua тип string (по сути ASCII).

lenght - длина в количестве элементов (не в байтах). Необязательный параметр. Значение по умолчанию 1.

codepage - кодовая страница для преобразования unicode строки. Необязательный параметр. Значение по умолчанию nil.


write(data, address, data_type[, codepage])

data - данные для записи. Допускается использование массивов (кроме data_type string и unicode)

address - адрес куда производится запись.

data_type - тип данных. Си тип, указывается в виде строки с учетом регистра (INT и int - два разных типа). Если вы объявили какой-либо пользовательский тип, то тоже будет будет его читать. Со структурами могут быть проблемы.
Из коробки знает:

char
short
int
long
и т.д.
допускается использование unsigned

Объявленные типы:
BOOL
BYTE
WORD
DWORD
LPVOID
LPCVOID
LPDWORD
PVOID
HANDLE
HWND
LONG_PTR
SIZE_T
UINT
WCHAR
LPWSTR
LPCCH
LPCWCH
CHAR
LPSTR
LPBOOL // must be "typedef BOOL far *LPBOOL;". far = error.

Так же отдельно создана возможность читать строки. Для этого необходимо указать тип:
"string" - для ASCII строки.
"unicode" - для строки в UTF-16. Cтандартный lua тип string (по сути ASCII) будет преобразован в UTF-16 и записан.

codepage - кодовая страница для преобразования unicode строки.


set_buffer_size([size])
Функция устанавливает буфер для чтения и записи (он общий) размером size.
Если size не указан, то просто вернет текущий размер буфера.


buffer_raw
Указатель на Си массив со считанными данными согласно типу. В частности может быть полезен для посимвольного перебора строк, где
log(mem.buffer_raw[0]) - выведет первый символ строки.


code
Код
--lua

local ffi = require("ffi")
local C = ffi.C

ffi.cdef
[[
    typedef int            BOOL;
    typedef unsigned char  BYTE;
    typedef unsigned short WORD;
    typedef unsigned long  DWORD;
    typedef void *         LPVOID;
    typedef const void *   LPCVOID;
    typedef DWORD *        LPDWORD;
    typedef void *         PVOID;
    typedef PVOID          HANDLE;
    typedef HANDLE         HWND;
    typedef long           LONG_PTR;
    typedef LONG_PTR       SIZE_T;
    typedef unsigned int   UINT;
    typedef wchar_t        WCHAR;
    typedef WCHAR*         LPWSTR;
    typedef const char*    LPCCH;
    typedef const WCHAR*   LPCWCH;
    typedef char           CHAR;
    typedef CHAR*          LPSTR;
    typedef BOOL*          LPBOOL; // must be "typedef BOOL far *LPBOOL;". far = error.

    void*   malloc(
    size_t  size);

    void    free(
    void*   ptr);

    DWORD   __stdcall GetWindowThreadProcessId(
    HWND    hWnd,
    LPDWORD lpdwProcessId);

    HANDLE  __stdcall OpenProcess(
    DWORD   dwDesiredAccess,
    BOOL    bInheritHandle,
    DWORD   dwProcessId);

    BOOL    __stdcall CloseHandle(
    HANDLE  hObject);

    BOOL    __stdcall ReadProcessMemory(
    HANDLE  hProcess,
    LPCVOID lpBaseAddress,
    LPVOID  lpBuffer,
    SIZE_T  nSize,
    SIZE_T  *lpNumberOfBytesRead);

    int     __stdcall WriteProcessMemory(
    HANDLE  hProcess,
    LPVOID  lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T  nSize,
    SIZE_T* lpNumberOfBytesWritten);

    int     __stdcall MultiByteToWideChar(
    UINT    CodePage,
    DWORD   dwFlags,
    LPCCH   lpMultiByteStr,
    int     cbMultiByte,
    LPWSTR  lpWideCharStr,
    int     cchWideChar);

    int     __stdcall WideCharToMultiByte(
    UINT    CodePage,
    DWORD   dwFlags,
    LPCWCH  lpWideCharStr,
    int     cchWideChar,
    LPSTR   lpMultiByteStr,
    int     cbMultiByte,
    LPCCH   lpDefaultChar,
    LPBOOL  lpUsedDefaultChar);

    DWORD   __stdcall GetLastError();
]]
local kernel = ffi.load"kernel32"

local PROCESS_VM_READ  = 0x0010
local PROCESS_VM_WRITE = 0x0020

local export = {}

do
    local buffer_size = 0
    export.set_buffer_size = function(size)
        if size then
            export.buffer_raw = nil
            collectgarbage()
            export.buffer_raw = ffi.gc(C.malloc(size), C.free)
            buffer_size = size
        end
        return buffer_size
    end
end
export.set_buffer_size(128)

local read_memory = function (address, size) end
do
    local PID = ffi.new("DWORD[1]")
    read_memory = function (address, size)

        C.GetWindowThreadProcessId(ffi.cast("HWND", workwindow()), PID)
        local process = C.OpenProcess(PROCESS_VM_READ, true, PID[0]);

        if process and ffi.cast("int", process) > 0 then
            local result = C.ReadProcessMemory(
                process,
                ffi.cast("const void *", address),
                export.buffer_raw,
                size,
                nil)
            C.CloseHandle(process)
            if result ~= 0  then
                return true
            end
        end
        log('Process not opened') return nil, -2
    end
end

export.read = function (address, data_type, lenght, codepage)
    lenght   = lenght or 1
    codepage = codepage or 0

    if     data_type == "string" then
        if lenght > export.set_buffer_size() then
            export.set_buffer_size(lenght)
        end
       if read_memory(address, lenght) then
           export.buffer = ffi.cast("char *", export.buffer_raw)
           return ffi.string(export.buffer, lenght)
       end

    elseif data_type == "unicode" then
        if lenght*4 > export.set_buffer_size() then
            export.set_buffer_size(lenght*4)
        end

        if export.read(address, "string", lenght*4) then

            local wString = ffi.cast("short *", export.buffer_raw)
            local aString = ffi.new("char *", ffi.cast("char *", export.buffer_raw) + math.floor(export.set_buffer_size()/2))

            ffi.C.WideCharToMultiByte(codepage, 0, wString, lenght, aString, lenght, nil, nil)
            return ffi.string(aString, lenght)
        end
        return nil
    else
        if read_memory(address, lenght*ffi.sizeof(data_type)) then
            export.buffer = ffi.cast(data_type.." *", export.buffer_raw)
            return export.buffer[0]
        end
        return nil
    end
end

local write_memory = function (address, size) end
do
    local PID = ffi.new("DWORD[1]")
    write_memory = function (address, size)
        if size > export.set_buffer_size() then
            export.set_buffer_size(size)
        end

        C.GetWindowThreadProcessId(ffi.cast("HWND", workwindow()), PID)
        local process = C.OpenProcess(0x20+0x8, true, PID[0]);

        local result = C.ReadProcessMemory(
                process,
                ffi.cast("const void *", address),
                export.buffer_raw,
                size,
                nil)

        if  process and ffi.cast("int", process) > 0 then
            local result = C.WriteProcessMemory(
                process,
                ffi.cast("void*", address),
                ffi.cast("const void*", export.buffer_raw),
                size,
                wrote)

            C.CloseHandle(process)
            if result ~= 0  then
                return true
            else
                return false, "no data wrote"
            end
        end
        log('Process not opened') return nil, -2
    end
end

export.write = function (data, address, data_type, codepage)
    if     data_type == "string"  then
       if #data + 1 > export.set_buffer_size() then
            export.set_buffer_size(#data + 1)
       end
       ffi.copy(export.buffer_raw, data)

       if write_memory(address, #data) then
           export.buffer = ffi.cast("char *", export.buffer_raw)
           return true
       end


    elseif data_type == "unicode" then
        codepage = codepage or 0
        local wString = ffi.cast("short *", export.buffer_raw)

        C.MultiByteToWideChar(codepage, 0, data, #data, wString, #data);
        if write_memory(address, #data*2) then
            export.buffer = ffi.cast("char *", export.buffer_raw)
            return true
        end
    else
        data = type(data) == table and data or {data}
        local type_size = ffi.sizeof(data_type)
        local data_size = #data*type_size

        if data_size > export.set_buffer_size() then
           export.set_buffer_size(data_size)
        end

        -- Put data to buffer
        local pointer = ffi.cast(data_type .. " *", export.buffer_raw)
        for i = 1, #data do
            pointer[i-1] = ffi.cast(data_type, data[i])
        end

        export.buffer = ffi.cast(data_type.." *", export.buffer_raw)

        if write_memory(address, data_size) then
            return true
        end
    end

    return nil
end

return export


Оно сыровато, но вроде работает. Времени катастрофически не хватает.

Сообщение отредактировал DarkMaster - 16.3.2024, 5:13


--------------------
Скрипты UOPilot под заказ.
Консультации по UOpilot 15$/час.
Услуги Lua разработчика (не пилот, проекты, постоянка)
Disсоrd:
Kov____
Пользователь в онлайне!Delete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения

Сообщений в этой теме
фонарик   lua, readmem, unicode   19.2.2024, 14:37
DarkMaster   Задача именно работать с юникодом или просто счита...   19.2.2024, 15:37
фонарик   Задача именно работать с юникодом или просто счит...   19.2.2024, 15:43
DarkMaster   --lua log"clear" local ffi = require...   19.2.2024, 16:01
фонарик   Тебя интересует функция export.unicode(address, le...   19.2.2024, 16:22
DarkMaster   никаких кавычек. значения переданы верно. рабочее ...   19.2.2024, 16:25
фонарик   никаких кавычек. значения переданы верно. рабочее ...   19.2.2024, 16:27
DarkMaster   считай как string. есть зябрики?   19.2.2024, 16:29
фонарик   А еще в логе перед строкой с nil есть строка с соо...   19.2.2024, 16:30
DarkMaster   --lua log"clear" local ffi = require...   19.2.2024, 16:36
фонарик   что в логе теперь? 16:37:50 70 (, 0...   19.2.2024, 16:38
DarkMaster   --lua log"clear" local ffi = require...   19.2.2024, 16:42
фонарик   Пробуй. Там читалось окно пилота =) я тестил то на...   19.2.2024, 16:48
DarkMaster   Это больше спасибо cirus и Cockney, которых я сего...   19.2.2024, 16:50
фонарик   Это больше спасибо cirus и Cockney, которых я сего...   19.2.2024, 17:05
DarkMaster   естественно просмотрел. добавьте в конце моего скр...   19.2.2024, 17:26
фонарик   Ну вот, вообще теперь красота :lol: Здоровья и мир...   19.2.2024, 17:42
DarkMaster   А теперь немного о быдло коде =) Меня очень сильно...   19.2.2024, 18:52
Cockney   Так какой вопрос то ?) Тут поинт в том, что два...   19.2.2024, 19:25
DarkMaster   Ну вроде как да, по логике... Но проверка идет то...   19.2.2024, 19:31
Cockney   В общем случае думаю что не прокатит, т.к. там не ...   19.2.2024, 20:02
Aqualon   Если вам просто надо спарсить utf8 строку из памят...   20.2.2024, 2:34
DarkMaster   Можно этот момент поподробнее? Там есть какие-то ...   22.2.2024, 16:42
Cockney   Ну, а какие там стандарты...из описания функция ...   22.2.2024, 20:40
DarkMaster   имелось ввиду возможность прочитать объявленный п...   12.3.2024, 18:44
Cockney   тут лучше на примерах, задача то какая ? пока я то...   12.3.2024, 22:21
Cockney   короче как я понял ты костылишь сериализатор/десер...   15.3.2024, 21:20


Ответить в эту темуОткрыть новую тему
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 

- Текстовая версия | Версия для КПК Сейчас: 2.6.2024, 12:05
Designed by Nickostyle