//
//  SecurityChecks.m
//  Runner
//
//  Created by Hamza Aleghwairyeen on 26/07/2025.
//

#import <Foundation/Foundation.h>




#import "SecurityChecks.h"
#import <sys/stat.h>
#include <dlfcn.h>
#import <sys/sysctl.h>
#include <mach-o/dyld.h> // For _dyld_image_count and _dyld_get_image_name
#include <unistd.h> // for fork()
#include <sys/socket.h> //for socket, connect
#include <netinet/in.h> // for sockaddr_in
#include <arpa/inet.h> // for inet_pton

@implementation SecurityChecks

+ (BOOL)isDeviceJailbroken {
    // (Same jailbreak checks as before - from previous responses)
     // 1. Check for common jailbreak files
    NSArray *jailbreakPaths = @[
        @"/Applications/Cydia.app",
//        #if !TARGET_IPHONE_SIMULATOR
//        @"/bin/bash",
//        #endif
        @"/Library/MobileSubstrate/MobileSubstrate.dylib",
        @"/bin/bash",
        @"/usr/sbin/sshd",
        @"/etc/apt",
        @"/private/var/lib/apt/",
        @"/private/var/tmp/cydia.log",
    ];

    for (NSString *path in jailbreakPaths) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
            NSLog(@"Jailbreak file detected: %@", path);
            return YES;
        }
    }

  // 2. Check if we can write outside the sandbox (a strong indicator)
  NSError *error = nil;
    NSString *testPath = @"/private/jailbreaktest.txt";
    [@"test" writeToFile:testPath atomically:YES encoding:NSUTF8StringEncoding error:&error];

    if (!error) {
        // If we can write, it's jailbroken.  Remove the file immediately.
        [[NSFileManager defaultManager] removeItemAtPath:testPath error:nil];
        NSLog(@"Sandbox write test indicates jailbreak.");
        return YES;
    }
    //3: Check for existence of the symbolic link /Applications.
     struct stat s;
    if (lstat("/Applications", &s) == 0) {
      if (S_ISLNK(s.st_mode)) {
            NSLog(@"Symbolic link /Applications exists, jailbroken");
          return YES;
      }
    }

  //4:Check for dyld Libraries
    uint32_t count = _dyld_image_count();
    for (uint32_t i = 0 ; i < count ; ++i) {
        const char *dyld =  _dyld_get_image_name(i);
      if (strstr(dyld, "MobileSubstrate") != NULL || strstr(dyld, "libcycript") != NULL) {
            NSLog(@"Suspicious dyld library found: %s", dyld);
          return YES;
      }
    }

  //5 Check if fork() is available.  Sandboxed apps should not be able to call fork().
    #if !TARGET_IPHONE_SIMULATOR // Don't run this on the simulator
        int pid = fork();
        if (pid == 0)
        {
          //in child, exit immediately to avoid crashing.
          exit(0);
        } else if (pid > 0)
        {
          NSLog(@"Fork available. Likely jailbroken.");
          return YES; // Fork succeeded; likely jailbroken
        }
    #endif

    return NO; // No jailbreak indicators found
}
+ (BOOL)isDebuggerAttached {
    int mib[4];
    struct kinfo_proc info;
    size_t size = sizeof(info);
    
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();
    
    sysctl(mib, 4, &info, &size, NULL, 0);
    
    return (info.kp_proc.p_flag & P_TRACED) != 0;
}

// Check for Frida's default port
+ (BOOL)isFridaListeningOnDefaultPort {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        return NO; // Couldn't create socket
    }

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(27042);  // Default Frida port
    inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);

    if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1) {
        NSLog(@"Frida default port (27042) is open.");
        close(sock);
        return YES;
    }

    close(sock);
    return NO;
}


+ (BOOL)isFridaDetected {
    int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
    size_t length;
    struct kinfo_proc *procs, *proc;

    sysctl(name, 3, NULL, &length, NULL, 0);
    procs = malloc(length);
    sysctl(name, 3, procs, &length, NULL, 0);

    for (proc = procs; (char *)proc < (char *)procs + length; proc++) {
        if (strstr(proc->kp_proc.p_comm, "frida") != NULL) {
            free(procs);
            return YES;
        }
    }

    free(procs);
    return NO;
}
// Check for loaded dylibs that indicate Frida
+(BOOL) checkForFridaDylib{
    uint32_t count = _dyld_image_count();
    for (uint32_t i = 0 ; i < count ; ++i) {
        const char *dyld =  _dyld_get_image_name(i);
        if (strstr(dyld, "FridaGadget") != NULL) { // Look for FridaGadget
              NSLog(@"FridaGadget dylib found: %s", dyld);
              return YES;
        }
    }
    return NO;
}

// Check process name
+ (BOOL)checkProcessName {
    NSString* processName = [[NSProcessInfo processInfo] processName];
  if ([processName containsString:@"Frida"]) {
      return YES;
    }
    return NO;
}

// A combined check for jailbreak and Frida
+ (BOOL)isDeviceCompromised {
    if ([SecurityChecks isDeviceJailbroken]) {
        return YES;
    }
    if ([SecurityChecks isFridaListeningOnDefaultPort]) {
        return YES;
    }
    if ([SecurityChecks checkForFridaDylib]) {
        return YES;
    }
    if([SecurityChecks checkProcessName]){
        return YES;
    }
//    if ([SecurityChecks isDebuggerAttached]) {
//    return YES;
//    }
//    if ([SecurityChecks isFridaDetected]) {
//    return YES;
//    }

    return NO;
}

@end
