memoryjs
adalah paket NPM untuk membaca dan menulis memori proses!
Fitur • Memulai • Penggunaan • Dokumentasi • Debug
YANG HARUS DILAKUKAN:
Ini adalah add-on Node (terakhir diuji agar berfungsi pada v14.15.0
) dan oleh karena itu memerlukan node-gyp untuk menggunakannya.
Anda mungkin juga perlu mengikuti langkah-langkah berikut untuk menginstal dan menyiapkan node-gyp
.
npm install memoryjs
Saat menggunakan memoryjs, proses target harus sesuai dengan arsitektur platform versi Node yang berjalan. Misalnya jika Anda ingin menargetkan proses 64 bit, Anda harus mencoba dan menggunakan Node.js versi 64 bit.
Anda juga perlu mengkompilasi ulang perpustakaan dan menargetkan platform yang Anda inginkan. Buka direktori modul node memoryjs, buka terminal dan jalankan salah satu skrip kompilasi berikut:
# will automatically compile based on the detected Node architecture
npm run build
# compile to target 32 bit processes
npm run build32
# compile to target 64 bit processes
npm run build64
Jika Anda berencana menggunakan modul ini dengan Node Webkit atau Electron, lihat catatan build Liam Mitchell di sini.
const memoryjs = require('memoryjs');
const processName = "csgo.exe";
// sync: open a process
const processObject = memoryjs.openProcess(processName);
// async: open a process
memoryjs.openProcess(processName, (error, processObject) => {});
// sync: get all processes
const processes = memoryjs.getProcesses();
// async: get all processes
memoryjs.getProcesses((error, processes) => {});
// close a process (release handle)
memoryjs.closeHandle(handle);
Lihat bagian Dokumentasi README ini untuk melihat seperti apa objek proses itu.
// sync: find a module
const moduleObject = memoryjs.findModule(moduleName, processId);
// async: find a module
memoryjs.findModule(moduleName, processId, (error, moduleObject) => {});
// sync: get all modules
const modules = memoryjs.getModules(processId);
// async: get all modules
memoryjs.getModules(processId, (error, modules) => {});
Lihat bagian Dokumentasi README ini untuk melihat seperti apa objek modul itu.
// sync: read data type from memory
const value = memoryjs.readMemory(handle, address, dataType);
// async: read data type from memory
memoryjs.readMemory(handle, address, dataType, (error, value) => {});
// sync: read buffer from memory
const buffer = memoryjs.readBuffer(handle, address, size);
// async: read buffer from memory
memoryjs.readBuffer(handle, address, size, (error, buffer) => {});
// sync: write data type to memory
memoryjs.writeMemory(handle, address, value, dataType);
// sync: write buffer to memory
memoryjs.writeBuffer(handle, address, buffer);
// sync: fetch memory regions
const regions = memoryjs.getRegions(handle);
// async: fetch memory regions
memoryjs.getRegions(handle, (regions) => {});
Lihat bagian Dokumentasi README ini untuk melihat nilai dataType
.
// sync: open a named file mapping object
const fileHandle = memoryjs.openFileMapping(fileName);
// sync: map entire file into a specified process
const baseAddress = memoryjs.mapViewOfFile(processHandle, fileName);
// sync: map portion of a file into a specified process
const baseAddress = memoryjs.mapViewOfFile(processHandle, fileName, offset, viewSize, pageProtection);
// sync: close handle to a file mapping object
const success = memoryjs.closeHandle(fileHandle);
Lihat bagian Dokumentasi README ini untuk melihat detail tentang parameter dan nilai kembalian untuk fungsi-fungsi ini.
// sync: change/set the protection on a region of memory
const oldProtection = memoryjs.virtualProtectEx(handle, address, size, protection);
Lihat bagian Dokumentasi README ini untuk melihat protection
nilai apa saja yang bisa dilakukan.
// sync: pattern scan all modules and memory regions
const address = memoryjs.findPattern(handle, pattern, flags, patternOffset);
// async: pattern scan all modules and memory regions
memoryjs.findPattern(handle, pattern, flags, patternOffset, (error, address) => {});
// sync: pattern scan a given module
const address = memoryjs.findPattern(handle, moduleName, pattern, flags, patternOffset);
// async: pattern scan a given module
memoryjs.findPattern(handle, moduleName, pattern, flags, patternOffset, (error, address) => {});
// sync: pattern scan a memory region or module at the given base address
const address = memoryjs.findPattern(handle, baseAddress, pattern, flags, patternOffset);
// async: pattern scan a memory region or module at the given base address
memoryjs.findPattern(handle, baseAddress, pattern, flags, patternOffset, (error, address) => {});
// sync: execute a function in a remote process
const result = memoryjs.callFunction(handle, args, returnType, address);
// async: execute a function in a remote process
memoryjs.callFunction(handle, args, returnType, address, (error, result) => {});
Klik di sini untuk melihat seperti apa objek hasil.
Klik di sini untuk detail tentang cara memformat argumen dan tipe pengembalian.
// sync: inject a DLL
const success = memoryjs.injectDll(handle, dllPath);
// async: inject a DLL
memoryjs.injectDll(handle, dllPath, (error, success) => {});
// sync: unload a DLL by module base address
const success = memoryjs.unloadDll(handle, moduleBaseAddress);
// async: unload a DLL by module base address
memoryjs.unloadDll(handle, moduleBaseAddress, (error, success) => {});
// sync: unload a DLL by module name
const success = memoryjs.unloadDll(handle, moduleName);
// async: unload a DLL by module name
memoryjs.unloadDll(handle, moduleName, (error, success) => {});
// sync: attach debugger
const success = memoryjs.attachDebugger(processId, exitOnDetach);
// sync: detach debugger
const success = memoryjs.detachDebugger(processId);
// sync: wait for debug event
const success = memoryjs.awaitDebugEvent(hardwareRegister, millisTimeout);
// sync: handle debug event
const success = memoryjs.handleDebugEvent(processId, threadId);
// sync: set hardware breakpoint
const success = memoryjs.setHardwareBreakpoint(processId, address, hardwareRegister, trigger, length);
// sync: remove hardware breakpoint
const success = memoryjs.removeHardwareBreakpoint(processId, hardwareRegister);
Catatan: dokumentasi ini sedang diperbarui, lihat Wiki untuk informasi lebih lanjut.
{ dwSize: 304,
th32ProcessID: 10316,
cntThreads: 47,
th32ParentProcessID: 7804,
pcPriClassBase: 8,
szExeFile: "csgo.exe",
modBaseAddr: 1673789440,
handle: 808 }
Properti handle
dan modBaseAddr
hanya tersedia saat membuka proses dan tidak saat membuat daftar proses.
{ modBaseAddr: 468123648,
modBaseSize: 80302080,
szExePath: 'c:\program files (x86)\steam\steamapps\common\counter-strike global offensive\csgo\bin\client.dll',
szModule: 'client.dll',
th32ProcessID: 10316,
GlblcntUsage: 2 }
{ returnValue: 1.23,
exitCode: 2 }
Objek ini dikembalikan ketika suatu fungsi dijalankan dalam proses jarak jauh:
returnValue
adalah nilai yang dikembalikan dari fungsi yang dipanggilexitCode
adalah status terminasi thread Saat menggunakan fungsi tulis atau baca, parameter tipe data (dataType) harus mereferensikan konstanta dari dalam pustaka:
Konstan | Byte | Alias | Jangkauan |
---|---|---|---|
memoryjs.BOOL | 1 | memoryjs.BOOLEAN | 0 banding 1 |
memoryjs.INT8 | 1 | memoryjs.BYTE , memoryjs.CHAR | -128 hingga 127 |
memoryjs.UINT8 | 1 | memoryjs.UBYTE , memoryjs.UCHAR | 0 hingga 255 |
memoryjs.INT16 | 2 | memoryjs.SHORT | -32.768 hingga 32.767 |
memoryjs.UINT16 | 2 | memoryjs.USHORT , memoryjs.WORD | 0 hingga 65.535 |
memoryjs.INT32 | 4 | memoryjs.INT , memoryjs.LONG | -2.147.483.648 hingga 2.147.483.647 |
memoryjs.UINT32 | 4 | memoryjs.UINT , memoryjs.ULONG , memoryjs.DWORD | 0 hingga 4.294.967.295 |
memoryjs.INT64 | 8 | tidak ada | -9.223.372.036.854.775.808 s/d 9.223.372.036.854.775.807 |
memoryjs.UINT64 | 8 | tidak ada | 0 hingga 18.446.744.073.709.551.615 |
memoryjs.FLOAT | 4 | tidak ada | 3.4E +/- 38 (7 digit) |
memoryjs.DOUBLE | 8 | tidak ada | 1.7E +/- 308 (15 digit) |
memoryjs.PTR | 4/8 | memoryjs.POINTER | tidak ada |
memoryjs.UPTR | 4/8 | memoryjs.UPOINTER | tidak ada |
memoryjs.STR | tidak ada | memoryjs.STRING | tidak ada |
memoryjs.VEC3 | 12 | memoryjs.VECTOR3 | tidak ada |
memoryjs.VEC4 | 16 | memoryjs.VECTOR4 | tidak ada |
Catatan:
_BE
ke tipe data. Misalnya: memoryjs.DOUBLE_BE
.INT64
, UINT64
, INT64_BE
, UINT64_BE
) Anda perlu menyediakan BigInt. Saat membaca bilangan bulat 64 bit, Anda akan menerima BigInt.Tipe data ini digunakan untuk menunjukkan tipe data yang dibaca atau ditulis.
Contoh bilangan bulat 64 bit:
const value = memoryjs.readMemory(handle, address, memoryjs.INT64);
console.log(typeof value); // bigint
memoryjs.writeMemory(handle, address, value + 1n, memoryjs.INT64);
Vector3 adalah struktur data dari tiga float:
const vector3 = { x: 0.0, y: 0.0, z: 0.0 };
memoryjs.writeMemory(handle, address, vector3, memoryjs.VEC3);
Vector4 adalah struktur data dari empat float:
const vector4 = { w: 0.0, x: 0.0, y: 0.0, z: 0.0 };
memoryjs.writeMemory(handle, address, vector4, memoryjs.VEC4);
Jika Anda memiliki struktur yang ingin Anda tulis ke memori, Anda dapat menggunakan buffer. Untuk contoh bagaimana melakukan hal ini, lihat contoh buffer.
Untuk menulis/membaca struktur ke/dari memori, Anda dapat menggunakan structron untuk mendefinisikan struktur Anda dan menggunakannya untuk menulis atau mengurai buffer.
Jika Anda ingin membaca std::string
menggunakan structron
, perpustakaan memperlihatkan tipe khusus yang dapat digunakan untuk membaca/menulis string:
// To create the type, we need to pass the process handle, base address of the
// structure, and the target process architecture (either "32" or "64").
const stringType = memoryjs.STRUCTRON_TYPE_STRING(processObject.handle, structAddress, '64');
// Create a custom structure using the custom type, full example in /examples/buffers.js
const Struct = require('structron');
const Player = new Struct()
.addMember(string, 'name');
Alternatifnya, Anda dapat menggunakan perpustakaan konsentrat dan pembubaran untuk mencapai hal yang sama. Contoh lamanya ada di sini.
Jenis perlindungan adalah nilai DWORD tanda bit.
Parameter ini harus merujuk pada konstanta dari perpustakaan:
memoryjs.PAGE_NOACCESS, memoryjs.PAGE_READONLY, memoryjs.PAGE_READWRITE, memoryjs.PAGE_WRITECOPY, memoryjs.PAGE_EXECUTE, memoryjs.PAGE_EXECUTE_READ, memoryjs.PAGE_EXECUTE_READWRITE, memoryjs.PAGE_EXECUTE_WRITECOPY, memoryjs.PAGE_GUARD, memoryjs.PAGE_NOCACHE, memoryjs.PAGE_WRITECOMBINE, memoryjs.PAGE_ENCLAVE_THREAD_CONTROL, memoryjs.PAGE_TARGETS_NO_UPDATE, memoryjs.PAGE_TARGETS_INVALID, memoryjs.PAGE_ENCLAVE_UNVALIDATED
Lihat Konstanta Perlindungan Memori MSDN untuk informasi lebih lanjut.
Jenis alokasi memori adalah nilai DWORD bit flag.
Parameter ini harus mereferensikan konstanta dari perpustakaan:
memoryjs.MEM_COMMIT, memoryjs.MEM_RESERVE, memoryjs.MEM_RESET, memoryjs.MEM_RESET_UNDO
Lihat dokumentasi VirtualAllocEx MSDN untuk informasi lebih lanjut.
Anda dapat menggunakan perpustakaan ini untuk membaca "string", atau "char*" dan menulis string.
Dalam kedua kasus tersebut Anda ingin mendapatkan alamat array char:
std::string str1 = "hello";
std::cout << "Address: 0x" << hex << (DWORD) str1.c_str() << dec << std::endl;
char* str2 = "hello";
std::cout << "Address: 0x" << hex << (DWORD) str2 << dec << std::endl;
Dari sini Anda cukup menggunakan alamat ini untuk menulis dan membaca memori.
Ada satu peringatan saat membaca string di memori, namun karena perpustakaan tidak mengetahui berapa panjang string tersebut, perpustakaan akan terus membaca hingga menemukan terminator nol pertama. Untuk mencegah loop tak terbatas, ia akan berhenti membaca jika belum menemukan terminator nol setelah 1 juta karakter.
Salah satu cara untuk melewati batasan ini di masa depan adalah dengan mengizinkan parameter yang memungkinkan pengguna mengatur jumlah karakter maksimum.
Saat memindai pola, bendera perlu dikibarkan untuk tipe tanda tangan. Parameter jenis tanda tangan harus berupa salah satu dari berikut ini:
0x0
atau memoryjs.NORMAL
yang menunjukkan tanda tangan normal.
0x1
atau memoryjs.READ
yang akan membaca memori di alamat tersebut.
0x2
atau memoryjs.SUBSTRACT
yang akan mengurangi basis gambar dari alamat.
Untuk menaikkan banyak tanda, gunakan operator bitwise OR: memoryjs.READ | memoryjs.SUBTRACT
.
Pustaka memperlihatkan fungsi untuk memetakan, memperoleh pegangan, dan membaca file yang dipetakan memori.
openFileMapping(Namafile)
Lihat dokumentasi OpenFileMappingA MSDN untuk informasi lebih lanjut.
mapViewOfFile(prosesHandle, Nama file)
memoryjs.openFileMapping
constants.PAGE_READONLY
.mapViewOfFile(processHandle, nama file, offset, ukuran tampilan, perlindungan halaman)
memoryjs.openFileMapping
number
atau bigint
): offset dari awal file (harus kelipatan 64KB)number
atau bigint
): jumlah byte yang akan dipetakan (jika 0
, seluruh file akan dibaca, terlepas dari offsetnya)Lihat dokumentasi MapViewOfFile2 MSDN untuk informasi lebih lanjut.
Lihat Jenis Perlindungan untuk jenis perlindungan halaman.
Kami memiliki proses yang membuat pemetaan file:
HANDLE fileHandle = CreateFileA("C:\foo.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE fileMappingHandle = CreateFileMappingA(fileHandle, NULL, PAGE_READONLY, 0, 0, "MappedFooFile");
Kita dapat memetakan file ke proses target tertentu dan membaca file dengan memoryjs
:
const processObject = memoryjs.openProcess("example.exe");
const fileHandle = memoryjs.openFileMapping("MappedFooFile");
// read entire file
const baseAddress = memoryjs.mapViewOfFile(processObject.handle, fileHandle.handle);
const data = memoryjs.readMemory(processObject.handle, baseAddress, memoryjs.STR);
// read 10 bytes after 64KB
const baseAddress = memoryjs.mapViewOfFile(processObject.handle, fileHandle.handle, 65536, 10, constants.PAGE_READONLY);
const buffer = memoryjs.readBuffer(processObject.handle, baseAddress, 10);
const data = buffer.toString();
const success = memoryjs.closeHandle(fileHandle);
Jika Anda ingin membaca file yang dipetakan memori tanpa memiliki proses target untuk memetakan file tersebut, Anda dapat memetakannya ke proses Node saat ini dengan variabel global process.pid
:
const processObject = memoryjs.openProcess(process.pid);
Eksekusi fungsi jarak jauh bekerja dengan membangun serangkaian argumen dan secara dinamis menghasilkan kode shell yang dimasukkan ke dalam proses target dan dieksekusi, oleh karena itu crash dapat terjadi.
Untuk memanggil suatu fungsi dalam suatu proses, fungsi callFunction
dapat digunakan. Pustaka mendukung penyampaian argumen ke fungsi dan harus dalam format berikut:
[{ type: T_INT, value: 4 }]
Pustaka mengharapkan argumen berupa array objek yang setiap objeknya memiliki type
yang menunjukkan tipe data argumen, dan value
yang merupakan nilai sebenarnya dari argumen tersebut. Berbagai tipe data yang didukung dapat ditemukan di bawah.
memoryjs.T_VOID = 0x0,
memoryjs.T_STRING = 0x1,
memoryjs.T_CHAR = 0x2,
memoryjs.T_BOOL = 0x3,
memoryjs.T_INT = 0x4,
memoryjs.T_DOUBLE = 0x5,
memoryjs.T_FLOAT = 0x6,
Saat menggunakan callFunction
, Anda juga perlu menyediakan tipe pengembalian fungsi, yang sekali lagi harus berupa salah satu nilai di atas.
Misalnya, diberikan fungsi C++ berikut:
int add(int a, int b) {
return a + b;
}
Anda akan memanggil fungsi ini seperti ini:
const args = [{
type: memoryjs.T_INT,
value: 2,
}, {
type: memoryjs.T_INT,
value: 5,
}];
const returnType = T_INT;
> memoryjs.callFunction(handle, args, returnType, address);
{ returnValue: 7, exitCode: 7 }
Lihat dokumentasi objek hasil untuk detail tentang apa yang callFunction
kembalikan.
Catatan: saat ini meneruskan double
sebagai argumen tidak didukung, tetapi mengembalikan satu argumen didukung.
Terima kasih banyak kepada berbagai kontributor yang memungkinkan fitur ini terwujud.
Breakpoint perangkat keras bekerja dengan melampirkan debugger ke proses, menetapkan breakpoint pada alamat tertentu dan mendeklarasikan jenis pemicu (misalnya breakpoint saat menulis ke alamat) dan kemudian terus menunggu hingga peristiwa debug muncul (dan kemudian menanganinya).
Pustaka ini memaparkan fungsi-fungsi utama, tetapi juga menyertakan kelas pembungkus untuk menyederhanakan prosesnya. Untuk contoh kode lengkap, periksa contoh debugging kami.
Saat menyetel breakpoint, Anda diharuskan meneruskan jenis pemicu:
memoryjs.TRIGGER_ACCESS
- breakpoint terjadi ketika alamat diaksesmemoryjs.TRIGGER_WRITE
- breakpoint terjadi ketika alamat ditulis Perhatikan bahwa saat memantau alamat yang berisi string, parameter size
fungsi setHardwareBreakpoint
harus berupa panjang string. Saat menggunakan kelas pembungkus Debugger
, pembungkus akan secara otomatis menentukan ukuran string dengan mencoba membacanya.
Untuk meringkas:
Saat menggunakan kelas Debugger
:
size
ke setHardwareBreakpoint
setHardwareBreakpoint
mengembalikan register yang digunakan untuk breakpointSaat menggunakan fungsi debugger secara manual:
size
adalah ukuran variabel di memori (misal int32 = 4 byte). Untuk sebuah string, parameter ini adalah panjang stringmemoryjs.DR0
hingga memoryhs.DR3
). Hanya 4 register perangkat keras yang tersedia (beberapa CPU bahkan mungkin memiliki kurang dari 4 yang tersedia). Ini berarti hanya 4 breakpoint yang dapat diatur pada waktu tertentusetHardwareBreakpoint
mengembalikan boolean yang menyatakan apakah operasi berhasilUntuk membaca lebih lanjut tentang debugging dan breakpoint perangkat keras, periksa tautan berikut:
Pembungkus Debugger berisi fungsi-fungsi berikut yang harus Anda gunakan:
class Debugger {
attach(processId, killOnDetach = false);
detach(processId);
setHardwareBreakpoint(processId, address, trigger, dataType);
removeHardwareBreakpoint(processId, register);
}
const hardwareDebugger = memoryjs.Debugger;
hardwareDebugger.attach(processId);
const address = 0xDEADBEEF;
const trigger = memoryjs.TRIGGER_ACCESS;
const dataType = memoryjs.INT;
const register = hardwareDebugger.setHardwareBreakpoint(processId, address, trigger, dataType);
// `debugEvent` event emission catches debug events from all registers
hardwareDebugger.on('debugEvent', ({ register, event }) => {
console.log(`Hardware Register ${register} breakpoint`);
console.log(event);
});
// You can listen to debug events from specific hardware registers
// by listening to whatever register was returned from `setHardwareBreakpoint`
hardwareDebugger.on(register, (event) => {
console.log(event);
});
const hardwareDebugger = memoryjs.Debugger;
hardwareDebugger.attach(processId);
// available registers: DR0 through DR3
const register = memoryjs.DR0;
// int = 4 bytes
const size = 4;
const address = 0xDEADBEEF;
const trigger = memoryjs.TRIGGER_ACCESS;
const dataType = memoryjs.INT;
const success = memoryjs.setHardwareBreakpoint(processId, address, register, trigger, size);
const timeout = 100;
setInterval(() => {
// `debugEvent` can be null if no event occurred
const debugEvent = memoryjs.awaitDebugEvent(register, timeout);
// If a breakpoint occurred, handle it
if (debugEvent) {
memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId);
}
}, timeout);
Catatan: loop tidak diperlukan, misalnya tidak diperlukan loop jika Anda hanya ingin menunggu hingga deteksi pertama dari alamat yang sedang diakses atau ditulis.
Buka direktori root modul dan jalankan salah satu perintah berikut:
# will automatically compile based on the detected Node architecture
npm run debug
# compile to target 32 bit processes
npm run debug32
# compile to target 64 bit processes
npm run debug64
index.js
agar memerlukan modul debug Buka direktori root dan ubah baris di index.js
dari:
const memoryjs = require('./build/Release/memoryjs');
Sebagai berikut:
const memoryjs = require('./build/Debug/memoryjs');
Buka solusi binding.sln
di Visual Studio, yang terdapat di folder build
di direktori root proyek.
node.exe
Anda (misalnya C:nodejsnode.exe
)C:projecttest.js
) Jelajahi file proyek di Visual Studio (dengan memperluas ..
lalu lib
di Solution Explorer). File header dapat dilihat dengan menahan Alt
dan mengklik nama file header di bagian atas file kode sumber.
Breakpoint diatur dengan mengklik di sebelah kiri nomor baris.
Mulai debugging dengan menekan F5
, dengan mengklik "Debug" di toolbar dan kemudian "Mulai Debugging", atau dengan mengklik "Debug Windows Lokal".
Skrip yang Anda tetapkan sebagai argumen perintah pada langkah 4 akan dijalankan, dan Visual Studio akan berhenti sejenak pada set breakpoint dan memungkinkan Anda menelusuri kode baris demi baris dan memeriksa variabel.