Здравствуйте. Не знаю правильно ли я думаю, но пилот не умеет понимать строку в юникоде. Из интернетов были добыты эльфийские инструменты плагины, которые (если судить по светящимся надписям на них) как раз созданы магами-чародеями программистами для таких задач. Но тут вспоминается басня Крылова "Мартышка и очки". На рабочем столе уже не осталось места для размещения очередного артефакта. И вот в печалях, в довлеющем ощущении тяжести и бренности бытия, спешу я снова к вам в лавку алхимиков на форум за советами и помощью.
Вот, например, у одного человека, создаваемый велосипед заканчивается чтением памяти:
Name = readmem ("0x07CCFFD8", "S", 16)
1D 04 30 04 31 04 3E 04 40 04
Задача именно работать с юникодом или просто считать его в читаемом виде?(конвертнуть)
--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
str = export.unicode(0x07CCFFD8, 6)
никаких кавычек. значения переданы верно. рабочее окно установлено?
считай как string. есть зябрики?
А еще в логе перед строкой с nil есть строка с сообщением Process not opened
Но строка там есть по тому адресу. На скрине то, что читает пилот. Хотя там должно быть слово "Набор".
--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
16:37:50 70 (, 0): PID[0]: 8652
16:37:50 70 (, 0): Process not opened
16:37:50 70 (, 0): nil
--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
Это больше спасибо cirus и Cockney, которых я сегодня задолбал с типами данных)
естественно просмотрел. добавьте в конце моего скрипта return export
в основном скрипте пишем:
local rmem = require"имя_файла_без_.lua_в_конце"
rmem.unicode(address, lenght) -- все тоже самое
Ну вот, вообще теперь красота Здоровья и мира всем волшебникам добрым людям
А теперь немного о быдло коде =)
Меня очень сильно вымораживает два вызова WideCharToMultiByte и два буфера для него.
Вполне очевидно, что астрока будет короче wстроки и если использовать буфер wстроки, то в него поместится астрока. Но функция прямо запрещает использовать один и тот же указатель. Городить из-за этого второй буфер, увеличение при необходимости, функцию ресайза, передавать управление буфером пользователю, чтобы в случае необходимости его грохал, объяснять чем различаются два буфера ну совсем не хочется. Насколько быдлячеством будет использование указателя со смещением -1 от wстроки?(память естественно будет аллоцирована)
В общем случае думаю что не прокатит, т.к. там не ясно как читается буффер + один код поинт юникода может быть разложен на условные 3-4 байта, что повредит следующий символ в юникод буффере.
Если речь об ограниченном множестве языков, то наверное так и можно сделать.
Если вам просто надо спарсить 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)
тут лучше на примерах, задача то какая ? пока я только понял что ты просто не хочешь выделять несколько раз память и используешь один буффер под разные данные.
чем копировать - не знаю, вряд ли есть что-то быстрее memcpy в терминах си. наверняка у луа есть такой аналог
--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
короче как я понял ты костылишь сериализатор/десериализатор да и еще zero-copy.
ну ниче умнее flatbuffers/protobuffers и их разновидностей еще не придумали, но там почти нигде нет прямого доступа до данных.
Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)