fpPS4-Temmie-s-Launcher/App/node_modules/memoryjs/lib/functions.h

209 lines
6.5 KiB
C++

#pragma once
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <TlHelp32.h>
#include <vector>
struct Call {
int returnValue;
std::string returnString;
DWORD exitCode;
};
namespace functions {
enum class Type {
T_VOID = 0x0,
T_STRING = 0x1,
T_CHAR = 0x2,
T_BOOL = 0x3,
T_INT = 0x4,
T_DOUBLE = 0x5,
T_FLOAT = 0x6
};
struct Arg {
Type type;
LPVOID value;
};
LPVOID reserveString(HANDLE hProcess, const char* value, SIZE_T size);
char readChar(HANDLE hProcess, DWORD64 address);
template <class returnDataType>
Call call(HANDLE pHandle, std::vector<Arg> args, Type returnType, DWORD64 address, char** errorMessage) {
std::vector<unsigned char> argShellcode;
std::reverse(args.begin(), args.end());
for (auto &arg : args) {
// 0x68: PUSH imm16/imm32
// 0x6A: PUSH imm8
if (arg.type == Type::T_INT || arg.type == Type::T_FLOAT) {
argShellcode.push_back(0x68);
int value = *static_cast<int*>(arg.value);
// Little endian representation
for (int i = 0; i < 4; i++) {
int shifted = (value >> (i * 8)) & 0xFF;
argShellcode.push_back(shifted);
}
continue;
}
if (arg.type == Type::T_STRING) {
argShellcode.push_back(0x68);
std::string value = *static_cast<std::string*>(arg.value);
LPVOID address = functions::reserveString(pHandle, value.c_str(), value.length());
// Little endian representation
for (int i = 0; i < 4; i++) {
int shifted = ((int)address >> (i * 8)) & 0xFF;
argShellcode.push_back(shifted);
}
continue;
}
argShellcode.push_back(0x6A);
unsigned char value = *static_cast<unsigned char*>(arg.value);
argShellcode.push_back(value);
}
// 83: ADD r/m16/32 imm8
std::vector<unsigned char> callShellcode = {
// call 0x00000000
0xE8, 0x00, 0x00, 0x00, 0x00,
// add esp, [arg count * 4]
0x83, 0xC4, (unsigned char)(args.size() * 0x4),
};
LPVOID returnValuePointer = 0;
if (returnType != Type::T_VOID) {
// We will reserve memory for where we want to store the result,
// and move the return value to this address.
returnValuePointer = VirtualAllocEx(pHandle, NULL, sizeof(returnDataType), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (returnType == Type::T_FLOAT) {
// fstp DWORD PTR [0x12345678]
// when `call` is executed, if return type is float, it's stored
// in fpu registers (st0 through st7). we can use the `fst`
// instruction to move st0 to a place in memory
// D9 FSTP m32real ST Store Floating Point Value and Pop
// D9 = for m32
callShellcode.push_back(0xD9);
callShellcode.push_back(0x1C);
callShellcode.push_back(0x25);
} else if (returnType == Type::T_DOUBLE) {
// fstp QWORD PTR [0x12345678]
// DD FSTP m64real ST Store Floating Point Value and Pop
// DD = for m64
callShellcode.push_back(0xDD);
callShellcode.push_back(0x1C);
callShellcode.push_back(0x25);
} else {
// mov [0x1234], eax
// A3: MOVE moffs16/32 eAX
// Call routines places return value inside EAX
callShellcode.push_back(0xA3);
}
for (int i = 0; i < 4; i++) {
int shifted = ((DWORD)returnValuePointer >> (i * 8)) & 0xFF;
callShellcode.push_back(shifted);
}
}
// C3: return
callShellcode.push_back(0xC3);
// concatenate the arg shellcode with the calling shellcode
std::vector<unsigned char> shellcode;
shellcode.reserve(argShellcode.size() + callShellcode.size());
shellcode.insert(shellcode.end(), argShellcode.begin(), argShellcode.end());
shellcode.insert(shellcode.end(), callShellcode.begin(), callShellcode.end());
// 5 = 0xE8 (call) + 4 empty bytes for where the address will go
int addessShellcodeOffset = argShellcode.size() + 5;
// Allocate space for the shellcode
SIZE_T size = shellcode.size() * sizeof(unsigned char);
LPVOID pShellcode = VirtualAllocEx(pHandle, NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// `call` opcode takes relative address, so calculate the relative address
// taking into account where the shellcode will be written in memory
DWORD64 relative = address - (uintptr_t)pShellcode - addessShellcodeOffset;
// Write the relative address to the shellcode
for (int i = 0; i < 4; i++) {
int shifted = (relative >> (i * 8)) & 0xFF;
// argShellcode.size() will offset to the `0xE8` opcode, add 1 to offset that instruction
shellcode.at(argShellcode.size() + 1 + i) = shifted;
}
// Write the shellcode
WriteProcessMemory(pHandle, pShellcode, shellcode.data(), size, NULL);
// Execute the shellcode
HANDLE thread = CreateRemoteThread(pHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)pShellcode, NULL, NULL, NULL);
Call data = { 0, "", (DWORD) -1 };
if (thread == NULL) {
*errorMessage = "unable to create remote thread.";
return data;
}
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &data.exitCode);
if (returnType != Type::T_VOID && returnType != Type::T_STRING) {
ReadProcessMemory(pHandle, (LPVOID)returnValuePointer, &data.returnValue, sizeof(int), NULL);
VirtualFreeEx(pHandle, returnValuePointer, 0, MEM_RELEASE);
}
if (returnType == Type::T_STRING) {
// String is stored in memory somewhere
// When returning a string, the address of the string is placed in EAX.
// So we read the current returnValuePointer address to get the actual address of the string
ReadProcessMemory(pHandle, (LPVOID)returnValuePointer, &returnValuePointer, sizeof(int), NULL);
std::vector<char> chars;
int offset = 0x0;
while (true) {
char c = functions::readChar(pHandle, (DWORD64)returnValuePointer + offset);
chars.push_back(c);
// break at 1 million chars
if (offset == (sizeof(char) * 1000000)) {
chars.clear();
break;
}
// break at terminator (end of string)
if (c == '\0') {
break;
}
// go to next char
offset += sizeof(char);
}
std::string str(chars.begin(), chars.end());
// TODO: pass str as LPVOID and cast back to string
data.returnString = str;
}
VirtualFreeEx(pHandle, pShellcode, 0, MEM_RELEASE);
return data;
}
}
#endif
#pragma once