mirror of
https://github.com/themitosan/fpPS4-Temmie-s-Launcher.git
synced 2025-04-02 10:31:50 -04:00
1515 lines
No EOL
58 KiB
C++
1515 lines
No EOL
58 KiB
C++
#include <windows.h>
|
|
#include <psapi.h>
|
|
#include <napi.h>
|
|
#include <string>
|
|
#include "module.h"
|
|
#include "process.h"
|
|
#include "memoryjs.h"
|
|
#include "memory.h"
|
|
#include "pattern.h"
|
|
#include "functions.h"
|
|
#include "dll.h"
|
|
#include "debugger.h"
|
|
|
|
#pragma comment(lib, "psapi.lib")
|
|
|
|
|
|
process Process;
|
|
// module Module;
|
|
memory Memory;
|
|
pattern Pattern;
|
|
// functions Functions;
|
|
|
|
struct Vector3 {
|
|
float x, y, z;
|
|
};
|
|
|
|
struct Vector4 {
|
|
float w, x, y, z;
|
|
};
|
|
|
|
Napi::Value openProcess(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 1 && args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 1 argument, or 2 arguments if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsString() && !args[0].IsNumber()) {
|
|
Napi::Error::New(env, "first argument must be a string or a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 2 && !args[1].IsFunction()) {
|
|
Napi::Error::New(env, "second argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Define error message that may be set by the function that opens the process
|
|
char* errorMessage = "";
|
|
|
|
process::Pair pair;
|
|
|
|
if (args[0].IsString()) {
|
|
std::string processName(args[0].As<Napi::String>().Utf8Value());
|
|
pair = Process.openProcess(processName.c_str(), &errorMessage);
|
|
|
|
// In case it failed to open, let's keep retrying
|
|
// while(!strcmp(process.szExeFile, "")) {
|
|
// process = Process.openProcess((char*) *(processName), &errorMessage);
|
|
// };
|
|
}
|
|
|
|
if (args[0].IsNumber()) {
|
|
pair = Process.openProcess(args[0].As<Napi::Number>().Uint32Value(), &errorMessage);
|
|
|
|
// In case it failed to open, let's keep retrying
|
|
// while(!strcmp(process.szExeFile, "")) {
|
|
// process = Process.openProcess(info[0].As<Napi::Number>().Uint32Value(), &errorMessage);
|
|
// };
|
|
}
|
|
|
|
// If an error message was returned from the function that opens the process, throw the error.
|
|
// Only throw an error if there is no callback (if there's a callback, the error is passed there).
|
|
if (strcmp(errorMessage, "") && args.Length() != 2) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Create a v8 Object (JSON) to store the process information
|
|
Napi::Object processInfo = Napi::Object::New(env);
|
|
|
|
processInfo.Set(Napi::String::New(env, "dwSize"), Napi::Value::From(env, (int)pair.process.dwSize));
|
|
processInfo.Set(Napi::String::New(env, "th32ProcessID"), Napi::Value::From(env, (int)pair.process.th32ProcessID));
|
|
processInfo.Set(Napi::String::New(env, "cntThreads"), Napi::Value::From(env, (int)pair.process.cntThreads));
|
|
processInfo.Set(Napi::String::New(env, "th32ParentProcessID"), Napi::Value::From(env, (int)pair.process.th32ParentProcessID));
|
|
processInfo.Set(Napi::String::New(env, "pcPriClassBase"), Napi::Value::From(env, (int)pair.process.pcPriClassBase));
|
|
processInfo.Set(Napi::String::New(env, "szExeFile"), Napi::String::New(env, pair.process.szExeFile));
|
|
processInfo.Set(Napi::String::New(env, "handle"), Napi::Value::From(env, (uintptr_t)pair.handle));
|
|
|
|
DWORD64 base = module::getBaseAddress(pair.process.szExeFile, pair.process.th32ProcessID);
|
|
processInfo.Set(Napi::String::New(env, "modBaseAddr"), Napi::Value::From(env, (uintptr_t)base));
|
|
|
|
// openProcess can either take one argument or can take
|
|
// two arguments for asychronous use (second argument is the callback)
|
|
if (args.Length() == 2) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[1].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), processInfo });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return processInfo;
|
|
}
|
|
}
|
|
|
|
Napi::Value closeProcess(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 1) {
|
|
Napi::Error::New(env, "requires 1 argument").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber()) {
|
|
Napi::Error::New(env, "first argument must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
Process.closeProcess((HANDLE)args[0].As<Napi::Number>().Int64Value());
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Value getProcesses(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() > 1) {
|
|
Napi::Error::New(env, "requires either 0 arguments or 1 argument if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 1 && !args[0].IsFunction()) {
|
|
Napi::Error::New(env, "first argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Define error message that may be set by the function that gets the processes
|
|
char* errorMessage = "";
|
|
|
|
std::vector<PROCESSENTRY32> processEntries = Process.getProcesses(&errorMessage);
|
|
|
|
// If an error message was returned from the function that gets the processes, throw the error.
|
|
// Only throw an error if there is no callback (if there's a callback, the error is passed there).
|
|
if (strcmp(errorMessage, "") && args.Length() != 1) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Creates v8 array with the size being that of the processEntries vector processes is an array of JavaScript objects
|
|
Napi::Array processes = Napi::Array::New(env, processEntries.size());
|
|
|
|
// Loop over all processes found
|
|
for (std::vector<PROCESSENTRY32>::size_type i = 0; i != processEntries.size(); i++) {
|
|
// Create a v8 object to store the current process' information
|
|
Napi::Object process = Napi::Object::New(env);
|
|
|
|
process.Set(Napi::String::New(env, "cntThreads"), Napi::Value::From(env, (int)processEntries[i].cntThreads));
|
|
process.Set(Napi::String::New(env, "szExeFile"), Napi::String::New(env, processEntries[i].szExeFile));
|
|
process.Set(Napi::String::New(env, "th32ProcessID"), Napi::Value::From(env, (int)processEntries[i].th32ProcessID));
|
|
process.Set(Napi::String::New(env, "th32ParentProcessID"), Napi::Value::From(env, (int)processEntries[i].th32ParentProcessID));
|
|
process.Set(Napi::String::New(env, "pcPriClassBase"), Napi::Value::From(env, (int)processEntries[i].pcPriClassBase));
|
|
|
|
// Push the object to the array
|
|
processes.Set(i, process);
|
|
}
|
|
|
|
/* getProcesses can either take no arguments or one argument
|
|
one argument is for asychronous use (the callback) */
|
|
if (args.Length() == 1) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[0].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), processes });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return processes;
|
|
}
|
|
}
|
|
|
|
Napi::Value getModules(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 1 && args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 1 argument, or 2 arguments if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber()) {
|
|
Napi::Error::New(env, "first argument must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 2 && !args[1].IsFunction()) {
|
|
Napi::Error::New(env, "first argument must be a number, second argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Define error message that may be set by the function that gets the modules
|
|
char* errorMessage = "";
|
|
|
|
std::vector<MODULEENTRY32> moduleEntries = module::getModules(args[0].As<Napi::Number>().Int32Value(), &errorMessage);
|
|
|
|
// If an error message was returned from the function getting the modules, throw the error.
|
|
// Only throw an error if there is no callback (if there's a callback, the error is passed there).
|
|
if (strcmp(errorMessage, "") && args.Length() != 2) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// Creates v8 array with the size being that of the moduleEntries vector
|
|
// modules is an array of JavaScript objects
|
|
Napi::Array modules = Napi::Array::New(env, moduleEntries.size());
|
|
|
|
// Loop over all modules found
|
|
for (std::vector<MODULEENTRY32>::size_type i = 0; i != moduleEntries.size(); i++) {
|
|
// Create a v8 object to store the current module's information
|
|
Napi::Object module = Napi::Object::New(env);
|
|
|
|
module.Set(Napi::String::New(env, "modBaseAddr"), Napi::Value::From(env, (uintptr_t)moduleEntries[i].modBaseAddr));
|
|
module.Set(Napi::String::New(env, "modBaseSize"), Napi::Value::From(env, (int)moduleEntries[i].modBaseSize));
|
|
module.Set(Napi::String::New(env, "szExePath"), Napi::String::New(env, moduleEntries[i].szExePath));
|
|
module.Set(Napi::String::New(env, "szModule"), Napi::String::New(env, moduleEntries[i].szModule));
|
|
module.Set(Napi::String::New(env, "th32ProcessID"), Napi::Value::From(env, (int)moduleEntries[i].th32ProcessID));
|
|
module.Set(Napi::String::New(env, "GlblcntUsage"), Napi::Value::From(env, (int)moduleEntries[i].GlblcntUsage));
|
|
|
|
// Push the object to the array
|
|
modules.Set(i, module);
|
|
}
|
|
|
|
// getModules can either take one argument or two arguments
|
|
// one/two arguments is for asychronous use (the callback)
|
|
if (args.Length() == 2) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[1].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), modules });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return modules;
|
|
}
|
|
}
|
|
|
|
Napi::Value findModule(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 1 && args.Length() != 2 && args.Length() != 3) {
|
|
Napi::Error::New(env, "requires 1 argument, 2 arguments, or 3 arguments if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsString() && !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "first argument must be a string, second argument must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 3 && !args[2].IsFunction()) {
|
|
Napi::Error::New(env, "third argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
std::string moduleName(args[0].As<Napi::String>().Utf8Value());
|
|
|
|
// Define error message that may be set by the function that gets the modules
|
|
char* errorMessage = "";
|
|
|
|
MODULEENTRY32 module = module::findModule(moduleName.c_str(), args[1].As<Napi::Number>().Int32Value(), &errorMessage);
|
|
|
|
// If an error message was returned from the function getting the module, throw the error.
|
|
// Only throw an error if there is no callback (if there's a callback, the error is passed there).
|
|
if (strcmp(errorMessage, "") && args.Length() != 3) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// In case it failed to open, let's keep retrying
|
|
while (!strcmp(module.szExePath, "")) {
|
|
module = module::findModule(moduleName.c_str(), args[1].As<Napi::Number>().Int32Value(), &errorMessage);
|
|
};
|
|
|
|
// Create a v8 Object (JSON) to store the process information
|
|
Napi::Object moduleInfo = Napi::Object::New(env);
|
|
|
|
moduleInfo.Set(Napi::String::New(env, "modBaseAddr"), Napi::Value::From(env, (uintptr_t)module.modBaseAddr));
|
|
moduleInfo.Set(Napi::String::New(env, "modBaseSize"), Napi::Value::From(env, (int)module.modBaseSize));
|
|
moduleInfo.Set(Napi::String::New(env, "szExePath"), Napi::String::New(env, module.szExePath));
|
|
moduleInfo.Set(Napi::String::New(env, "szModule"), Napi::String::New(env, module.szModule));
|
|
moduleInfo.Set(Napi::String::New(env, "th32ProcessID"), Napi::Value::From(env, (int)module.th32ProcessID));
|
|
moduleInfo.Set(Napi::String::New(env, "GlblcntUsage"), Napi::Value::From(env, (int)module.GlblcntUsage));
|
|
|
|
// findModule can either take one or two arguments,
|
|
// three arguments for asychronous use (third argument is the callback)
|
|
if (args.Length() == 3) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[2].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), moduleInfo });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return moduleInfo;
|
|
}
|
|
}
|
|
|
|
Napi::Value readMemory(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 3 && args.Length() != 4) {
|
|
Napi::Error::New(env, "requires 3 arguments, or 4 arguments if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsNumber() && !args[2].IsString()) {
|
|
Napi::Error::New(env, "first and second argument must be a number, third argument must be a string").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 4 && !args[3].IsFunction()) {
|
|
Napi::Error::New(env, "fourth argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
std::string dataTypeArg(args[2].As<Napi::String>().Utf8Value());
|
|
const char* dataType = dataTypeArg.c_str();
|
|
|
|
// Define the error message that will be set if no data type is recognised
|
|
char* errorMessage = "";
|
|
Napi::Value retVal = env.Null();
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
|
|
if (!strcmp(dataType, "int8") || !strcmp(dataType, "byte") || !strcmp(dataType, "char")) {
|
|
|
|
int8_t result = Memory.readMemory<int8_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "uint8") || !strcmp(dataType, "ubyte") || !strcmp(dataType, "uchar")) {
|
|
|
|
uint8_t result = Memory.readMemory<uint8_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "int16") || !strcmp(dataType, "short")) {
|
|
|
|
int16_t result = Memory.readMemory<int16_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "uint16") || !strcmp(dataType, "ushort") || !strcmp(dataType, "word")) {
|
|
|
|
uint16_t result = Memory.readMemory<uint16_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "int32") || !strcmp(dataType, "int") || !strcmp(dataType, "long")) {
|
|
|
|
int32_t result = Memory.readMemory<int32_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "uint32") || !strcmp(dataType, "uint") || !strcmp(dataType, "ulong") || !strcmp(dataType, "dword")) {
|
|
|
|
uint32_t result = Memory.readMemory<uint32_t>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "int64")) {
|
|
|
|
int64_t result = Memory.readMemory<int64_t>(handle, address);
|
|
retVal = Napi::Value::From(env, Napi::BigInt::New(env, result));
|
|
|
|
} else if (!strcmp(dataType, "uint64")) {
|
|
|
|
uint64_t result = Memory.readMemory<uint64_t>(handle, address);
|
|
retVal = Napi::Value::From(env, Napi::BigInt::New(env, result));
|
|
|
|
} else if (!strcmp(dataType, "float")) {
|
|
|
|
float result = Memory.readMemory<float>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "double")) {
|
|
|
|
double result = Memory.readMemory<double>(handle, address);
|
|
retVal = Napi::Value::From(env, result);
|
|
|
|
} else if (!strcmp(dataType, "ptr") || !strcmp(dataType, "pointer")) {
|
|
|
|
intptr_t result = Memory.readMemory<intptr_t>(handle, address);
|
|
|
|
if (sizeof(intptr_t) == 8) {
|
|
retVal = Napi::Value::From(env, Napi::BigInt::New(env, (int64_t) result));
|
|
} else {
|
|
retVal = Napi::Value::From(env, result);
|
|
}
|
|
|
|
} else if (!strcmp(dataType, "uptr") || !strcmp(dataType, "upointer")) {
|
|
|
|
uintptr_t result = Memory.readMemory<uintptr_t>(handle, address);
|
|
|
|
if (sizeof(uintptr_t) == 8) {
|
|
retVal = Napi::Value::From(env, Napi::BigInt::New(env, (uint64_t) result));
|
|
} else {
|
|
retVal = Napi::Value::From(env, result);
|
|
}
|
|
|
|
} else if (!strcmp(dataType, "bool") || !strcmp(dataType, "boolean")) {
|
|
|
|
bool result = Memory.readMemory<bool>(handle, address);
|
|
retVal = Napi::Boolean::New(env, result);
|
|
|
|
} else if (!strcmp(dataType, "string") || !strcmp(dataType, "str")) {
|
|
|
|
std::string str;
|
|
if (!Memory.readString(handle, address, &str)) {
|
|
errorMessage = "unable to read string";
|
|
} else {
|
|
retVal = Napi::String::New(env, str);
|
|
}
|
|
|
|
} else if (!strcmp(dataType, "vector3") || !strcmp(dataType, "vec3")) {
|
|
|
|
Vector3 result = Memory.readMemory<Vector3>(handle, address);
|
|
Napi::Object moduleInfo = Napi::Object::New(env);
|
|
moduleInfo.Set(Napi::String::New(env, "x"), Napi::Value::From(env, result.x));
|
|
moduleInfo.Set(Napi::String::New(env, "y"), Napi::Value::From(env, result.y));
|
|
moduleInfo.Set(Napi::String::New(env, "z"), Napi::Value::From(env, result.z));
|
|
retVal = moduleInfo;
|
|
|
|
} else if (!strcmp(dataType, "vector4") || !strcmp(dataType, "vec4")) {
|
|
|
|
Vector4 result = Memory.readMemory<Vector4>(handle, address);
|
|
Napi::Object moduleInfo = Napi::Object::New(env);
|
|
moduleInfo.Set(Napi::String::New(env, "w"), Napi::Value::From(env, result.w));
|
|
moduleInfo.Set(Napi::String::New(env, "x"), Napi::Value::From(env, result.x));
|
|
moduleInfo.Set(Napi::String::New(env, "y"), Napi::Value::From(env, result.y));
|
|
moduleInfo.Set(Napi::String::New(env, "z"), Napi::Value::From(env, result.z));
|
|
retVal = moduleInfo;
|
|
|
|
} else {
|
|
errorMessage = "unexpected data type";
|
|
}
|
|
|
|
if (strcmp(errorMessage, "") && args.Length() != 4) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 4) {
|
|
Napi::Function callback = args[3].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), retVal });
|
|
return env.Null();
|
|
} else {
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
Napi::Value readBuffer(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 3 && args.Length() != 4) {
|
|
Napi::Error::New(env, "requires 3 arguments, or 4 arguments if a callback is being used").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsNumber() && !args[2].IsNumber()) {
|
|
Napi::Error::New(env, "first, second and third arguments must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 4 && !args[3].IsFunction()) {
|
|
Napi::Error::New(env, "fourth argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
SIZE_T size = args[2].As<Napi::Number>().Int64Value();
|
|
|
|
// To fix the memory leak problem that was happening here, we need to release the
|
|
// temporary buffer we create after we're done creating a Napi::Buffer from it.
|
|
// Napi::Buffer::New doesn't free the memory, so it has be done manually
|
|
// but it can segfault when the memory is freed before being accessed.
|
|
// The solution is to use Napi::Buffer::Copy, and then we can manually free it.
|
|
//
|
|
// see: https://github.com/nodejs/node/issues/40936
|
|
// see: https://sagivo.com/2015/09/30/Go-Native-Calling-C-From-NodeJS.html
|
|
char* data = (char*) malloc(sizeof(char) * size);
|
|
Memory.readBuffer(handle, address, size, data);
|
|
|
|
Napi::Buffer<char> buffer = Napi::Buffer<char>::Copy(env, data, size);
|
|
free(data);
|
|
|
|
if (args.Length() == 4) {
|
|
Napi::Function callback = args[3].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, ""), buffer });
|
|
return env.Null();
|
|
} else {
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
Napi::Value writeMemory(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 4) {
|
|
Napi::Error::New(env, "requires 4 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsNumber() && !args[3].IsString()) {
|
|
Napi::Error::New(env, "first and second argument must be a number, third argument must be a string").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
std::string dataTypeArg(args[3].As<Napi::String>().Utf8Value());
|
|
const char* dataType = dataTypeArg.c_str();
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
|
|
if (!strcmp(dataType, "int8") || !strcmp(dataType, "byte") || !strcmp(dataType, "char")) {
|
|
|
|
Memory.writeMemory<int8_t>(handle, address, args[2].As<Napi::Number>().Int32Value());
|
|
|
|
} else if (!strcmp(dataType, "uint8") || !strcmp(dataType, "ubyte") || !strcmp(dataType, "uchar")) {
|
|
|
|
Memory.writeMemory<uint8_t>(handle, address, args[2].As<Napi::Number>().Uint32Value());
|
|
|
|
} else if (!strcmp(dataType, "int16") || !strcmp(dataType, "short")) {
|
|
|
|
Memory.writeMemory<int16_t>(handle, address, args[2].As<Napi::Number>().Int32Value());
|
|
|
|
} else if (!strcmp(dataType, "uint16") || !strcmp(dataType, "ushort") || !strcmp(dataType, "word")) {
|
|
|
|
Memory.writeMemory<uint16_t>(handle, address, args[2].As<Napi::Number>().Uint32Value());
|
|
|
|
} else if (!strcmp(dataType, "int32") || !strcmp(dataType, "int") || !strcmp(dataType, "long")) {
|
|
|
|
Memory.writeMemory<int32_t>(handle, address, args[2].As<Napi::Number>().Int32Value());
|
|
|
|
} else if (!strcmp(dataType, "uint32") || !strcmp(dataType, "uint") || !strcmp(dataType, "ulong") || !strcmp(dataType, "dword")) {
|
|
|
|
Memory.writeMemory<uint32_t>(handle, address, args[2].As<Napi::Number>().Uint32Value());
|
|
|
|
} else if (!strcmp(dataType, "int64")) {
|
|
|
|
Napi::BigInt bigInt = args[2].As<Napi::BigInt>();
|
|
bool lossless;
|
|
Memory.writeMemory<int64_t>(handle, address, bigInt.Int64Value(&lossless));
|
|
|
|
} else if (!strcmp(dataType, "uint64")) {
|
|
|
|
Napi::BigInt bigInt = args[2].As<Napi::BigInt>();
|
|
bool lossless;
|
|
Memory.writeMemory<uint64_t>(handle, address, bigInt.Uint64Value(&lossless));
|
|
|
|
} else if (!strcmp(dataType, "float")) {
|
|
|
|
Memory.writeMemory<float>(handle, address, args[2].As<Napi::Number>().FloatValue());
|
|
|
|
} else if (!strcmp(dataType, "double")) {
|
|
|
|
Memory.writeMemory<double>(handle, address, args[2].As<Napi::Number>().DoubleValue());
|
|
|
|
} else if (!strcmp(dataType, "ptr") || !strcmp(dataType, "pointer")) {
|
|
|
|
Napi::BigInt bigInt = args[2].As<Napi::BigInt>();
|
|
|
|
if (sizeof(intptr_t) == 8 && !bigInt.IsBigInt()) {
|
|
std::string error = "Writing memoryjs.PTR or memoryjs.POINTER on 64 bit target build requires you to supply a BigInt.";
|
|
error += " Rebuild the library with `npm run build32` to target 32 bit applications.";
|
|
Napi::Error::New(env, error).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (bigInt.IsBigInt()) {
|
|
bool lossless;
|
|
Memory.writeMemory<intptr_t>(handle, address, bigInt.Int64Value(&lossless));
|
|
} else {
|
|
Memory.writeMemory<intptr_t>(handle, address, args[2].As<Napi::Number>().Int32Value());
|
|
}
|
|
|
|
} else if (!strcmp(dataType, "uptr") || !strcmp(dataType, "upointer")) {
|
|
|
|
Napi::BigInt bigInt = args[2].As<Napi::BigInt>();
|
|
|
|
if (sizeof(uintptr_t) == 8 && !bigInt.IsBigInt()) {
|
|
std::string error = "Writing memoryjs.PTR or memoryjs.POINTER on 64 bit target build requires you to supply a BigInt.";
|
|
error += " Rebuild the library with `npm run build32` to target 32 bit applications.";
|
|
Napi::Error::New(env, error).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (bigInt.IsBigInt()) {
|
|
bool lossless;
|
|
Memory.writeMemory<uintptr_t>(handle, address, bigInt.Uint64Value(&lossless));
|
|
} else {
|
|
Memory.writeMemory<uintptr_t>(handle, address, args[2].As<Napi::Number>().Uint32Value());
|
|
}
|
|
|
|
} else if (!strcmp(dataType, "bool") || !strcmp(dataType, "boolean")) {
|
|
|
|
Memory.writeMemory<bool>(handle, address, args[2].As<Napi::Boolean>().Value());
|
|
|
|
} else if (!strcmp(dataType, "string") || !strcmp(dataType, "str")) {
|
|
|
|
std::string valueParam(args[2].As<Napi::String>().Utf8Value());
|
|
valueParam.append("", 1);
|
|
|
|
// Write String, Method 1
|
|
//Memory.writeMemory<std::string>(handle, address, std::string(*valueParam));
|
|
|
|
// Write String, Method 2
|
|
Memory.writeMemory(handle, address, (char*) valueParam.data(), valueParam.size());
|
|
|
|
} else if (!strcmp(dataType, "vector3") || !strcmp(dataType, "vec3")) {
|
|
|
|
Napi::Object value = args[2].As<Napi::Object>();
|
|
Vector3 vector = {
|
|
value.Get(Napi::String::New(env, "x")).As<Napi::Number>().FloatValue(),
|
|
value.Get(Napi::String::New(env, "y")).As<Napi::Number>().FloatValue(),
|
|
value.Get(Napi::String::New(env, "z")).As<Napi::Number>().FloatValue()
|
|
};
|
|
Memory.writeMemory<Vector3>(handle, address, vector);
|
|
|
|
} else if (!strcmp(dataType, "vector4") || !strcmp(dataType, "vec4")) {
|
|
|
|
Napi::Object value = args[2].As<Napi::Object>();
|
|
Vector4 vector = {
|
|
value.Get(Napi::String::New(env, "w")).As<Napi::Number>().FloatValue(),
|
|
value.Get(Napi::String::New(env, "x")).As<Napi::Number>().FloatValue(),
|
|
value.Get(Napi::String::New(env, "y")).As<Napi::Number>().FloatValue(),
|
|
value.Get(Napi::String::New(env, "z")).As<Napi::Number>().FloatValue()
|
|
};
|
|
Memory.writeMemory<Vector4>(handle, address, vector);
|
|
|
|
} else {
|
|
Napi::Error::New(env, "unexpected data type").ThrowAsJavaScriptException();
|
|
}
|
|
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Value writeBuffer(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 3) {
|
|
Napi::Error::New(env, "required 3 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "first and second argument must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
SIZE_T length = args[2].As<Napi::Buffer<char>>().Length();
|
|
char* data = args[2].As<Napi::Buffer<char>>().Data();
|
|
Memory.writeMemory<char*>(handle, address, data, length);
|
|
|
|
return env.Null();
|
|
}
|
|
|
|
// Napi::Value findPattern(const Napi::CallbackInfo& args) {
|
|
// Napi::Env env = args.Env();
|
|
|
|
// HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
// DWORD64 baseAddress = args[1].As<Napi::Number>().Int64Value();
|
|
// DWORD64 baseSize = args[2].As<Napi::Number>().Int64Value();
|
|
// std::string signature(args[3].As<Napi::String>().Utf8Value());
|
|
// short flags = args[4].As<Napi::Number>().Uint32Value();
|
|
// uint32_t patternOffset = args[5].As<Napi::Number>().Uint32Value();
|
|
|
|
// // matching address
|
|
// uintptr_t address = 0;
|
|
// char* errorMessage = "";
|
|
|
|
// // read memory region occupied by the module to pattern match inside
|
|
// std::vector<unsigned char> moduleBytes = std::vector<unsigned char>(baseSize);
|
|
// ReadProcessMemory(handle, (LPVOID)baseAddress, &moduleBytes[0], baseSize, nullptr);
|
|
// unsigned char* byteBase = const_cast<unsigned char*>(&moduleBytes.at(0));
|
|
|
|
// Pattern.findPattern(handle, baseAddress, byteBase, baseSize, signature.c_str(), flags, patternOffset, &address);
|
|
|
|
// if (address == 0) {
|
|
// errorMessage = "unable to match pattern inside any modules or regions";
|
|
// }
|
|
|
|
// if (args.Length() == 5) {
|
|
// Napi::Function callback = args[4].As<Napi::Function>();
|
|
// callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Value::From(env, address) });
|
|
// return env.Null();
|
|
// } else {
|
|
// return Napi::Value::From(env, address);
|
|
// }
|
|
// }
|
|
|
|
Napi::Value findPattern(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 4 && args.Length() != 5) {
|
|
Napi::Error::New(env, "requires 4 arguments, 5 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsString() || !args[2].IsNumber() || !args[3].IsNumber()) {
|
|
Napi::Error::New(env, "expected: number, string, string, number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 5 && !args[4].IsFunction()) {
|
|
Napi::Error::New(env, "callback argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
std::string pattern(args[1].As<Napi::String>().Utf8Value());
|
|
short flags = args[2].As<Napi::Number>().Uint32Value();
|
|
uint32_t patternOffset = args[3].As<Napi::Number>().Uint32Value();
|
|
|
|
// matching address
|
|
uintptr_t address = 0;
|
|
char* errorMessage = "";
|
|
|
|
std::vector<MODULEENTRY32> modules = module::getModules(GetProcessId(handle), &errorMessage);
|
|
Pattern.search(handle, modules, 0, pattern.c_str(), flags, patternOffset, &address);
|
|
|
|
// if no match found inside any modules, search memory regions
|
|
if (address == 0) {
|
|
std::vector<MEMORY_BASIC_INFORMATION> regions = Memory.getRegions(handle);
|
|
Pattern.search(handle, regions, 0, pattern.c_str(), flags, patternOffset, &address);
|
|
}
|
|
|
|
if (address == 0) {
|
|
errorMessage = "unable to match pattern inside any modules or regions";
|
|
}
|
|
|
|
if (args.Length() == 5) {
|
|
Napi::Function callback = args[4].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Value::From(env, address) });
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Value::From(env, address);
|
|
}
|
|
}
|
|
|
|
Napi::Value findPatternByModule(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 5 && args.Length() != 6) {
|
|
Napi::Error::New(env, "requires 5 arguments, 6 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsString() || !args[2].IsString() || !args[3].IsNumber() || !args[4].IsNumber()) {
|
|
Napi::Error::New(env, "expected: number, string, string, number, number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 6 && !args[5].IsFunction()) {
|
|
Napi::Error::New(env, "callback argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
std::string moduleName(args[1].As<Napi::String>().Utf8Value());
|
|
std::string pattern(args[2].As<Napi::String>().Utf8Value());
|
|
short flags = args[3].As<Napi::Number>().Uint32Value();
|
|
uint32_t patternOffset = args[4].As<Napi::Number>().Uint32Value();
|
|
|
|
// matching address
|
|
uintptr_t address = 0;
|
|
char* errorMessage = "";
|
|
|
|
MODULEENTRY32 module = module::findModule(moduleName.c_str(), GetProcessId(handle), &errorMessage);
|
|
|
|
uintptr_t baseAddress = (uintptr_t) module.modBaseAddr;
|
|
DWORD baseSize = module.modBaseSize;
|
|
|
|
// read memory region occupied by the module to pattern match inside
|
|
std::vector<unsigned char> moduleBytes = std::vector<unsigned char>(baseSize);
|
|
ReadProcessMemory(handle, (LPVOID)baseAddress, &moduleBytes[0], baseSize, nullptr);
|
|
unsigned char* byteBase = const_cast<unsigned char*>(&moduleBytes.at(0));
|
|
|
|
Pattern.findPattern(handle, baseAddress, byteBase, baseSize, pattern.c_str(), flags, patternOffset, &address);
|
|
|
|
if (address == 0) {
|
|
errorMessage = "unable to match pattern inside any modules or regions";
|
|
}
|
|
|
|
if (args.Length() == 6) {
|
|
Napi::Function callback = args[5].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Value::From(env, address) });
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Value::From(env, address);
|
|
}
|
|
}
|
|
|
|
Napi::Value findPatternByAddress(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 5 && args.Length() != 6) {
|
|
Napi::Error::New(env, "requires 5 arguments, 6 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsNumber() || !args[2].IsString() || !args[3].IsNumber() || !args[4].IsNumber()) {
|
|
Napi::Error::New(env, "expected: number, number, string, number, number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 6 && !args[5].IsFunction()) {
|
|
Napi::Error::New(env, "callback argument must be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 baseAddress = args[1].As<Napi::Number>().Int64Value();
|
|
std::string pattern(args[2].As<Napi::String>().Utf8Value());
|
|
short flags = args[3].As<Napi::Number>().Uint32Value();
|
|
uint32_t patternOffset = args[4].As<Napi::Number>().Uint32Value();
|
|
|
|
// matching address
|
|
uintptr_t address = 0;
|
|
char* errorMessage = "";
|
|
|
|
std::vector<MODULEENTRY32> modules = module::getModules(GetProcessId(handle), &errorMessage);
|
|
Pattern.search(handle, modules, baseAddress, pattern.c_str(), flags, patternOffset, &address);
|
|
|
|
if (address == 0) {
|
|
std::vector<MEMORY_BASIC_INFORMATION> regions = Memory.getRegions(handle);
|
|
Pattern.search(handle, regions, baseAddress, pattern.c_str(), flags, patternOffset, &address);
|
|
}
|
|
|
|
if (address == 0) {
|
|
errorMessage = "unable to match pattern inside any modules or regions";
|
|
}
|
|
|
|
if (args.Length() == 6) {
|
|
Napi::Function callback = args[5].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Value::From(env, address) });
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Value::From(env, address);
|
|
}
|
|
}
|
|
|
|
Napi::Value callFunction(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 4 && args.Length() != 5) {
|
|
Napi::Error::New(env, "requires 4 arguments, 5 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsObject() && !args[2].IsNumber() && !args[3].IsNumber()) {
|
|
Napi::Error::New(env, "invalid arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// TODO: temp (?) solution to forcing variables onto the heap
|
|
// to ensure consistent addresses. copy everything to a vector, and use the
|
|
// vector's instances of the variables as the addresses being passed to `functions.call()`.
|
|
// Another solution: do `int x = new int(4)` and then use `&x` for the address
|
|
std::vector<LPVOID> heap;
|
|
|
|
std::vector<functions::Arg> parsedArgs;
|
|
Napi::Array arguments = args[1].As<Napi::Array>();
|
|
for (unsigned int i = 0; i < arguments.Length(); i++) {
|
|
Napi::Object argument = arguments.Get(i).As<Napi::Object>();
|
|
|
|
functions::Type type = (functions::Type) argument.Get(Napi::String::New(env, "type")).As<Napi::Number>().Uint32Value();
|
|
|
|
if (type == functions::Type::T_STRING) {
|
|
std::string stringValue = argument.Get(Napi::String::New(env, "value")).As<Napi::String>().Utf8Value();
|
|
parsedArgs.push_back({ type, &stringValue });
|
|
}
|
|
|
|
if (type == functions::Type::T_INT) {
|
|
int data = argument.Get(Napi::String::New(env, "value")).As<Napi::Number>().Int32Value();
|
|
|
|
// As we only pass the addresses of the variable to the `call` function and not a copy
|
|
// of the variable itself, we need to ensure that the variable stays alive and in a unique
|
|
// memory location until the `call` function has been executed. So manually allocate memory,
|
|
// track it, and then free it once the function has been called.
|
|
// TODO: find a better solution?
|
|
int* memory = (int*) malloc(sizeof(int));
|
|
*memory = data;
|
|
heap.push_back(memory);
|
|
|
|
parsedArgs.push_back({ type, memory });
|
|
}
|
|
|
|
if (type == functions::Type::T_FLOAT) {
|
|
float data = argument.Get(Napi::String::New(env, "value")).As<Napi::Number>().FloatValue();
|
|
|
|
float* memory = (float*) malloc(sizeof(float));
|
|
*memory = data;
|
|
heap.push_back(memory);
|
|
|
|
parsedArgs.push_back({ type, memory });
|
|
}
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
functions::Type returnType = (functions::Type) args[2].As<Napi::Number>().Uint32Value();
|
|
DWORD64 address = args[3].As<Napi::Number>().Int64Value();
|
|
|
|
char* errorMessage = "";
|
|
Call data = functions::call<int>(handle, parsedArgs, returnType, address, &errorMessage);
|
|
|
|
// Free all the memory we allocated
|
|
for (auto &memory : heap) {
|
|
free(memory);
|
|
}
|
|
|
|
heap.clear();
|
|
|
|
if (strcmp(errorMessage, "") && args.Length() != 5) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Object info = Napi::Object::New(env);
|
|
|
|
Napi::String keyString = Napi::String::New(env, "returnValue");
|
|
|
|
if (returnType == functions::Type::T_STRING) {
|
|
info.Set(keyString, Napi::String::New(env, data.returnString.c_str()));
|
|
}
|
|
|
|
if (returnType == functions::Type::T_CHAR) {
|
|
info.Set(keyString, Napi::Value::From(env, (char) data.returnValue));
|
|
}
|
|
|
|
if (returnType == functions::Type::T_BOOL) {
|
|
info.Set(keyString, Napi::Value::From(env, (bool) data.returnValue));
|
|
}
|
|
|
|
if (returnType == functions::Type::T_INT) {
|
|
info.Set(keyString, Napi::Value::From(env, (int) data.returnValue));
|
|
}
|
|
|
|
if (returnType == functions::Type::T_FLOAT) {
|
|
float value = *(float *)&data.returnValue;
|
|
info.Set(keyString, Napi::Value::From(env, value));
|
|
}
|
|
|
|
if (returnType == functions::Type::T_DOUBLE) {
|
|
double value = *(double *)&data.returnValue;
|
|
info.Set(keyString, Napi::Value::From(env, value));
|
|
}
|
|
|
|
info.Set(Napi::String::New(env, "exitCode"), Napi::Value::From(env, data.exitCode));
|
|
|
|
if (args.Length() == 5) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[2].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), info });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return info;
|
|
}
|
|
|
|
}
|
|
|
|
Napi::Value virtualProtectEx(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 4 && args.Length() != 5) {
|
|
Napi::Error::New(env, "requires 4 arguments, 5 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() && !args[1].IsNumber() && !args[2].IsNumber()) {
|
|
Napi::Error::New(env, "All arguments should be numbers.").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 5 && !args[4].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
DWORD result;
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
SIZE_T size = args[2].As<Napi::Number>().Int64Value();
|
|
DWORD protection = args[3].As<Napi::Number>().Uint32Value();
|
|
|
|
bool success = VirtualProtectEx(handle, (LPVOID) address, size, protection, &result);
|
|
|
|
char* errorMessage = "";
|
|
|
|
if (success == 0) {
|
|
errorMessage = "an error occurred calling VirtualProtectEx";
|
|
// errorMessage = GetLastErrorToString().c_str();
|
|
}
|
|
|
|
// If there is an error and there is no callback, throw the error
|
|
if (strcmp(errorMessage, "") && args.Length() != 5) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 5) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[5].As<Napi::Function>();
|
|
callback.Call(env.Global(), {
|
|
Napi::String::New(env, errorMessage),
|
|
Napi::Value::From(env, result)
|
|
});
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Value::From(env, result);
|
|
}
|
|
}
|
|
|
|
Napi::Value getRegions(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 1 && args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 1 argument, 2 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber()) {
|
|
Napi::Error::New(env, "invalid arguments: first argument must be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 2 && !args[1].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
std::vector<MEMORY_BASIC_INFORMATION> regions = Memory.getRegions(handle);
|
|
|
|
Napi::Array regionsArray = Napi::Array::New(env, regions.size());
|
|
|
|
for (std::vector<MEMORY_BASIC_INFORMATION>::size_type i = 0; i != regions.size(); i++) {
|
|
Napi::Object region = Napi::Object::New(env);
|
|
|
|
region.Set(Napi::String::New(env, "BaseAddress"), Napi::Value::From(env, (DWORD64) regions[i].BaseAddress));
|
|
region.Set(Napi::String::New(env, "AllocationBase"), Napi::Value::From(env, (DWORD64) regions[i].AllocationBase));
|
|
region.Set(Napi::String::New(env, "AllocationProtect"), Napi::Value::From(env, (DWORD) regions[i].AllocationProtect));
|
|
region.Set(Napi::String::New(env, "RegionSize"), Napi::Value::From(env, (SIZE_T) regions[i].RegionSize));
|
|
region.Set(Napi::String::New(env, "State"), Napi::Value::From(env, (DWORD) regions[i].State));
|
|
region.Set(Napi::String::New(env, "Protect"), Napi::Value::From(env, (DWORD) regions[i].Protect));
|
|
region.Set(Napi::String::New(env, "Type"), Napi::Value::From(env, (DWORD) regions[i].Type));
|
|
|
|
char moduleName[MAX_PATH];
|
|
DWORD size = GetModuleFileNameExA(handle, (HINSTANCE)regions[i].AllocationBase, moduleName, MAX_PATH);
|
|
|
|
if (size != 0) {
|
|
region.Set(Napi::String::New(env, "szExeFile"), Napi::String::New(env, moduleName));
|
|
}
|
|
|
|
regionsArray.Set(i, region);
|
|
}
|
|
|
|
if (args.Length() == 2) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[1].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, ""), regionsArray });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return regionsArray;
|
|
}
|
|
}
|
|
|
|
Napi::Value virtualQueryEx(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2 && args.Length() != 3) {
|
|
Napi::Error::New(env, "requires 2 arguments, 3 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "first and second argument need to be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 3 && !args[2].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
|
|
MEMORY_BASIC_INFORMATION information;
|
|
SIZE_T result = VirtualQueryEx(handle, (LPVOID)address, &information, sizeof(information));
|
|
|
|
char* errorMessage = "";
|
|
|
|
if (result == 0 || result != sizeof(information)) {
|
|
errorMessage = "an error occurred calling VirtualQueryEx";
|
|
// errorMessage = GetLastErrorToString().c_str();
|
|
}
|
|
|
|
// If there is an error and there is no callback, throw the error
|
|
if (strcmp(errorMessage, "") && args.Length() != 3) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Object region = Napi::Object::New(env);
|
|
|
|
region.Set(Napi::String::New(env, "BaseAddress"), Napi::Value::From(env, (DWORD64) information.BaseAddress));
|
|
region.Set(Napi::String::New(env, "AllocationBase"), Napi::Value::From(env, (DWORD64) information.AllocationBase));
|
|
region.Set(Napi::String::New(env, "AllocationProtect"), Napi::Value::From(env, (DWORD) information.AllocationProtect));
|
|
region.Set(Napi::String::New(env, "RegionSize"), Napi::Value::From(env, (SIZE_T) information.RegionSize));
|
|
region.Set(Napi::String::New(env, "State"), Napi::Value::From(env, (DWORD) information.State));
|
|
region.Set(Napi::String::New(env, "Protect"), Napi::Value::From(env, (DWORD) information.Protect));
|
|
region.Set(Napi::String::New(env, "Type"), Napi::Value::From(env, (DWORD) information.Type));
|
|
|
|
if (args.Length() == 3) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[1].As<Napi::Function>();
|
|
callback.Call(env.Global(), { Napi::String::New(env, ""), region });
|
|
return env.Null();
|
|
} else {
|
|
// return JSON
|
|
return region;
|
|
}
|
|
}
|
|
|
|
Napi::Value virtualAllocEx(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 5 && args.Length() != 6) {
|
|
Napi::Error::New(env, "requires 5 arguments, 6 with callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[2].IsNumber() || !args[3].IsNumber() || !args[4].IsNumber()) {
|
|
Napi::Error::New(env, "invalid arguments: arguments 0, 2, 3 and 4 need to be numbers").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 6 && !args[5].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
SIZE_T size = args[2].As<Napi::Number>().Int64Value();
|
|
DWORD allocationType = args[3].As<Napi::Number>().Uint32Value();
|
|
DWORD protection = args[4].As<Napi::Number>().Uint32Value();
|
|
LPVOID address;
|
|
|
|
// Means in the JavaScript space `null` was passed through.
|
|
if (args[1] == env.Null()) {
|
|
address = NULL;
|
|
} else {
|
|
address = (LPVOID) args[1].As<Napi::Number>().Int64Value();
|
|
}
|
|
|
|
LPVOID allocatedAddress = VirtualAllocEx(handle, address, size, allocationType, protection);
|
|
|
|
char* errorMessage = "";
|
|
|
|
// If null, it means an error occurred
|
|
if (allocatedAddress == NULL) {
|
|
errorMessage = "an error occurred calling VirtualAllocEx";
|
|
// errorMessage = GetLastErrorToString().c_str();
|
|
}
|
|
|
|
// If there is an error and there is no callback, throw the error
|
|
if (strcmp(errorMessage, "") && args.Length() != 6) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 6) {
|
|
// Callback to let the user handle with the information
|
|
Napi::Function callback = args[5].As<Napi::Function>();
|
|
callback.Call(env.Global(), {
|
|
Napi::String::New(env, errorMessage),
|
|
Napi::Value::From(env, (intptr_t)allocatedAddress)
|
|
});
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Value::From(env, (intptr_t)allocatedAddress);
|
|
}
|
|
}
|
|
|
|
Napi::Value attachDebugger(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 2 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsBoolean()) {
|
|
Napi::Error::New(env, "first argument needs to be a number, second a boolean").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
DWORD processId = args[0].As<Napi::Number>().Uint32Value();
|
|
bool killOnExit = args[1].As<Napi::Boolean>().Value();
|
|
|
|
bool success = debugger::attach(processId, killOnExit);
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
|
|
Napi::Value detatchDebugger(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
DWORD processId = args[0].As<Napi::Number>().Uint32Value();
|
|
|
|
if (args.Length() != 1) {
|
|
Napi::Error::New(env, "requires only 1 argument").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber()) {
|
|
Napi::Error::New(env, "only argument needs to be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
bool success = debugger::detatch(processId);
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
|
|
Napi::Value awaitDebugEvent(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 2 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "both arguments need to be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
int millisTimeout = args[1].As<Napi::Number>().Uint32Value();
|
|
|
|
DebugEvent debugEvent;
|
|
bool success = debugger::awaitDebugEvent(millisTimeout, &debugEvent);
|
|
|
|
Register hardwareRegister = static_cast<Register>(args[0].As<Napi::Number>().Uint32Value());
|
|
|
|
if (success && debugEvent.hardwareRegister == hardwareRegister) {
|
|
Napi::Object info = Napi::Object::New(env);
|
|
|
|
info.Set(Napi::String::New(env, "processId"), Napi::Value::From(env, (DWORD) debugEvent.processId));
|
|
info.Set(Napi::String::New(env, "threadId"), Napi::Value::From(env, (DWORD) debugEvent.threadId));
|
|
info.Set(Napi::String::New(env, "exceptionCode"), Napi::Value::From(env, (DWORD) debugEvent.exceptionCode));
|
|
info.Set(Napi::String::New(env, "exceptionFlags"), Napi::Value::From(env, (DWORD) debugEvent.exceptionFlags));
|
|
info.Set(Napi::String::New(env, "exceptionAddress"), Napi::Value::From(env, (DWORD64) debugEvent.exceptionAddress));
|
|
info.Set(Napi::String::New(env, "hardwareRegister"), Napi::Value::From(env, static_cast<int>(debugEvent.hardwareRegister)));
|
|
|
|
return info;
|
|
}
|
|
|
|
// If we aren't interested in passing this event back to the JS space,
|
|
// just silently handle it
|
|
if (success && debugEvent.hardwareRegister != hardwareRegister) {
|
|
debugger::handleDebugEvent(debugEvent.processId, debugEvent.threadId);
|
|
}
|
|
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Value handleDebugEvent(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 2 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "both arguments need to be numbers").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
DWORD processId = args[0].As<Napi::Number>().Uint32Value();
|
|
DWORD threadId = args[1].As<Napi::Number>().Uint32Value();
|
|
|
|
bool success = debugger::handleDebugEvent(processId, threadId);
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
|
|
Napi::Value setHardwareBreakpoint(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 5) {
|
|
Napi::Error::New(env, "requires 5 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
for (unsigned int i = 0; i < args.Length(); i++) {
|
|
if (!args[i].IsNumber()) {
|
|
Napi::Error::New(env, "all arguments need to be numbers").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
}
|
|
|
|
DWORD processId = args[0].As<Napi::Number>().Uint32Value();
|
|
DWORD64 address = args[1].As<Napi::Number>().Int64Value();
|
|
Register hardwareRegister = static_cast<Register>(args[2].As<Napi::Number>().Uint32Value());
|
|
|
|
// Execute = 0x0
|
|
// Access = 0x3
|
|
// Writer = 0x1
|
|
int trigger = args[3].As<Napi::Number>().Uint32Value();
|
|
|
|
int length = args[4].As<Napi::Number>().Uint32Value();
|
|
|
|
bool success = debugger::setHardwareBreakpoint(processId, address, hardwareRegister, trigger, length);
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
|
|
Napi::Value removeHardwareBreakpoint(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2) {
|
|
Napi::Error::New(env, "requires 2 arguments").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsNumber()) {
|
|
Napi::Error::New(env, "both arguments need to be numbers").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
DWORD processId = args[0].As<Napi::Number>().Uint32Value();
|
|
Register hardwareRegister = static_cast<Register>(args[1].As<Napi::Number>().Uint32Value());
|
|
|
|
bool success = debugger::setHardwareBreakpoint(processId, 0, hardwareRegister, 0, 0);
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
|
|
Napi::Value injectDll(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2 && args.Length() != 3) {
|
|
Napi::Error::New(env, "requires 2 arguments, or 3 with a callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber() || !args[1].IsString()) {
|
|
Napi::Error::New(env, "first argument needs to be a number, second argument needs to be a string").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 3 && !args[2].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
std::string dllPath(args[1].As<Napi::String>().Utf8Value());
|
|
Napi::Function callback = args[2].As<Napi::Function>();
|
|
|
|
char* errorMessage = "";
|
|
DWORD moduleHandle = -1;
|
|
bool success = dll::inject(handle, dllPath, &errorMessage, &moduleHandle);
|
|
|
|
if (strcmp(errorMessage, "") && args.Length() != 3) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
// `moduleHandle` above is the return value of the `LoadLibrary` procedure,
|
|
// which we retrieve through `GetExitCode`. This value can become truncated
|
|
// in large address spaces such as 64 bit since `GetExitCode` just returns BOOL,
|
|
// so it's unreliable to use as the handle. Since the handle of a module is just a pointer
|
|
// to the address of the DLL mapped in the process' virtual address space, we can fetch
|
|
// this separately, so we won't return it to prevent it being passed to `unloadDll`
|
|
// and in some cases unexpectedly failing when it is truncated.
|
|
|
|
if (args.Length() == 3) {
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Boolean::New(env, success) });
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
}
|
|
|
|
Napi::Value unloadDll(const Napi::CallbackInfo& args) {
|
|
Napi::Env env = args.Env();
|
|
|
|
if (args.Length() != 2 && args.Length() != 3) {
|
|
Napi::Error::New(env, "requires 2 arguments, or 3 with a callback").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[0].IsNumber()) {
|
|
Napi::Error::New(env, "first argument needs to be a number").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (!args[1].IsNumber() && !args[1].IsString()) {
|
|
Napi::Error::New(env, "second argument needs to be a number or a string").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (args.Length() == 3 && !args[2].IsFunction()) {
|
|
Napi::Error::New(env, "callback needs to be a function").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
HANDLE handle = (HANDLE)args[0].As<Napi::Number>().Int64Value();
|
|
Napi::Function callback = args[2].As<Napi::Function>();
|
|
|
|
HMODULE moduleHandle;
|
|
|
|
// get module handle (module base address) directly
|
|
if (args[1].IsNumber()) {
|
|
moduleHandle = (HMODULE)args[1].As<Napi::Number>().Int64Value();
|
|
}
|
|
|
|
// find module handle from name of DLL
|
|
if (args[1].IsString()) {
|
|
std::string moduleName(args[1].As<Napi::String>().Utf8Value());
|
|
char* errorMessage = "";
|
|
|
|
MODULEENTRY32 module = module::findModule(moduleName.c_str(), GetProcessId(handle), &errorMessage);
|
|
|
|
if (strcmp(errorMessage, "")) {
|
|
if (args.Length() != 3) {
|
|
Napi::Error::New(env, "unable to find specified module").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
} else {
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage) });
|
|
return Napi::Boolean::New(env, false);
|
|
}
|
|
}
|
|
|
|
moduleHandle = (HMODULE) module.modBaseAddr;
|
|
}
|
|
|
|
char* errorMessage = "";
|
|
bool success = dll::unload(handle, &errorMessage, moduleHandle);
|
|
|
|
if (strcmp(errorMessage, "") && args.Length() != 3) {
|
|
Napi::Error::New(env, errorMessage).ThrowAsJavaScriptException();
|
|
return Napi::Boolean::New(env, false);
|
|
}
|
|
|
|
if (args.Length() == 3) {
|
|
callback.Call(env.Global(), { Napi::String::New(env, errorMessage), Napi::Boolean::New(env, success) });
|
|
return env.Null();
|
|
} else {
|
|
return Napi::Boolean::New(env, success);
|
|
}
|
|
}
|
|
|
|
// https://stackoverflow.com/a/17387176
|
|
std::string GetLastErrorToString() {
|
|
DWORD errorMessageID = ::GetLastError();
|
|
|
|
// No error message, return empty string
|
|
if(errorMessageID == 0) {
|
|
return std::string();
|
|
}
|
|
|
|
LPSTR messageBuffer = nullptr;
|
|
|
|
size_t size = FormatMessageA(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
errorMessageID,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPSTR)&messageBuffer,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
std::string message(messageBuffer, size);
|
|
|
|
// Free the buffer
|
|
LocalFree(messageBuffer);
|
|
return message;
|
|
}
|
|
|
|
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
exports.Set(Napi::String::New(env, "openProcess"), Napi::Function::New(env, openProcess));
|
|
exports.Set(Napi::String::New(env, "closeProcess"), Napi::Function::New(env, closeProcess));
|
|
exports.Set(Napi::String::New(env, "getProcesses"), Napi::Function::New(env, getProcesses));
|
|
exports.Set(Napi::String::New(env, "getModules"), Napi::Function::New(env, getModules));
|
|
exports.Set(Napi::String::New(env, "findModule"), Napi::Function::New(env, findModule));
|
|
exports.Set(Napi::String::New(env, "readMemory"), Napi::Function::New(env, readMemory));
|
|
exports.Set(Napi::String::New(env, "readBuffer"), Napi::Function::New(env, readBuffer));
|
|
exports.Set(Napi::String::New(env, "writeMemory"), Napi::Function::New(env, writeMemory));
|
|
exports.Set(Napi::String::New(env, "writeBuffer"), Napi::Function::New(env, writeBuffer));
|
|
exports.Set(Napi::String::New(env, "findPattern"), Napi::Function::New(env, findPattern));
|
|
exports.Set(Napi::String::New(env, "findPatternByModule"), Napi::Function::New(env, findPatternByModule));
|
|
exports.Set(Napi::String::New(env, "findPatternByAddress"), Napi::Function::New(env, findPatternByAddress));
|
|
exports.Set(Napi::String::New(env, "virtualProtectEx"), Napi::Function::New(env, virtualProtectEx));
|
|
exports.Set(Napi::String::New(env, "callFunction"), Napi::Function::New(env, callFunction));
|
|
exports.Set(Napi::String::New(env, "virtualAllocEx"), Napi::Function::New(env, virtualAllocEx));
|
|
exports.Set(Napi::String::New(env, "getRegions"), Napi::Function::New(env, getRegions));
|
|
exports.Set(Napi::String::New(env, "virtualQueryEx"), Napi::Function::New(env, virtualQueryEx));
|
|
exports.Set(Napi::String::New(env, "attachDebugger"), Napi::Function::New(env, attachDebugger));
|
|
exports.Set(Napi::String::New(env, "detatchDebugger"), Napi::Function::New(env, detatchDebugger));
|
|
exports.Set(Napi::String::New(env, "awaitDebugEvent"), Napi::Function::New(env, awaitDebugEvent));
|
|
exports.Set(Napi::String::New(env, "handleDebugEvent"), Napi::Function::New(env, handleDebugEvent));
|
|
exports.Set(Napi::String::New(env, "setHardwareBreakpoint"), Napi::Function::New(env, setHardwareBreakpoint));
|
|
exports.Set(Napi::String::New(env, "removeHardwareBreakpoint"), Napi::Function::New(env, removeHardwareBreakpoint));
|
|
exports.Set(Napi::String::New(env, "injectDll"), Napi::Function::New(env, injectDll));
|
|
exports.Set(Napi::String::New(env, "unloadDll"), Napi::Function::New(env, unloadDll));
|
|
return exports;
|
|
}
|
|
|
|
NODE_API_MODULE(memoryjs, init) |