Skip to content

Commit 41c981f

Browse files
committed
stable injection!
1 parent bec8b6f commit 41c981f

File tree

10 files changed

+419
-240
lines changed

10 files changed

+419
-240
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
2+
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
3+
name: CMake on a single platform
4+
5+
on:
6+
push:
7+
branches: [ "master" ]
8+
pull_request:
9+
branches: [ "master" ]
10+
11+
env:
12+
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
13+
BUILD_TYPE: Release
14+
15+
jobs:
16+
build:
17+
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
18+
# You can convert this to a matrix build if you need cross-platform coverage.
19+
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
20+
runs-on: windows-latest
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Configure CMake
26+
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
27+
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
28+
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
29+
30+
- name: Build
31+
# Build your program with the given configuration
32+
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
33+
34+
- name: Test
35+
working-directory: ${{github.workspace}}/build
36+
# Execute tests defined by the CMake configuration.
37+
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
38+
run: ctest -C ${{env.BUILD_TYPE}}
39+
40+
- name: Upload build
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: Charon-dev
44+
path: build/Charon/Release/Charon.exe

Charon/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ FetchContent_Declare (
1919
)
2020
FetchContent_MakeAvailable (spdlog)
2121
target_link_libraries(Charon PRIVATE spdlog::spdlog)
22+
23+
FetchContent_Declare (
24+
argparse
25+
URL https://github.com/p-ranav/argparse/archive/refs/tags/v3.1.zip
26+
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
27+
)
28+
FetchContent_MakeAvailable (argparse)
29+
target_link_libraries(Charon PRIVATE argparse)

Charon/include/globals.hpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
#pragma once
22
#include <string>
33

4-
const std::string robloxVersion = "version-3c1b78b767674c66";
5-
using Stk_t = void**;
4+
#define END_MARKER goto __scf_skip_end;__debugbreak();__debugbreak();__scf_skip_end:{};
5+
constexpr ULONG END_MARKER_SIG = 0x02EB04EB; //jmprel+4,jmprel+2 //^^ the above instructions translate to this
6+
constexpr ULONGLONG STACK_PLACEHOLDER = 0x1493028DEAD;
67

7-
#define SCF_WRAP_START _Pragma("optimize(\"\", off)")
8-
#define SCF_WRAP_END _Pragma("optimize(\"\", on)")
9-
10-
#define SCF_END goto __scf_skip_end;__debugbreak();__debugbreak();__scf_skip_end:{};
11-
constexpr ULONG SCF_END_MARKER = 0x02EB04EB; //^^ the above instructions translate to this
12-
constexpr ULONGLONG SCF_STACK_PLACEHOLDER = 0x1493028DEAD;
13-
14-
#define SCF_STACK *const_cast<Stk_t*>(&__scf_ptr_stk);
15-
#define SCF_START const Stk_t __scf_ptr_stk = reinterpret_cast<const Stk_t>(SCF_STACK_PLACEHOLDER); Stk_t Stack = SCF_STACK;
8+
#define SCF_STACK *const_cast<void***>(&__scf_ptr_stk);
9+
#define DEFINE_STACK const void** __scf_ptr_stk = reinterpret_cast<const void**>(STACK_PLACEHOLDER); void** Stack = SCF_STACK;

Charon/include/manualmapper.hpp

