feat: add injector cli

This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2026-01-11 14:27:05 -05:00
parent c9f25e7b6a
commit a9bf8fe6d6
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
7 changed files with 305 additions and 1 deletions

6
.clangd Normal file
View File

@ -0,0 +1,6 @@
CompileFlags:
BuiltinHeaders: QueryDriver
CompilationDatabase: .
Remove:
- 'external:I*'
- 'external:W*'

View File

@ -31,4 +31,5 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui Core5Compat QUIET) find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui Core5Compat QUIET)
add_subdirectory(framework) add_subdirectory(framework)
add_subdirectory(injector)

34
injector/CMakeLists.txt Normal file
View File

@ -0,0 +1,34 @@
include(FetchContent)
FetchContent_Declare(
argparse
GIT_REPOSITORY https://github.com/p-ranav/argparse.git
)
FetchContent_MakeAvailable(argparse)
### --- sources --- ###
file(GLOB_RECURSE INJECTOR_SOURCES CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
)
file(GLOB_RECURSE INJECTOR_HEADERS CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp"
)
list(FILTER INJECTOR_SOURCES EXCLUDE REGEX "/(out|build|cmake-build-|CMakeFiles)/")
list(REMOVE_ITEM INJECTOR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
add_library(libtoonboom_injector STATIC ${INJECTOR_SOURCES} ${INJECTOR_HEADERS})
target_compile_options(libtoonboom_injector PRIVATE "/EHsc")
target_include_directories(libtoonboom_injector PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
add_executable(toon_boom_injector "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
target_link_libraries(toon_boom_injector PRIVATE libtoonboom_injector)
target_link_libraries(toon_boom_injector PRIVATE argparse)
target_compile_options(toon_boom_injector PRIVATE "/EHsc")

73
injector/src/finder.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "finder.h"
#include <iostream>
namespace fs = std::filesystem;
std::vector<std::pair<std::string, std::filesystem::directory_entry>> findToonBoomVersions() {
std::string programFiles("C:\\Program Files\\Toon Boom Animation\\");
std::string programFilesX86("C:\\Program Files (x86)\\Toon Boom Animation\\");
std::vector<std::pair<std::string, std::filesystem::directory_entry>> versions;
try {
for (const auto &entry : fs::directory_iterator(programFiles)) {
auto subEntries = findSubEntries(entry);
versions.insert(versions.end(), subEntries.begin(), subEntries.end());
}
} catch (const std::filesystem::filesystem_error &e) {
}
try {
for (const auto &entry : fs::directory_iterator(programFilesX86)) {
auto subEntries = findSubEntries(entry);
versions.insert(versions.end(), subEntries.begin(), subEntries.end());
}
} catch (const std::filesystem::filesystem_error &e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return versions;
}
std::vector<std::pair<std::string, std::filesystem::directory_entry>>
findSubEntries(const std::filesystem::directory_entry &entry) {
std::vector<std::pair<std::string, std::filesystem::directory_entry>> versions;
std::set<std::string> exeNames = {"StoryboardPro.exe", "HarmonyPremium.exe",
"HarmonyAdvanced.exe",
"HarmonyEssentials.exe"};
if (fs::is_directory(entry.path())) {
if (entry.path().filename().string().find("Toon Boom") !=
std::string::npos) {
for (const auto &subEntry :
fs::recursive_directory_iterator(entry.path())) {
try {
if (fs::is_regular_file(subEntry.path()) &&
exeNames.contains(subEntry.path().filename().string())) {
versions.push_back(std::make_pair(entry.path().filename().string(), subEntry));
}
} catch (const std::filesystem::filesystem_error &e) {
continue;
}
}
}
}
return versions;
}
DWORD GetProcessIdByName(const std::string& processName) {
DWORD pid = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
PROCESSENTRY32 pe = { sizeof(pe) };
if (Process32First(hSnapshot, &pe)) {
do {
// std::cout << "Process name: " << pe.szExeFile << std::endl;
if (processName == std::string(pe.szExeFile)) {
pid = pe.th32ProcessID;
break;
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return pid;
}

14
injector/src/finder.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <windows.h>
#include <TlHelp32.h>
#include <string>
#include <vector>
#include <filesystem>
#include <set>
std::vector<std::pair<std::string, std::filesystem::directory_entry>> findToonBoomVersions();
std::vector<std::pair<std::string, std::filesystem::directory_entry>> findSubEntries(const std::filesystem::directory_entry& entry);
DWORD GetProcessIdByName(const std::string& processName);

176
injector/src/main.cpp Normal file
View File

@ -0,0 +1,176 @@
#include "./finder.h"
#include <argparse/argparse.hpp>
#include <iostream>
#include <vector>
argparse::ArgumentParser *&createProgram(int argc, char *argv[]) {
static auto program = new argparse::ArgumentParser("tb-injector-cli.exe");
program->set_prefix_chars("-/");
program->set_assign_chars(":=");
program->add_argument("-h", "--help")
.help("show help message and exit")
.implicit_value(true);
program->add_argument("-v", "--debug")
.help("log program's stdout and stderr to the given file")
.default_value("");
program->add_argument("-p", "--program")
.help("path to a Toon Boom program")
.default_value("")
.nargs(0, 1);
program->add_argument("-i", "--dep")
.help("path to an additional dll to copy into program's install dir")
.default_value<std::vector<std::string>>({})
.append();
program->add_argument("-d", "--dll").append().help("path to a dll to inject");
return program;
}
void copyDll(const std::string &dllPath,
const std::filesystem::directory_entry &entry, bool isDebug,
bool isDep) {
auto absPath = std::filesystem::absolute(dllPath);
if (!std::filesystem::exists(absPath)) {
std::cerr << "[warning] dll path " << absPath << " does not exist"
<< std::endl;
return;
}
std::filesystem::copy(absPath,
entry.path().parent_path() /
std::filesystem::path(dllPath).filename().string(),
std::filesystem::copy_options::overwrite_existing);
if (isDebug) {
std::cout << "Copied " << (isDep ? "dependency dll" : "dll") << " at "
<< absPath << " to "
<< entry.path().parent_path() /
std::filesystem::path(dllPath).filename().string()
<< std::endl;
}
}
int main(int argc, char *argv[]) {
auto args = createProgram(argc, argv);
try {
args->parse_args(argc, argv);
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cerr << args;
return 1;
}
if (args->present<bool>("-h")) {
std::cout << args->help().str() << std::endl;
return 0;
}
bool isDebug = false;
std::string logFile = "";
std::filesystem::directory_entry entry;
if (args->get<std::string>("-v") != "") {
logFile = args->get<std::string>("-v");
std::cout << "Logging to " << (logFile == "-" ? "console" : logFile)
<< std::endl;
}
std::string program = args->get<std::string>("-p");
if (program == "" || !std::filesystem::exists(program)) {
std::vector<std::pair<std::string, std::filesystem::directory_entry>>
versions = findToonBoomVersions();
bool isValidOption = false;
std::cout
<< "The following Toon Boom software was detected on your system: "
<< std::endl;
for (int i = 0; i < versions.size(); i++) {
std::cout << "\t" << "#" << i + 1 << ": " << versions[i].first
<< std::endl;
}
while (!isValidOption) {
std::cout << "Please pick a number between 1 and " << versions.size()
<< ": ";
int choice;
std::cin >> choice;
if (choice >= 1 && choice <= versions.size()) {
isValidOption = true;
program = versions[choice - 1].second.path().filename().string();
entry = versions[choice - 1].second;
}
}
} else {
entry = std::filesystem::directory_entry(program);
}
if (isDebug) {
std::cout << "Target executable: " << program << std::endl;
}
auto dllPaths = args->get<std::vector<std::string>>("-d");
if (dllPaths.size() == 0) {
std::cout << "must provide at least one dll path" << std::endl;
return 1;
}
auto dllDeps = args->get<std::vector<std::string>>("-i");
for (auto dllPath : dllPaths) {
copyDll(dllPath, entry, isDebug, false);
}
for (auto dllPath : dllDeps) {
copyDll(dllPath, entry, isDebug, true);
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
SECURITY_ATTRIBUTES sattr;
ZeroMemory(&sattr, sizeof(sattr));
sattr.bInheritHandle = FALSE;
si.cb = sizeof(si);
if (!CreateProcess(NULL, entry.path().string().data(), &sattr, NULL, FALSE,
CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL,
entry.path().parent_path().string().data(), &si, &pi)) {
std::cerr << "Failed to create process" << std::endl;
return 1;
}
std::cout << "Target process ID: " << pi.dwProcessId << std::endl;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
FARPROC hLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA");
for (auto dllPath : dllPaths) {
auto realPath = (std::filesystem::path(program).parent_path().string() + "/" + std::filesystem::absolute(dllPath).filename().string());
LPVOID remoteBuffer =
VirtualAllocEx(hProcess, NULL, dllPath.size() + 1,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (remoteBuffer == NULL) {
std::cerr << "Failed to allocate memory in target process" << std::endl;
return 1;
}
if (!WriteProcessMemory(hProcess, remoteBuffer, realPath.data(),
realPath.size() + 1, NULL)) {
std::cerr << "Failed to write process memory" << std::endl;
CloseHandle(hProcess);
return 1;
}
DWORD remoteTID;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)hLoadLibraryA,
remoteBuffer, 0, &remoteTID);
if (hThread == NULL) {
std::cerr << "Failed to create remote thread" << std::endl;
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return 1;
}
std::cout << "Remote thread ID: " << remoteTID << std::endl;
WaitForSingleObject(hThread, INFINITE);
DWORD exitCode;
GetExitCodeThread(hThread, &exitCode);
std::cout << "Exit code: " << exitCode << std::endl;
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
}
ResumeThread(pi.hThread);
CloseHandle(hProcess);
std::cout << "Congratulations!!! you have been injected :3" << std::endl;
return 0;
}

0
injector/src/stub.cpp Normal file
View File