//
//  hooking.c
//  kordophoned
//
//  Created by James Magahern on 11/13/18.
//  Copyright © 2018 James Magahern. All rights reserved.
//

#include "hooking.h"
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>

BOOL HookIMAgent(const char *relativeDylibPath, char **errorString)
{
    MBIMLogInfo(@"Hooking imagent");
    
    const char *hookDylibPath = realpath(relativeDylibPath, NULL);
    
    // See if file is there.
    int succ = access(hookDylibPath, R_OK);
    if (succ != 0) {
        *errorString = "Unable to access hook dylib. Does file exist?";
        return NO;
    }
    
    // Make sure we can load the dylib (filters out codesigning issues)
    void *handle = dlopen(hookDylibPath, RTLD_NOW);
    if (!handle) {
        *errorString = dlerror();
        return NO;
    }
    
    /*********
     ***********
     PROBABLY DON'T DO THIS
     
     If other processes start and load agentHook, then they will crash because dyld tries to
     interpose a function that doesn't exist.
     
     A better way (maybe put this in a script or something):
     ( But launchctl debug needs to run as root :( )
     
     $ launchctl debug gui/501/com.apple.imagent --environment DYLD_INSERT_LIBRARIES=(path to libagentHook.dylib)
     
     $ launchctl kill SIGKILL gui/501/com.apple.imagent
     
     // then let it restart...
     
     **/
    
    // Set launchd DYLD_INSERT_LIBRARIES environment variable
    const char *systemCommandFormatString = "/bin/launchctl setenv DYLD_INSERT_LIBRARIES %s";
    size_t bufferSize = strlen(systemCommandFormatString) + strlen(hookDylibPath) + 2;
    char *systemCommand = (char *)malloc(sizeof(char) * bufferSize);
    
    sprintf(systemCommand, "/bin/launchctl setenv DYLD_INSERT_LIBRARIES %s", hookDylibPath);
    int setEnvSucc = system(systemCommand);
    if (setEnvSucc != 0) {
        *errorString = "Unable to set launchd environment variable.";
        return NO;
    }
    
    MBIMLogInfo(@"Successfully setup environment variables");
    
    // Kill imagent so the new one has the loaded bundle
    MBIMLogInfo(@"Killing imagent...");
    int killAgentSuccess = system("killall imagent");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        system("/bin/launchctl unsetenv DYLD_INSERT_LIBRARIES");
    });
    
    return (killAgentSuccess == 0);
}