Lines changed: 26 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
#include <spdlog/spdlog.h>
33
#include "memory.hpp"
44
#include "globals.hpp"
5+
56
namespace ManualMapper {
67
struct Detour {
7-
ULONGLONG stubAddr;
8-
ULONGLONG stubSize;
9-
ULONGLONG targetAddr;
8+
ULONGLONG allocAddr;
9+
ULONGLONG size;
10+
ULONGLONG originalJmp;
11+
};
12+
struct Allocation {
13+
ULONGLONG base;
14+
ULONGLONG size;
15+
};
16+
struct FunctionResult {
17+
std::vector<UCHAR> funcBytes;
18+
ULONGLONG base;
1019
};
1120
enum Status {
12-
STATUS_BUSY,
21+
STATUS_UNSET,
1322
STATUS_1,
1423
STATUS_2,
1524
STATUS_3,
@@ -18,156 +27,27 @@ namespace ManualMapper {
1827
};
1928

2029
//allocates and writes the dll to the target process
21-
HMODULE _allocDll(HANDLE handle, const std::string& dllPath);
22-
30+
Allocation _allocDll(HANDLE handle, const std::string& dllPath);
2331
//alloctes a piece of memory to hold the status of the hook, for communication internal and external
2432
LPVOID _createStatus(HANDLE handle);
2533

2634
//this detour will be jmped to by our hooked function which will then jmp to our dll :D
2735
//this function writes the function bytes of the detour and allocates valuable data with it
2836
//return statement exclusive
37+
FunctionResult _parseFunction(ULONGLONG funcBase, ULONGLONG size);
2938
ULONGLONG _calculateLocalFuncSize(ULONGLONG funcBase);
3039

31-
template<typename RetType, typename ...Args>
32-
ULONGLONG _createDetour(HANDLE handle, RetType(*targetFunction)(Args...), ULONGLONG dllAddr, ULONGLONG stub, ULONGLONG f) {
33-
34-
MemExternal::suspendByfronThreads(handle);
35-
ULONGLONG targetFuncSize = _calculateLocalFuncSize((ULONGLONG)targetFunction);
36-
ULONGLONG statusAddr = (ULONGLONG)_createStatus(handle);
37-
ULONGLONG originalJmp = MemExternal::readJmpAbs(handle, stub);
38-
39-
//get func bytes
40-
UCHAR* funcBytes = new UCHAR[targetFuncSize+200];
41-
//restore the original jump
42-
//mov rax, stub
43-
*(UCHAR*)funcBytes = 0x48;
44-
*(UCHAR*)(funcBytes + 1) = 0xB8;
45-
*(ULONGLONG*)(funcBytes + 2) = stub + 6;
46-
47-
//mov rcx, original
48-
*(UCHAR*)(funcBytes + 10) = 0x48;
49-
*(UCHAR*)(funcBytes + 11) = 0xB9;
50-
*(ULONGLONG*)(funcBytes + 12) = originalJmp;
51-
52-
//mov [rax], rcx
53-
*(UCHAR*)(funcBytes + 20) = 0x48;
54-
*(UCHAR*)(funcBytes + 21) = 0x89;
55-
*(UCHAR*)(funcBytes + 22) = 0x08;
56-
57-
UCHAR* targetFunctionBytes = funcBytes + 23;
58-
59-
//set the status to the hook was ran
60-
*(UCHAR*)targetFunctionBytes = 0x48;
61-
*(UCHAR*)(targetFunctionBytes + 1) = 0xB8;
62-
*(ULONGLONG*)(targetFunctionBytes + 2) = statusAddr;
63-
*(UCHAR*)(targetFunctionBytes + 10) = 0xC7;
64-
*(UCHAR*)(targetFunctionBytes + 11) = 0x00;
65-
*(ULONG*)(targetFunctionBytes + 12) = Status::STATUS_1;
66-
67-
targetFunctionBytes += 16;
68-
//copy the detour function bytes
69-
memcpy(targetFunctionBytes, (LPVOID)((ULONGLONG)targetFunction), targetFuncSize);
70-
71-
//allocate memory for entire detour
72-
LPVOID detourFunctionAlloc = VirtualAllocEx(handle, nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
73-
74-
//replace its stack placeholders with the real addr were going to use
75-
//stack is located in the allocation right after the function
76-
77-
ULONGLONG stackAddr = (ULONGLONG)detourFunctionAlloc + targetFuncSize+200;
78-
79-
for (ULONGLONG offset = 0; offset < targetFuncSize; offset++) {
80-
UCHAR* currentBytes = targetFunctionBytes + offset;
81-
if (*(ULONGLONG*)(currentBytes) == SCF_STACK_PLACEHOLDER) {
82-
*(ULONGLONG*)(currentBytes) = stackAddr;
83-
offset += sizeof(ULONGLONG);
84-
continue;
85-
}
86-
}
87-
88-
89-
{
90-
//right at the end
91-
UCHAR* currentBytes = targetFunctionBytes + targetFuncSize;
92-
93-
//set the status
94-
*(UCHAR*)currentBytes = 0x48;
95-
*(UCHAR*)(currentBytes + 1) = 0xB8;
96-
*(ULONGLONG*)(currentBytes + 2) = statusAddr;
97-
*(UCHAR*)(currentBytes + 10) = 0xC7;
98-
*(UCHAR*)(currentBytes + 11) = 0x00;
99-
*(ULONG*)(currentBytes + 12) = Status::STATUS_SUCCESS;
100-
101-
UCHAR* currentBytes1 = currentBytes + 16;
102-
103-
//redirect control flow back to where it was supposed to go
104-
*(UCHAR*)(currentBytes1) = 0xFF;
105-
*(UCHAR*)(currentBytes1 + 1) = 0x25;
106-
*(ULONG*)(currentBytes1 + 2) = 0x00000000;
107-
*(ULONGLONG*)(currentBytes1 + 6) = ((ULONGLONG)originalJmp);
108-
109-
}
110-
111-
112-
//fill the stack with actual data
113-
std::vector<ULONGLONG> stack = {};
114-
stack.push_back(dllAddr);
115-
stack.push_back(statusAddr);
116-
117-
HMODULE kernelBase = MemExternal::getLoadedModule(handle,"KERNELBASE.dll");
118-
ULONGLONG pLoadLibraryA = (ULONGLONG)GetProcAddress(kernelBase, "LoadLibraryA");
119-
stack.push_back(pLoadLibraryA);
120-
ULONGLONG pGetProcAddress = (ULONGLONG)GetProcAddress(kernelBase, "GetProcAddress");
121-
stack.push_back(pGetProcAddress);
122-
ULONGLONG pGetModuleHandleA = (ULONGLONG)GetProcAddress(kernelBase, "GetModuleHandleA");
123-
stack.push_back(pGetModuleHandleA);
124-
125-
//write the stack
126-
ULONGLONG stackAddrEnum = stackAddr;
127-
for (ULONGLONG each : stack) {
128-
if (!WriteProcessMemory(handle, (LPVOID)stackAddrEnum, &each, sizeof(ULONGLONG), 0)) {
129-
spdlog::error("Failed to write stack data! Err: {}", GetLastError());
130-
break;
131-
}
132-
stackAddrEnum += sizeof(ULONGLONG);
133-
}
134-
135-
if (!WriteProcessMemory(handle, detourFunctionAlloc, funcBytes, targetFuncSize + 200, 0)) {
136-
spdlog::error("Failed to write target func byte data! Err: {}", GetLastError());
137-
}
138-
139-
MemExternal::writeJmpAbs(handle, stub, (ULONGLONG)detourFunctionAlloc);
140-
FlushInstructionCache(handle, (LPVOID)stub, 12);
141-
142-
//we have to make the piece of memory that had our original jump writable so that our detour code and restore it
143-
ULONG oldProt;
144-
VirtualProtectEx(handle, (LPVOID)(stub + 6), sizeof(ULONGLONG), PAGE_EXECUTE_READWRITE, &oldProt);
145-
146-
ULONG lastStatus = -1;
147-
while (true) {
148-
ULONG status = 0;
149-
if (!ReadProcessMemory(handle, (LPVOID)statusAddr, &status, sizeof(status), 0)) {
150-
spdlog::error("Failed to read status! Err: {}", GetLastError());
151-
break;
152-
}
153-
if (lastStatus != status) {
154-
spdlog::info("Status: {}", status);
155-
}
156-
if (status == Status::STATUS_SUCCESS) {
157-
spdlog::info("successfully reached the end of the hook");
158-
MemExternal::resumeByfronThreads(handle);
159-
160-
//VirtualFreeEx(handle, detourFunctionAlloc, targetFuncSize + 200, MEM_RELEASE);
161-
//VirtualProtectEx(handle, (LPVOID)(stub + 6), sizeof(ULONGLONG), PAGE_EXECUTE, &oldProt);
162-
163-
break;
164-
}
165-
lastStatus = status;
166-
}
167-
168-
delete[] funcBytes;
169-
return (ULONGLONG)detourFunctionAlloc;
170-
}
40+
Detour _createDetour(
41+
Allocation optPreAllocatedData, //if you already allocated memory in the target process you can pass it in here
42+
HANDLE handle,
43+
LPVOID detourLocal, //the base address of the local detour function
44+
ULONGLONG stub, //the place where the absolute jmp were overriding is
45+
std::vector<ULONGLONG> stack, //the variables that the detour can use
46+
ULONGLONG statusAddr = 0 //during the hook, will move numbers into this address to signal the step in the hook its in
47+
//if status addr is set, it will also automatically restore the hook when its run once
48+
);
49+
50+
Detour _createDllDetour(HANDLE handle, LPVOID detourLocal, ULONGLONG stub, ULONGLONG dllAddr);
17151

17252
//the main function, does all the shit
17353
void inject(HANDLE handle, const std::string& dllPath);

Charon/include/memory.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ namespace MemExternal {
1919
HMODULE getLoadedModule(HANDLE handle, const char* modName);
2020
ULONG getModuleSize(HANDLE handle, HMODULE module);
2121

22-
//for testing
2322
void suspendAllThreads(HANDLE handle);
2423
void resumeAllThreads(HANDLE handle);
2524
void suspendByfronThreads(HANDLE handle);
@@ -64,11 +63,10 @@ namespace MemInternal {
6463
void __forceinline fixImports(ULONGLONG dll,
6564
decltype(&LoadLibraryA) &pLoadLibraryA,
6665
decltype(&GetModuleHandleA) &pGetModuleHandleA,
67-
decltype(&GetProcAddress) &pGetProcAddress ) {
66+
decltype(&GetProcAddress) &pGetProcAddress, ULONGLONG* status) {
6867
auto* dosHeader = (IMAGE_DOS_HEADER*)(dll);
6968
auto* ntHeaders = (IMAGE_NT_HEADERS*)(dll + dosHeader->e_lfanew);
7069
auto* optionalHeader = &ntHeaders->OptionalHeader;
71-
auto size = optionalHeader->SizeOfImage;
7270

7371
auto& importDir = optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
7472
if (importDir.Size) {
@@ -101,6 +99,7 @@ namespace MemInternal {
10199
thunk++;
102100
func++;
103101
}
102+
104103
importDesc++;
105104
}
106105
}

Charon/include/offsets.hpp

Whitespace-only changes.

Charon/src/main.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
#include <iostream>
33
#include <spdlog/spdlog.h>
4+
#include <argparse/argparse.hpp>
5+
46
#include "../include/manualmapper.hpp"
57
#include "../include/util.hpp"
68
int main(int argc, char* argv[]) {
@@ -18,11 +20,34 @@ int main(int argc, char* argv[]) {
1820
"`\"Y8888Y\"\' 88 88 `\"8bbdP\"Y8 88 `\"YbbdP\"\' 88 88 \n";
1921
Util::resetTextColor();
2022

23+
24+
argparse::ArgumentParser parser("Charon");
25+
26+
parser.add_description(
27+
"A Manual Map DLL Injector for Roblox. Most probably detected. Features absolutely no smart bypasses and your dll threads will probably cease to function after 30 seconds or so");
28+
parser.add_epilog("https://thorioum.net");
29+
parser.add_argument("-d", "--dll").required().help("the path to the dll to inject");
30+
try
31+
{
32+
parser.parse_args(argc, argv);
33+
}
34+
catch (const std::exception& ex)
35+
{
36+
spdlog::error("Error Parsing Args: {}", ex.what());
37+
return 1;
38+
}
39+
40+
std::string dllPath = parser.get< std::string >("dll");
2141
spdlog::info("Waiting for RobloxPlayerBeta.exe. . .");
42+
while (FindWindowW(nullptr, L"Roblox") == NULL)
43+
{
44+
Sleep(200);
45+
}
2246
HANDLE robloxHandle = MemExternal::WaitForProcess(PROCESS_ALL_ACCESS, FALSE, "RobloxPlayerBeta.exe").first;
47+
2348
spdlog::info("Succesfully opened handle to Roblox!");
2449

25-
ManualMapper::inject(robloxHandle, "testing.dll");
50+
ManualMapper::inject(robloxHandle, dllPath);
2651

2752
CloseHandle(robloxHandle);
2853
}

0 commit comments

Comments
 (0)