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

2 страниц V < 1 2  
Ответить в эту темуОткрыть новую тему
> lua, readmem, unicode, Считать из памяти строку в формате UTF-16
Cockney
сообщение 19.2.2024, 20:02
Сообщение #21


********

Master
Сообщений: 1.395
Регистрация: 22.6.2013
Группа: Пользователи
Наличность: 21064
Пользователь №: 16.156



В общем случае думаю что не прокатит, т.к. там не ясно как читается буффер + один код поинт юникода может быть разложен на условные 3-4 байта, что повредит следующий символ в юникод буффере.

Если речь об ограниченном множестве языков, то наверное так и можно сделать.
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
Aqualon
сообщение 20.2.2024, 2:34
Сообщение #22


**

Neophyte
Сообщений: 23
Регистрация: 21.3.2023
Группа: Пользователи
Наличность: 1
Пользователь №: 20.503
Возраст: 22



Если вам просто надо спарсить utf8 строку из памяти, я для такого пользовался примерно таким ужасом, но сетапится этот ужас относительно просто
Код

-- либу с утф возьмите отсюда
-- https://github.com/Stepets/utf8.lua
utf8 = require('lua/utf8')
-- Я могу ошибаться, но лично у меня этот файлик лежал по этому пути.
-- Тут лежат функции для конвертации из utf8 в win кодировки и обратно
require('lua/win-125x')

function utf8_from(t)
  local bytearr = {}
  for _, v in ipairs(t) do
    local utf8byte = v < 0 and (0xff + v + 1) or v
    table.insert(bytearr, utf8.char(utf8byte))
  end
  return table.concat(bytearr)
end

function parseUnicodeStringFromMemory(startAddr)
    -- максимальное количество символов, поставьте нужное вам, это чисто оградка.
    -- Если строка ваша null-terminated, вернёт он именно ту что вам нужна.
    local UPPER_BOUND = 20
    local result = {}

    for i = 1, UPPER_BOUND do
        local step = 0x2 * (i - 1)

        local char = readmem(startAddr + step, 'w')

        if char == 0 then break end

        table.insert(result, char)
    end

    return utf8_to_win(utf8_from(result))
end
-- Пример использования в вашем случае, адрес взял у вас из топика
local name = parseUnicodeStringFromMemory(0x07CCFFD8)
log(name)

Безусловно, вышеописанный ужас не заменит нормальную поддержку ридмема с utf8, но это рабочее решение с минимумом дополнительных действий.
Я со временем вообще ушёл от работы со строками, особенно с utf строками, для тех мест где нужна человеческая читаемость я замапил айдишники с вручную выписанными пояснениями в отдельном файлике с константами, это убирает кучу геморроя.
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
DarkMaster
сообщение 22.2.2024, 16:42
Сообщение #23


***********

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



Цитата
один код поинт юникода может быть разложен на условные 3-4 байта

Можно этот момент поподробнее? Там есть какие-то лимиты по страндарту? Я прямо _очень_ не хочу делать два буфера. Можно использовать один, раздавать непересекающиеся указатели, все кошерно. Но мне нужно astring закидывать в начало буфера. Соответственно, если я считаю кусок памяти в некоторый оффсет буфера, а потом у меня не влезет astring, то ресайз делать вообще не хорошо. Или может можно посчтитать len до копирования памяти из другого процесса (без инжектов)?

Вообще идейно человек может считать n элементов любого типа, функция вернет первый элемент, в случае строки - вернет всю строку типа lua string. Но вот буфер я хочу оставить открытым для пользователя. Т.е. будет свободный доступ в виде массива к каждому считанному элементу. Соответственно будет удобно искать некторые структуры для которых сложно найти статичную цепочку указателей, возможность посимвольной переборки строк. Вобщем красота.

Сообщение отредактировал DarkMaster - 22.2.2024, 16:46


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


********

Master
Сообщений: 1.395
Регистрация: 22.6.2013
Группа: Пользователи
Наличность: 21064
Пользователь №: 16.156



Цитата
Можно этот момент поподробнее? Там есть какие-то лимиты по страндарту?


Ну, а какие там стандарты...из описания функция конвертит utf16 -> multi byte (до 4 байтов включительно на 1 символ в случае если конвертация в utf8 происходит, до 2 байт если конвертация в соотв. кодовую таблицу).

Цитата
Можно использовать один, раздавать непересекающиеся указатели, все кошерно. Но мне нужно astring закидывать в начало буфера. Соответственно, если я считаю кусок памяти в некоторый оффсет буфера, а потом у меня не влезет astring, то ресайз делать вообще не хорошо. Или может можно посчтитать len до копирования памяти из другого процесса (без инжектов)?


Не осилил, особенно когда речь пошла про другой процесс. Похоже на реальный быдлокод.

Цитата
Вообще идейно человек может считать n элементов любого типа, функция вернет первый элемент, в случае строки - вернет всю строку типа lua string. Но вот буфер я хочу оставить открытым для пользователя. Т.е. будет свободный доступ в виде массива к каждому считанному элементу. Соответственно будет удобно искать некторые структуры для которых сложно найти статичную цепочку указателей, возможность посимвольной переборки строк. Вобщем красота.


абстрактный конь в вакууме. какие элементы, какого типа, какие структуры с произвольным поиском. а как это еще упаковывается в MANAGED луа строку еще интереснее.



В нормальных системах/языках/библиотеках принято оптимизировать за счет всяких пулов строк/буферов, различных кешей. А городить какие то там подбуферы без четкого понимания что куда и как ляжет и как это все сожрет компилятор/интерпретатор - себе дороже, Лучше сделать с реаллокациями но предсказуемым поведением + найти вектор оптимизации другой
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
DarkMaster
сообщение 12.3.2024, 18:44
Сообщение #25


***********

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



Цитата
абстрактный конь в вакууме. какие элементы, какого типа, какие структуры с произвольным поиском. а как это еще упаковывается в MANAGED луа строку еще интереснее.

имелось ввиду возможность прочитать объявленный пользователем тип. Понятное дело, что не панацея, но это уход от ограничения вшитых пары типов. Да может разбиться о первый же pragma pack, но идейно это правильно и в целом лучше.

Чем быстро копировать lua string в уже аллоцированную область памяти? Была какая-то функция для этого, кто-то из "великих" писал на mailing lists. Вспомнить не могу и найти не получается... Давно дело было...


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


********

Master
Сообщений: 1.395
Регистрация: 22.6.2013
Группа: Пользователи
Наличность: 21064
Пользователь №: 16.156



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

чем копировать - не знаю, вряд ли есть что-то быстрее memcpy в терминах си. наверняка у луа есть такой аналог
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
DarkMaster
сообщение 13.3.2024, 18:12
Сообщение #27


***********

Модератор UOPilot
Сообщений: 9.467
Регистрация: 2.12.2008
Группа: Супермодераторы
Наличность: 27725
Пользователь №: 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Отправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения
Cockney
сообщение 15.3.2024, 21:20
Сообщение #28


********

Master
Сообщений: 1.395
Регистрация: 22.6.2013
Группа: Пользователи
Наличность: 21064
Пользователь №: 16.156



короче как я понял ты костылишь сериализатор/десериализатор да и еще zero-copy.

ну ниче умнее flatbuffers/protobuffers и их разновидностей еще не придумали, но там почти нигде нет прямого доступа до данных.
Пользователь в офлайнеDelete PostОтправить личное сообщение
Вернуться в начало страницы
+Ответить с цитированием данного сообщения

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

 

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