LiteLoaderBDS-1.16.40/ScriptEngine/API/NativeAPI.cpp
2022-09-21 19:47:03 +08:00

552 lines
24 KiB
C++

#include "NativeAPI.h"
#include <HookAPI.h>
#include <magic_enum/magic_enum.hpp>
#include "../Tools/Demangler/include/MicrosoftDemangle.h"
Concurrency::concurrent_unordered_map<std::string, NativeFunction> NativeFunction::parsedSymbol;
#define NATIVE_CHECK_ARG_TYPE(TYPE) \
if (args[i].getKind() != TYPE) { \
logger.error("Wrong type of argument!"); \
logger.error("In Symbol: " + funcSymbol->mSymbol); \
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName); \
return Local<Value>(); \
}
#define NATIVE_CHECK_ARG_CLASS(TYPE) \
if (!IsInstanceOf<TYPE>(args[i])) { \
logger.error("Wrong type of argument!"); \
logger.error("In Symbol: " + funcSymbol->mSymbol); \
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName); \
return Local<Value>(); \
}
Local<Value> NativeFunction::callNativeFunction(DCCallVM* vm, NativeFunction* funcSymbol, const Arguments& args) {
if (args.size() < funcSymbol->mParams.size()) {
logger.error("Too Few arguments!");
logger.error("In Component: NativeCall");
logger.error("In Symbol: " + funcSymbol->mSymbol);
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName);
return Local<Value>();
}
dcReset(vm);
for (int i = 0; i < funcSymbol->mParams.size(); ++i) {
switch (funcSymbol->mParams[i]) {
case NativeFunction::Types::Bool:
NATIVE_CHECK_ARG_TYPE(ValueKind::kBoolean);
dcArgChar(vm, args[i].asBoolean().value());
break;
case NativeFunction::Types::Char:
case NativeFunction::Types::UnsignedChar:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgChar(vm, (char)args[i].asNumber().toInt32());
break;
case NativeFunction::Types::Short:
case NativeFunction::Types::UnsignedShort:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgShort(vm, (short)args[i].asNumber().toInt32());
break;
case NativeFunction::Types::Int:
case NativeFunction::Types::UnsignedInt:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgInt(vm, (int)args[i].asNumber().toInt32());
break;
case NativeFunction::Types::Long:
case NativeFunction::Types::UnsignedLong:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgLong(vm, (long)args[i].asNumber().toInt32());
break;
case NativeFunction::Types::LongLong:
case NativeFunction::Types::UnsignedLongLong:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgLongLong(vm, args[i].asNumber().toInt64());
break;
case NativeFunction::Types::Float:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgFloat(vm, args[i].asNumber().toFloat());
break;
case NativeFunction::Types::Double:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgDouble(vm, args[i].asNumber().toDouble());
break;
case NativeFunction::Types::Pointer:
switch (args[i].getKind()) {
case ValueKind::kObject:
NATIVE_CHECK_ARG_CLASS(NativePointer);
dcArgPointer(vm, NativePointer::extract(args[i]));
break;
case ValueKind::kNumber:
NATIVE_CHECK_ARG_TYPE(ValueKind::kNumber);
dcArgPointer(vm, (void*)args[i].asNumber().toInt64());
break;
case ValueKind::kNull:
default:
dcArgPointer(vm, nullptr);
break;
}
break;
default:
break;
}
}
Local<Value> res;
DCpointer func = (DCpointer)funcSymbol->mFunction;
switch (funcSymbol->mReturnVal) {
case NativeFunction::Types::Void:
dcCallVoid(vm, func);
break;
case NativeFunction::Types::Bool: {
// in c, bool is the alias of int
// [c]#define bool int, sizeof(bool) == 4
// but in cpp, bool represent a true/false value
// it'a a builtin type, sizeof(bool) == sizeof(char) == 1
res = Boolean::newBoolean(dcCallChar(vm, func));
break;
}
case NativeFunction::Types::Char:
case NativeFunction::Types::UnsignedChar:
res = Number::newNumber(dcCallChar(vm, func));
break;
case NativeFunction::Types::Short:
case NativeFunction::Types::UnsignedShort:
res = Number::newNumber(dcCallShort(vm, func));
break;
case NativeFunction::Types::Int:
case NativeFunction::Types::UnsignedInt:
res = Number::newNumber(dcCallInt(vm, func));
break;
case NativeFunction::Types::Long:
case NativeFunction::Types::UnsignedLong:
res = Number::newNumber((int)dcCallLong(vm, func));
break;
case NativeFunction::Types::LongLong:
case NativeFunction::Types::UnsignedLongLong:
res = Number::newNumber(dcCallLongLong(vm, func));
break;
case NativeFunction::Types::Float:
res = Number::newNumber(dcCallFloat(vm, func));
break;
case NativeFunction::Types::Double:
res = Number::newNumber(dcCallDouble(vm, func));
break;
case NativeFunction::Types::Pointer:
res = NativePointer::newNativePointer((NativePointer*)dcCallPointer(vm, func));
break;
default:
break;
}
return res;
}
char NativeFunction::getTypeSignature(NativeFunction::Types type) {
switch (type) {
case NativeFunction::Types::Bool:
return 'B';
break;
case NativeFunction::Types::Char:
return 'c';
break;
case NativeFunction::Types::UnsignedChar:
return 'C';
break;
case NativeFunction::Types::Short:
return 's';
break;
case NativeFunction::Types::UnsignedShort:
return 'S';
break;
case NativeFunction::Types::Int:
return 'i';
break;
case NativeFunction::Types::UnsignedInt:
return 'I';
break;
case NativeFunction::Types::Long:
return 'j';
break;
case NativeFunction::Types::UnsignedLong:
return 'J';
break;
case NativeFunction::Types::LongLong:
return 'l';
break;
case NativeFunction::Types::UnsignedLongLong:
return 'L';
break;
case NativeFunction::Types::Float:
return 'f';
break;
case NativeFunction::Types::Double:
return 'd';
break;
case NativeFunction::Types::Pointer:
return 'p';
break;
default:
return 'v';
break;
}
}
inline std::string NativeFunction::buildDynCallbackSig() {
std::string result;
size_t params_size = mParams.size();
result.reserve(params_size + 2);
for (int i = 0; i < params_size; ++i) {
result[i] = getTypeSignature(mParams[i]);
}
result[params_size + 0] = ')';
result[params_size + 1] = getTypeSignature(mReturnVal);
return result;
}
NativeFunction::Types NativeFunction::getNativeType(llvm::ms_demangle::Node* type) {
using namespace llvm::ms_demangle;
if (type) {
if (type->kind() == NodeKind::PrimitiveType) {
switch (((PrimitiveTypeNode*)type)->PrimKind) {
case PrimitiveKind::Bool:
return NativeFunction::Types::Bool;
break;
case PrimitiveKind::Char:
return NativeFunction::Types::Char;
break;
case PrimitiveKind::Uchar:
return NativeFunction::Types::UnsignedChar;
break;
case PrimitiveKind::Short:
return NativeFunction::Types::Short;
break;
case PrimitiveKind::Ushort:
return NativeFunction::Types::UnsignedShort;
break;
case PrimitiveKind::Int:
return NativeFunction::Types::Int;
break;
case PrimitiveKind::Uint:
return NativeFunction::Types::UnsignedInt;
break;
case PrimitiveKind::Long:
return NativeFunction::Types::Long;
break;
case PrimitiveKind::Ulong:
return NativeFunction::Types::UnsignedLong;
break;
case PrimitiveKind::Int64:
return NativeFunction::Types::LongLong;
break;
case PrimitiveKind::Uint64:
return NativeFunction::Types::UnsignedLongLong;
break;
case PrimitiveKind::Float:
return NativeFunction::Types::Float;
break;
case PrimitiveKind::Double:
return NativeFunction::Types::Double;
break;
default:
return NativeFunction::Types::Void;
}
} else if (type->kind() == NodeKind::PointerType || type->kind() == NodeKind::NamedIdentifier) {
return NativeFunction::Types::Pointer;
} else if (type->kind() == NodeKind::TagType) {
switch (((TagTypeNode*)type)->Tag) {
case TagKind::Enum:
return NativeFunction::Types::Int;
default:
return NativeFunction::Types::Pointer;
}
}
}
return NativeFunction::Types::Void;
}
NativeFunction NativeFunction::getOrParse(const std::string& symbol) {
// query cache
if (auto iter = parsedSymbol.find(symbol); iter != parsedSymbol.end()) {
return iter->second; // return by copy
}
// no cache found
using namespace llvm::ms_demangle;
NativeFunction result;
result.mFunction = dlsym_real(symbol.c_str());
result.mSymbol = symbol;
Demangler demangler;
StringView symbolView{symbol.c_str()};
SymbolNode* AST = demangler.parse(symbolView);
if (demangler.Error)
throw std::runtime_error("failed to demangle symbol");
if (AST->kind() != NodeKind::FunctionSymbol)
throw std::runtime_error("mangled name is not a function");
auto funcAST = static_cast<FunctionSymbolNode*>(AST);
result.mReturnVal = getNativeType(funcAST->Signature->ReturnType);
if (funcAST->Signature->Quals & Q_Pointer64)
result.mParams.push_back(NativeFunction::Types::Pointer);
if (funcAST->Signature->Params) {
result.mParams.reserve(funcAST->Signature->Params->Count + 1);
for (auto i = 0; i < funcAST->Signature->Params->Count; ++i) {
result.mParams.push_back(getNativeType(funcAST->Signature->Params->Nodes[i]));
}
}
// push into cache
parsedSymbol.insert({symbol, result});
return result;
}
Local<Value> ScriptNativeFunction::fromSymbol(const Arguments& args) {
using namespace llvm::ms_demangle;
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
const std::string symbol = args[0].toStr();
auto scriptResult = args.engine()->newNativeClass<ScriptNativeFunction>();
ScriptNativeFunction* result = args.engine()->getNativeInstance<ScriptNativeFunction>(scriptResult);
result->cloneFrom(getOrParse(symbol));
return scriptResult;
}
Local<Value> ScriptNativeFunction::fromDescription(const Arguments& args) {
auto scriptResult = args.engine()->newNativeClass<ScriptNativeFunction>();
ScriptNativeFunction* result = args.engine()->getNativeInstance<ScriptNativeFunction>(scriptResult);
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kNumber);
size_t argsSize = args.size();
result->mReturnVal = magic_enum::enum_cast<NativeFunction::Types>(args[0].toInt()).value_or(NativeFunction::Types::Void);
for (size_t i = 1; i < argsSize; i++) {
CHECK_ARG_TYPE(args[i], ValueKind::kNumber);
result->mParams.push_back(magic_enum::enum_cast<NativeFunction::Types>(args[i].toInt()).value_or(NativeFunction::Types::Void));
}
return scriptResult;
}
Local<Value> ScriptNativeFunction::fromScript(const Arguments& args) {
CHECK_ARG_TYPE(args[args.size() - 1], ValueKind::kFunction);
auto scriptResult = args.engine()->newNativeClass<DynamicHookData>(args.thiz());
DynamicHookData* nativeScriptFunction = args.engine()->getNativeInstance<DynamicHookData>(scriptResult);
CHECK_ARG_TYPE(args[0], ValueKind::kNumber);
size_t argsSize = args.size();
nativeScriptFunction->mReturnVal = magic_enum::enum_cast<NativeFunction::Types>(args[0].toInt()).value_or(NativeFunction::Types::Void);
for (size_t i = 1; i < argsSize - 1; i++) {
CHECK_ARG_TYPE(args[i], ValueKind::kNumber);
nativeScriptFunction->mParams.push_back(magic_enum::enum_cast<NativeFunction::Types>(args[i].toInt()).value_or(NativeFunction::Types::Void));
}
nativeScriptFunction->mEngine = args.engine();
nativeScriptFunction->mNativeCallack = dcbNewCallback(nativeScriptFunction->buildDynCallbackSig().c_str(), &nativeCallbackHandler, nativeScriptFunction);
nativeScriptFunction->mFunction = nativeScriptFunction->mNativeCallack;
nativeScriptFunction->mScriptCallback = args[args.size() - 1].asFunction();
return scriptResult;
}
Local<Value> NativeFunction::getCallableFunction() {
return Function::newFunction([this](const Arguments& args) -> Local<Value> {
return callNativeFunction(ENGINE_OWN_DATA()->dynamicCallVM, this, args);
});
}
char NativeFunction::nativeCallbackHandler(DCCallback* cb, DCArgs* args, DCValue* result, void* userdata) {
DynamicHookData* hookInfo = (DynamicHookData*)userdata;
EngineScope enter(hookInfo->mEngine);
try {
vector<Local<Value>> paras;
for (int i = 0; i < hookInfo->mParams.size(); ++i) {
switch (hookInfo->mParams[i]) {
case NativeFunction::Types::Bool:
paras.emplace_back(Boolean::newBoolean(dcbArgBool(args)));
break;
case NativeFunction::Types::Char:
paras.emplace_back(Number::newNumber(dcbArgChar(args)));
break;
case NativeFunction::Types::UnsignedChar:
paras.emplace_back(Number::newNumber(dcbArgUChar(args)));
break;
case NativeFunction::Types::Short:
paras.emplace_back(Number::newNumber(dcbArgShort(args)));
break;
case NativeFunction::Types::UnsignedShort:
paras.emplace_back(Number::newNumber(dcbArgUShort(args)));
break;
case NativeFunction::Types::Int:
paras.emplace_back(Number::newNumber(dcbArgInt(args)));
break;
case NativeFunction::Types::UnsignedInt:
paras.emplace_back(Number::newNumber((int)dcbArgUInt(args)));
break;
case NativeFunction::Types::Long:
paras.emplace_back(Number::newNumber((int)dcbArgLong(args)));
break;
case NativeFunction::Types::UnsignedLong:
paras.emplace_back(Number::newNumber((int)dcbArgULong(args)));
break;
case NativeFunction::Types::LongLong:
paras.emplace_back(Number::newNumber(dcbArgLongLong(args)));
break;
case NativeFunction::Types::UnsignedLongLong:
paras.emplace_back(Number::newNumber((long long)dcbArgULongLong(args)));
break;
case NativeFunction::Types::Float:
paras.emplace_back(Number::newNumber(dcbArgFloat(args)));
break;
case NativeFunction::Types::Double:
paras.emplace_back(Number::newNumber(dcbArgDouble(args)));
break;
case NativeFunction::Types::Pointer:
paras.emplace_back(NativePointer::newNativePointer((NativePointer*)dcbArgPointer(args)));
break;
default:
break;
}
}
Local<Value> res = hookInfo->mScriptCallback.get().call({}, paras);
switch (hookInfo->mReturnVal) {
case NativeFunction::Types::Bool:
result->c = res.asBoolean().value();// pass bool by char
break;
case NativeFunction::Types::Char:
result->c = (char)res.toInt();
break;
case NativeFunction::Types::UnsignedChar:
result->C = (unsigned char)res.toInt();
break;
case NativeFunction::Types::Short:
result->s = (short)res.toInt();
break;
case NativeFunction::Types::UnsignedShort:
result->S = (unsigned short)res.toInt();
break;
case NativeFunction::Types::Int:
result->i = (int)res.toInt();
break;
case NativeFunction::Types::UnsignedInt:
result->I = (unsigned int)res.toInt();
break;
case NativeFunction::Types::Long:
result->j = (long)res.toInt();
break;
case NativeFunction::Types::UnsignedLong:
result->J = (unsigned long)res.toInt();
break;
case NativeFunction::Types::LongLong:
result->l = (long long)res.asNumber().toInt64();
break;
case NativeFunction::Types::UnsignedLongLong:
result->L = (unsigned long long)res.asNumber().toInt64();
break;
case NativeFunction::Types::Float:
result->f = res.asNumber().toFloat();
break;
case NativeFunction::Types::Double:
result->d = res.asNumber().toDouble();
break;
case NativeFunction::Types::Pointer:
switch (res.getKind()) {
case ValueKind::kObject:
result->p = NativePointer::extract(res);
break;
case ValueKind::kNumber:
result->p = (void*)res.asNumber().toInt64();
break;
case ValueKind::kNull:
default:
result->p = nullptr;
break;
}
break;
default:
break;
}
} catch (const Exception& e) {
logger.error("Hook Callback Failed!");
logger.error("Message: {}",e.message());
logger.error("StackTrace: {}", e.stacktrace());
logger.error("In Symbol: " + hookInfo->mSymbol);
logger.error("In Plugin: " + ENGINE_OWN_DATA()->pluginName);
}
return NativeFunction::getTypeSignature(hookInfo->mReturnVal);
}
Local<Value> ScriptNativeFunction::setAddress(const Local<Value>& value) {
auto engine = this->getScriptEngine();
bool isNativePointer = engine->isInstanceOf<NativePointer>(value);
bool isInt64 = value.getKind() == ValueKind::kNumber;
if (isNativePointer) {
NativePointer* ptr = engine->getNativeInstance<NativePointer>(value);
this->mFunction = ptr->unwrap();
return Local<Value>();
}
if (isInt64) {
*((__int64*)&this->mFunction) = value.asNumber().toInt64();
return Local<Value>();
}
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
Local<Value> ScriptNativeFunction::getAddress() {
try {
return Number::newNumber(*((__int64*)&this->mFunction));
}
CATCH("Fail in getUchar!")
}
Local<Value> ScriptNativeFunction::hook(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kFunction);
auto scriptResult = args.engine()->newNativeClass<DynamicHookData>(args.thiz());
DynamicHookData* hookSymbol = args.engine()->getNativeInstance<DynamicHookData>(scriptResult);
hookSymbol->cloneFrom(NativeFunction(*args.engine()->getNativeInstance<ScriptNativeFunction>(args.thiz())));
hookSymbol->mEngine = args.engine();
hookSymbol->mNativeCallack = dcbNewCallback(hookSymbol->buildDynCallbackSig().c_str(), &nativeCallbackHandler, hookSymbol);
hookSymbol->mScriptCallback = args[0].asFunction();
void* hookOriginl = nullptr;
int hookResult = HookFunction(hookSymbol->mFunction, &hookOriginl, hookSymbol->mNativeCallack);
hookSymbol->mFunction = hookOriginl;
return scriptResult;
}
ClassDefine<void> NativeTypeEnumBuilder =
defineClass("NativeTypes")
.property("Void", &NativeFunction::getType<NativeFunction::Types::Void>)
.property("Bool", &NativeFunction::getType<NativeFunction::Types::Bool>)
.property("Char", &NativeFunction::getType<NativeFunction::Types::Char>)
.property("UnsignedChar", &NativeFunction::getType<NativeFunction::Types::UnsignedChar>)
.property("Short", &NativeFunction::getType<NativeFunction::Types::Short>)
.property("UnsignedShort", &NativeFunction::getType<NativeFunction::Types::UnsignedShort>)
.property("Int", &NativeFunction::getType<NativeFunction::Types::Int>)
.property("UnsignedInt", &NativeFunction::getType<NativeFunction::Types::UnsignedInt>)
.property("Long", &NativeFunction::getType<NativeFunction::Types::Long>)
.property("UnsignedLong", &NativeFunction::getType<NativeFunction::Types::UnsignedLong>)
.property("LongLong", &NativeFunction::getType<NativeFunction::Types::LongLong>)
.property("UnsignedLongLong", &NativeFunction::getType<NativeFunction::Types::UnsignedLongLong>)
.property("Float", &NativeFunction::getType<NativeFunction::Types::Float>)
.property("Double", &NativeFunction::getType<NativeFunction::Types::Double>)
.property("Pointer", &NativeFunction::getType<NativeFunction::Types::Pointer>)
.build();
ClassDefine<ScriptNativeFunction> NativeCallBuilder =
defineClass<ScriptNativeFunction>("NativeFunction")
.constructor()
.function("fromSymbol", &ScriptNativeFunction::fromSymbol)
.function("fromDescription", &ScriptNativeFunction::fromDescription)
.function("fromScript", &ScriptNativeFunction::fromScript)
.instanceFunction("hook", &ScriptNativeFunction::hook)
.instanceProperty("call", &ScriptNativeFunction::getCallableFunction)
.instanceProperty("address", &ScriptNativeFunction::getAddress, &ScriptNativeFunction::setAddress)
.build();
ClassDefine<DynamicHookData> NativeHookBuilder =
defineClass<DynamicHookData>("NativeHook")
.constructor()
.instanceProperty("call", &ScriptNativeFunction::getCallableFunction)
.instanceProperty("address", &ScriptNativeFunction::getAddress, &ScriptNativeFunction::setAddress)
.build();