mirror of
https://github.com/themitosan/fpPS4-Temmie-s-Launcher.git
synced 2025-04-02 10:31:50 -04:00
209 lines
6.5 KiB
C++
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
|