Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

UoKit.com Форумы _ UO Pilot _ lua, readmem, unicode

Автор: фонарик 19.2.2024, 14:37

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

Вот, например, у одного человека, создаваемый велосипед заканчивается чтением памяти:

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

То ли я где-то прочёл, то ли кто-то сказал, но я почему-то знаю что необходимо обзавестись синей изолентой плагином, который нужно правильно причесать, положить в правильное место и создать заклинание на ассемблере правильную функцию, которая и считает из памяти всё в правильном виде. Но без вашей помощи мне не справится, я вообще даже не понимаю, правильно ли я в Воронеж ехал сделал ли я хоть что-то правильное в своих попытках. Пожалуйста, поделитесь советом, как же всё-таки данные из памяти правильно приготовить? Легенды гласят, доподлинно известно, что это строка в кодировке UTF-16.

Автор: DarkMaster 19.2.2024, 15:37

Задача именно работать с юникодом или просто считать его в читаемом виде?(конвертнуть)

Автор: фонарик 19.2.2024, 15:43

Цитата(DarkMaster @ 19.2.2024, 15:37) *

Задача именно работать с юникодом или просто считать его в читаемом виде?(конвертнуть)
Никаких дополнительных манипуляций мне не нужно. Просто необходимо считать память по адресу и получить строку в виде нормального текста.

Автор: DarkMaster 19.2.2024, 16:01

Код

--lua
log"clear"

local ffi = require("ffi")
local C = ffi.C
local user = ffi.load('User32')

--bind WINAPI
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;


    void *malloc(size_t size);
    void free(void *ptr);

    DWORD GetWindowThreadProcessId(DWORD hWnd, LPDWORD lpdwProcessId);
    DWORD OpenProcess(DWORD dwDesiredAccess, BOOL  bInheritHandle, DWORD dwProcessId);
    BOOL  CloseHandle(DWORD hObject);

    int __stdcall MultiByteToWideChar(int cp, int flag, const char* src, int
        srclen, wchar_t* dst, int dstlen);
    int __stdcall WideCharToMultiByte(int cp, int flag, wchar_t *src, int
        srclen, char* dst, int dstlen, const char* defchar, int* used);
    int __stdcall WriteProcessMemory( void *hProcess,void *lpBaseAddress,void
        *lpBuffer,size_t nSize,size_t *lpNumberOfBytesWritten);

    BOOL ReadProcessMemory(
        DWORD  hProcess,
        DWORD  lpBaseAddress,
        LPVOID  lpBuffer,
        SIZE_T  nSize,
        SIZE_T  *lpNumberOfBytesRead
    );
]]

local PROCESS_VM_READ = 0x0010

local export = {}
export.set_buffer_size = function(size)
    export.buffer_raw = nil
    collectgarbage()
    export.buffer_raw = ffi.gc(C.malloc(size), C.free)
    export.buffer_size = size
