Failed to get function pointers, Dylib Injection Struggles

126 Views Asked by At

I am trying to inject a dylib into a game called Roblox and have found the memory addresses neccessary already. When I inject using DYLD_INSERT_LIBRARIES, the code does nothing and gives the error defined in my code: "Failed to get function pointers."

I am building with the flags: -std=c++11 -dynamiclib -lc++ -framework CoreFoundation. and using gcc to compile. I am on MacOS and have little coding experience.

main.cpp:

#include <cstdio>
#include <syslog.h>
#include <thread>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <exception>
#include <mach/mach.h> // For Mach-O headers
#include <mach-o/dyld.h> // For _dyld_get_image_vmaddr_slide
#include <dlfcn.h> // For dlopen and dlclose

enum colours : std::uint8_t
{
    regular,
    info,
    warn,
    error
};

// Typedef for the functions to be loaded from the executable library
using r_getdatamodel2_typedef = int* (*)();
using r_getdatamodel_typedef = int(*)(int* a1, int* a2);

// Function pointers for the functions to be loaded from the executable library
r_getdatamodel_typedef r_getdatamodel = nullptr;
r_getdatamodel2_typedef r_getdatamodel2 = nullptr;

// Function to print messages using syslog
void print(std::uint8_t color, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsyslog(LOG_NOTICE, fmt, args);
    va_end(args);
}

// Function to split a string into a vector of strings based on whitespaces
std::vector<std::string> split(std::string& str)
{
    std::vector<std::string> myvector;
    std::stringstream mystream(str);
    while (!mystream.eof()) {
        std::string newstr;
        mystream >> newstr;
        myvector.push_back(newstr);
    }
    return myvector;
}

// Function to find the first child instance with a given name
uintptr_t findfirstchild(uintptr_t instance, std::string childname)
{
    uintptr_t childinfo = *reinterpret_cast<uintptr_t*>(instance + 0x30);
    uintptr_t childstart = *reinterpret_cast<uintptr_t*>(childinfo + 0xC);
    uintptr_t childend = *reinterpret_cast<uintptr_t*>(childinfo + 0x10);
    for (auto i = childstart; i < childend; i += 8) {
        uintptr_t instance = *reinterpret_cast<uintptr_t*>(i);
        std::string* name = *reinterpret_cast<std::string**>(instance + 0x28);
        if (!strcmp(name->c_str(), childname.c_str())) {
            return instance;
        }
    }
    std::cout << "no found!\n";
    return 0;
}

// Entry point of the code (main function)
auto entry() -> void {
    
    const auto printFunc = reinterpret_cast<void(*)(std::uint8_t, const char*, ...)>(_dyld_get_image_vmaddr_slide(0) + 0x100c46941);
    void* handle = dlopen(nullptr, RTLD_NOW);
    if (!handle) {
        std::cerr << "Failed to open executable library." << std::endl;
        return;
    }
    if (!r_getdatamodel || !r_getdatamodel2) {
        std::cerr << "Failed to get function pointers." << std::endl;
        dlclose(handle);
        return;
    }

    // ... (existing code remains the same)

    printFunc(colours::regular, "Function pointers loaded successfully.");

    int a[2]{ 0 };
    
    // Load the executable library
    

    // Get the function pointers
    r_getdatamodel = reinterpret_cast<r_getdatamodel_typedef>(dlsym(handle, "getDataModel"));
    r_getdatamodel2 = reinterpret_cast<r_getdatamodel2_typedef>(dlsym(handle, "getDataModel2"));
    

    // Check if the function pointers are valid
   

    // Close the library handle after getting the function pointers
    dlclose(handle);

    // ...

    // Get the print function pointer from the executable image slide
   
    printFunc(colours::regular, "trying...");

    // ...

    // Define the function pointer for the setspeed function from the executable image slide
    using setspeed_typedef = void(*)(int humanoid, float newspeed);
    setspeed_typedef setspeed = reinterpret_cast<setspeed_typedef>(_dyld_get_image_vmaddr_slide(0) + 0x1032bbdc7);

    // ...

    // Call the r_getdatamodel function to get the data model pointer
    r_getdatamodel(r_getdatamodel2(), a);
    uintptr_t datamodel = a[0] + 0xC;
    uintptr_t workspace = findfirstchild(datamodel, "Workspace");
    uintptr_t localplayer = *reinterpret_cast<uintptr_t*>(findfirstchild(datamodel, "Players") + 0x128);
    uintptr_t humanoid = findfirstchild(*reinterpret_cast<uintptr_t*>(localplayer + 0x7C), "Humanoid");
    humanoid = findfirstchild(*reinterpret_cast<uintptr_t*>(localplayer + 0x7C), "Humanoid");
    std::string store;
    while (std::getline(std::cin, store)) {
        std::vector<std::string> args = split(store);
        if (args.size() != 0) {
            if (!strcmp(args[0].c_str(), "gravity")) {
                if (args.size() == 2) {
                    try {
                        printFunc(colours::warn, "gravity enabled?");
                    }
                    catch (std::exception e) {
                        std::cout << e.what() << std::endl;
                        printFunc(colours::warn, "gravity not enabled");
                    }
                }
                else {
                    printFunc(colours::warn, "Usage: gravity 100\n"); ;
                }
            }
            else if (!strcmp(args[0].c_str(), "speed")) {
                if (args.size() == 2) {
                    try {
                        humanoid = findfirstchild(*reinterpret_cast<uintptr_t*>(localplayer + 0x7C), "Humanoid");
                        int humanoidID = static_cast<int>(humanoid); // Perform static cast from uintptr_t to int
                        setspeed(humanoidID, std::stof(args[1]));
                        printFunc(colours::warn, "setspeed activated");
                    }
                    catch (std::exception e) {
                        std::cout << e.what() << std::endl;
                    }
                }
                else {
                    printFunc(colours::warn,"Usage: speed 100\n");
                }
            }
            else if (!strcmp(args[0].c_str(), "jump")) {

            }
            else {
                printFunc(colours::warn,"Invalid command!\n");
            }
        }
    }

    // Print success message and sleep for 100 milliseconds
    printFunc(colours::regular, "success...");
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

// Constructor attribute to ensure the entry() function is called on library load
__attribute__((constructor))
static void dylib_main(void) {
    std::thread{ entry }.detach();
}

(main.cpp is compiled into a dylib which is then injected into RobloxPlayer) I am getting these memory addresses from BinaryNinja Before anyone says anything, this is for educational purposes.

I have tried different ways of injection and tried to find other memory addresses to modify. I expect the speed inside the game to change and to print "success" inside the in-game console. I have the code the way it is because the game uses luau and the code changing it is: game.Players.LocalPlayer.Character.Humanoid.WalkSpeed = 100 i had to replicate in C++.

0

There are 0 best solutions below