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