end
export.set_buffer_size(128)

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

        user.GetWindowThreadProcessId(workwindow(), PID)
        local process = C.OpenProcess(PROCESS_VM_READ,true, PID[0]);

        if process > 0 then
            local result = C.ReadProcessMemory(-1, 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.char = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.byte = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("BYTE *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.short = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("short *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.word = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("WORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.int = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("int *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.dword = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("DWORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.float = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("float *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.double = function (address, lenght)
    if ReadMemory(address, lenght*8) then
        export.buffer = ffi.cast("double *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.string = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return ffi.string(export.buffer, lenght)
    end
    return nil
end

--unicode to ansistring
function w2astring (wString)

end

export.unicode = function (address, lenght)
    if export.string(address, lenght) then
        local wString = ffi.cast("short *", export.buffer_raw)
        local len = ffi.C.WideCharToMultiByte(0, 0, wString, -1, nil, 0, nil, nil)
        local aString = ffi.new("char[?]",len)
        ffi.C.WideCharToMultiByte(0, 0, wString, -1, aString, lenght, nil, nil)
        return ffi.string(aString)
    end
    return nil
end


Оно прям из под руки. Может быть сыро, косо. Тебя интересует функция export.unicode(address, lenght), где адрес - это адрес(кто бы догадался?), lenght - длина в символах для чтения. Не в байтах! В буквах!

Автор: фонарик 19.2.2024, 16:22

Цитата(DarkMaster @ 19.2.2024, 16:01) *
Тебя интересует функция export.unicode(address, lenght)
А как правильно передавать значения?
Код
str = export.unicode(0x07CCFFD8, 6)
и далее в ковычках и без, с 0x и без - возвращает nil sad.gif Как оказалось все сложно, я понимаю теперь почему длинные слова так расстраивали Винни Пуха unsure.gif

Автор: DarkMaster 19.2.2024, 16:25

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

Автор: фонарик 19.2.2024, 16:27

Цитата(DarkMaster @ 19.2.2024, 16:25) *
никаких кавычек. значения переданы верно. рабочее окно установлено?
Так точно, привязано вручную. Проверил в другом скрипте что считывает крякозябры - по этому адресу они и лежат.

Автор: DarkMaster 19.2.2024, 16:29

считай как string. есть зябрики?

Автор: фонарик 19.2.2024, 16:30

А еще в логе перед строкой с nil есть строка с сообщением Process not opened

Прикрепленное изображение

Но строка там есть по тому адресу. На скрине то, что читает пилот. Хотя там должно быть слово "Набор".

Автор: DarkMaster 19.2.2024, 16:36

Код

--lua
log"clear"

local ffi = require("ffi")
local C = ffi.C
local user = ffi.load('User32')

--bind WINAPI
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;


    void *malloc(size_t size);
    void free(void *ptr);

    DWORD GetWindowThreadProcessId(DWORD hWnd, LPDWORD lpdwProcessId);
    DWORD OpenProcess(DWORD dwDesiredAccess, BOOL  bInheritHandle, DWORD dwProcessId);
    BOOL  CloseHandle(DWORD hObject);

    int __stdcall MultiByteToWideChar(int cp, int flag, const char* src, int
        srclen, wchar_t* dst, int dstlen);
    int __stdcall WideCharToMultiByte(int cp, int flag, wchar_t *src, int
        srclen, char* dst, int dstlen, const char* defchar, int* used);
    int __stdcall WriteProcessMemory( void *hProcess,void *lpBaseAddress,void
        *lpBuffer,size_t nSize,size_t *lpNumberOfBytesWritten);

    BOOL ReadProcessMemory(
        DWORD  hProcess,
        DWORD  lpBaseAddress,
        LPVOID  lpBuffer,
        SIZE_T  nSize,
        SIZE_T  *lpNumberOfBytesRead
    );
]]

local PROCESS_VM_READ = 0x0010

local export = {}
export.set_buffer_size = function(size)
    export.buffer_raw = nil
    collectgarbage()
    export.buffer_raw = ffi.gc(C.malloc(size), C.free)
    export.buffer_size = size
end
export.set_buffer_size(128)

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

        user.GetWindowThreadProcessId(workwindow(), PID)
log("PID[0]: " .. PID[0])
        local process = C.OpenProcess(PROCESS_VM_READ,true, PID[0]);

        if process > 0 then
            local result = C.ReadProcessMemory(-1, 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.char = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.byte = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("BYTE *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.short = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("short *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.word = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("WORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.int = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("int *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.dword = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("DWORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.float = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("float *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.double = function (address, lenght)
    if ReadMemory(address, lenght*8) then
        export.buffer = ffi.cast("double *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.string = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return ffi.string(export.buffer, lenght)
    end
    return nil
end

--unicode to ansistring
function w2astring (wString)

end

export.unicode = function (address, lenght)
    if export.string(address, lenght) then
        local wString = ffi.cast("short *", export.buffer_raw)
        local len = ffi.C.WideCharToMultiByte(0, 0, wString, -1, nil, 0, nil, nil)
        local aString = ffi.new("char[?]",len)
        ffi.C.WideCharToMultiByte(0, 0, wString, -1, aString, lenght, nil, nil)
        return ffi.string(aString)
    end
    return nil
end



что в логе теперь?

Автор: фонарик 19.2.2024, 16:38

Цитата(DarkMaster @ 19.2.2024, 16:36) *
что в логе теперь?
Код
16:37:50 70 (, 0): PID[0]: 8652
16:37:50 70 (, 0): Process not opened
16:37:50 70 (, 0):  nil

Автор: DarkMaster 19.2.2024, 16:42

Код

--lua
log"clear"

local ffi = require("ffi")
local C = ffi.C
local user = ffi.load('User32')

--bind WINAPI
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;


    void *malloc(size_t size);
    void free(void *ptr);

    DWORD GetWindowThreadProcessId(DWORD hWnd, LPDWORD lpdwProcessId);
    DWORD OpenProcess(DWORD dwDesiredAccess, BOOL  bInheritHandle, DWORD dwProcessId);
    BOOL  CloseHandle(DWORD hObject);

    int __stdcall MultiByteToWideChar(int cp, int flag, const char* src, int
        srclen, wchar_t* dst, int dstlen);
    int __stdcall WideCharToMultiByte(int cp, int flag, wchar_t *src, int
        srclen, char* dst, int dstlen, const char* defchar, int* used);
    int __stdcall WriteProcessMemory( void *hProcess,void *lpBaseAddress,void
        *lpBuffer,size_t nSize,size_t *lpNumberOfBytesWritten);

    BOOL ReadProcessMemory(
        DWORD  hProcess,
        DWORD  lpBaseAddress,
        LPVOID  lpBuffer,
        SIZE_T  nSize,
        SIZE_T  *lpNumberOfBytesRead
    );
]]

local PROCESS_VM_READ = 0x0010

local export = {}
export.set_buffer_size = function(size)
    export.buffer_raw = nil
    collectgarbage()
    export.buffer_raw = ffi.gc(C.malloc(size), C.free)
    export.buffer_size = size
end
export.set_buffer_size(128)

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

        user.GetWindowThreadProcessId(workwindow(), PID)
        local process = C.OpenProcess(PROCESS_VM_READ,true, PID[0]);

        if process > 0 then
            local result = C.ReadProcessMemory(process, 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.char = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.byte = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("BYTE *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.short = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("short *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.word = function (address, lenght)
    if ReadMemory(address, lenght*2) then
        export.buffer = ffi.cast("WORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.int = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("int *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.dword = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("DWORD *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.float = function (address, lenght)
    if ReadMemory(address, lenght*4) then
        export.buffer = ffi.cast("float *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.double = function (address, lenght)
    if ReadMemory(address, lenght*8) then
        export.buffer = ffi.cast("double *", export.buffer_raw)
        return export.buffer[0]
    end
    return nil
end

export.string = function (address, lenght)
    if ReadMemory(address, lenght) then
        export.buffer = ffi.cast("char *", export.buffer_raw)
        return ffi.string(export.buffer, lenght)
    end
    return nil
end

export.unicode = function (address, lenght)
    if export.string(address, lenght) then
        local wString = ffi.cast("short *", export.buffer_raw)
        local len = ffi.C.WideCharToMultiByte(0, 0, wString, -1, nil, 0, nil, nil)
        local aString = ffi.new("char[?]",len)
        ffi.C.WideCharToMultiByte(0, 0, wString, -1, aString, lenght, nil, nil)
        return ffi.string(aString)
    end
    return nil
end

Пробуй. Там читалось окно пилота =) я тестил то на пилоте им же и память выделял - потому и шуршало. Байпас убрать забыл.

Автор: фонарик 19.2.2024, 16:48

Цитата(DarkMaster @ 19.2.2024, 16:42) *
Пробуй. Там читалось окно пилота =) я тестил то на пилоте им же и память выделял - потому и шуршало. Байпас убрать забыл.
РАААБОТАЕТТ yahoo.gif Большущее Вам спасибо, DarkMaster. Вы настоящий волшебник cool.gif

Только, вот, как бы это объяснить unsure.gif Такое количество строчек в коде на древнем непонятном языке, когда сам по себе скрипт небольшой... Можно как-то ваше заклинание обернуть в какой-нибудь файл и просто его подключать? Чисто чтобы не демотивироваться при изменении и дополнении скрипта biggrin.gif Можете посоветовать как так сделать? Если так можно и оно будет так работать конечно.

Автор: DarkMaster 19.2.2024, 16:50

Это больше спасибо cirus и Cockney, которых я сегодня задолбал с типами данных)

Автор: фонарик 19.2.2024, 17:05

Цитата(DarkMaster @ 19.2.2024, 16:50) *
Это больше спасибо cirus и Cockney, которых я сегодня задолбал с типами данных)
Спасибо всему совету магистров всем всем огромное thanks.gif Может проглядели мой вопрос выше про перемещение заклинания кода с волшебными функциями вне тела самого скрипта? rolleyes.gif

Автор: DarkMaster 19.2.2024, 17:26

естественно просмотрел. добавьте в конце моего скрипта return export
в основном скрипте пишем:
local rmem = require"имя_файла_без_.lua_в_конце"
rmem.unicode(address, lenght) -- все тоже самое

Автор: фонарик 19.2.2024, 17:42

Ну вот, вообще теперь красота laugh.gif Здоровья и мира всем волшебникам добрым людям friendss.gif

Автор: DarkMaster 19.2.2024, 18:52

А теперь немного о быдло коде =)
Меня очень сильно вымораживает два вызова WideCharToMultiByte и два буфера для него.
Вполне очевидно, что астрока будет короче wстроки и если использовать буфер wстроки, то в него поместится астрока. Но функция прямо запрещает использовать один и тот же указатель. Городить из-за этого второй буфер, увеличение при необходимости, функцию ресайза, передавать управление буфером пользователю, чтобы в случае необходимости его грохал, объяснять чем различаются два буфера ну совсем не хочется. Насколько быдлячеством будет использование указателя со смещением -1 от wстроки?(память естественно будет аллоцирована)

Автор: Cockney 19.2.2024, 19:25

Цитата

Но функция прямо запрещает использовать один и тот же указатель


Так какой вопрос то ?)

Тут поинт в том, что два буфыера не должны никак пересекаться, имхо

Автор: DarkMaster 19.2.2024, 19:31

Цитата
Тут поинт в том, что два буфыера не должны никак пересекаться, имхо

Ну вроде как да, по логике... Но проверка идет только на прямое сравнение.
Вопрос по сути о том насколько не нужно делать два буфера с адресацией(условно) 0-127 и 1-127. Или прокатит?) Единично работает. Ворос в том, что будет в дальшейшем и не вылезут ли коллизии.

Автор: Cockney 19.2.2024, 20:02

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

Если речь об ограниченном множестве языков, то наверное так и можно сделать.

Автор: Aqualon 20.2.2024, 2:34

Если вам просто надо спарсить 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 строками, для тех мест где нужна человеческая читаемость я замапил айдишники с вручную выписанными пояснениями в отдельном файлике с константами, это убирает кучу геморроя.

Автор: DarkMaster 22.2.2024, 16:42

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

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

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

Автор: Cockney 22.2.2024, 20:40

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


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

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


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

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


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



В нормальных системах/языках/библиотеках принято оптимизировать за счет всяких пулов строк/буферов, различных кешей. А городить какие то там подбуферы без четкого понимания что куда и как ляжет и как это все сожрет компилятор/интерпретатор - себе дороже, Лучше сделать с реаллокациями но предсказуемым поведением + найти вектор оптимизации другой

Автор: DarkMaster 12.3.2024, 18:44

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

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

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

Автор: Cockney 12.3.2024, 22:21

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

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

Автор: DarkMaster 13.3.2024, 18:12

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

опыт с финдимиджем показал, что 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


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

Автор: Cockney 15.3.2024, 21:20

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

ну ниче умнее flatbuffers/protobuffers и их разновидностей еще не придумали, но там почти нигде нет прямого доступа до данных.

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)