25-3/12/2
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
C624C4672BD56D34002834AF /* promo.wav in Resources */ = {isa = PBXBuildFile; fileRef = C624C45F2BD56D34002834AF /* promo.wav */; };
|
||||
C624C4682BD56D34002834AF /* cancel.wav in Resources */ = {isa = PBXBuildFile; fileRef = C624C4602BD56D34002834AF /* cancel.wav */; };
|
||||
C628BC412C316B1200E4D33B /* ding.wav in Resources */ = {isa = PBXBuildFile; fileRef = C628BC402C316B1100E4D33B /* ding.wav */; };
|
||||
C6372AD92D81BDE000C44265 /* SecurityChecks.m in Sources */ = {isa = PBXBuildFile; fileRef = C6372AD82D81BDE000C44265 /* SecurityChecks.m */; };
|
||||
C6372ADF2D81C68C00C44265 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C6372ADE2D81C68700C44265 /* Localizable.strings */; };
|
||||
C6B15AA12B5FB24600746405 /* order.wav in Resources */ = {isa = PBXBuildFile; fileRef = C6B15A9F2B5FB24600746405 /* order.wav */; };
|
||||
C6B15AA22B5FB24600746405 /* tone2.wav in Resources */ = {isa = PBXBuildFile; fileRef = C6B15AA02B5FB24600746405 /* tone2.wav */; };
|
||||
C6B450142D67556C001427AA /* Constants1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B450132D67556B001427AA /* Constants1.swift */; };
|
||||
@@ -80,6 +82,9 @@
|
||||
C624C45F2BD56D34002834AF /* promo.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = promo.wav; path = ../../android/app/src/main/res/raw/promo.wav; sourceTree = "<group>"; };
|
||||
C624C4602BD56D34002834AF /* cancel.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = cancel.wav; path = ../../android/app/src/main/res/raw/cancel.wav; sourceTree = "<group>"; };
|
||||
C628BC402C316B1100E4D33B /* ding.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = ding.wav; path = ../../android/app/src/main/res/raw/ding.wav; sourceTree = "<group>"; };
|
||||
C6372AD72D81BDA900C44265 /* SecurityChecks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecurityChecks.h; sourceTree = "<group>"; };
|
||||
C6372AD82D81BDE000C44265 /* SecurityChecks.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SecurityChecks.m; sourceTree = "<group>"; };
|
||||
C6372ADE2D81C68700C44265 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
||||
C69588332A8C1F6B00C3AC67 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
C6B15A9F2B5FB24600746405 /* order.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = order.wav; sourceTree = "<group>"; };
|
||||
C6B15AA02B5FB24600746405 /* tone2.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = tone2.wav; sourceTree = "<group>"; };
|
||||
@@ -164,7 +169,10 @@
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6372ADE2D81C68700C44265 /* Localizable.strings */,
|
||||
C6229B672D610C970014397D /* JailbreakDetection.swift */,
|
||||
C6372AD72D81BDA900C44265 /* SecurityChecks.h */,
|
||||
C6372AD82D81BDE000C44265 /* SecurityChecks.m */,
|
||||
C69588332A8C1F6B00C3AC67 /* Runner.entitlements */,
|
||||
C6B15A9F2B5FB24600746405 /* order.wav */,
|
||||
C6B15AA02B5FB24600746405 /* tone2.wav */,
|
||||
@@ -301,6 +309,7 @@
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
C628BC412C316B1200E4D33B /* ding.wav in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
C6372ADF2D81C68C00C44265 /* Localizable.strings in Resources */,
|
||||
C624C4642BD56D34002834AF /* tone1.mp3 in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
C60CA7372BE9EF38002095B2 /* GoogleService-Info.plist in Resources */,
|
||||
@@ -435,6 +444,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C6229B682D610CB20014397D /* JailbreakDetection.swift in Sources */,
|
||||
C6372AD92D81BDE000C44265 /* SecurityChecks.m in Sources */,
|
||||
C6B450142D67556C001427AA /* Constants1.swift in Sources */,
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
|
||||
@@ -1,40 +1,175 @@
|
||||
//import UIKit
|
||||
//import Flutter
|
||||
//import FirebaseCore
|
||||
//import GoogleMaps
|
||||
//
|
||||
//@main
|
||||
//@objc class AppDelegate: FlutterAppDelegate {
|
||||
// override func application(
|
||||
// _ application: UIApplication,
|
||||
// didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
// ) -> Bool {
|
||||
// GMSServices.provideAPIKey(Constants.googleMapsAPIKey)
|
||||
// FirebaseApp.configure()
|
||||
// GeneratedPluginRegistrant.register(with: self)
|
||||
//
|
||||
// if JailbreakDetection.isJailbroken() {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
// self.showJailbreakAlert()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
// }
|
||||
//
|
||||
// func showJailbreakAlert() {
|
||||
// guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { return }
|
||||
//
|
||||
// let alert = UIAlertController(
|
||||
// title: "تحذير أمني",
|
||||
// message: "تم اكتشاف أن جهازك يحتوي على جيلبريك. لا يمكن تشغيل التطبيق على هذا الجهاز.",
|
||||
// preferredStyle: .alert
|
||||
// )
|
||||
//
|
||||
// alert.addAction(UIAlertAction(title: "إنهاء التطبيق", style: .destructive) { _ in
|
||||
// exit(0)
|
||||
// })
|
||||
//
|
||||
// rootVC.present(alert, animated: true, completion: nil)
|
||||
// }
|
||||
//}
|
||||
import UIKit
|
||||
import Flutter
|
||||
import FirebaseCore
|
||||
import GoogleMaps
|
||||
|
||||
@main
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
|
||||
// Load API keys from a secure location (NOT in plain text!)
|
||||
// See the "Secure API Key Storage" section below.
|
||||
GMSServices.provideAPIKey(Constants.googleMapsAPIKey)
|
||||
FirebaseApp.configure()
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
if JailbreakDetection.isJailbroken() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
self.showJailbreakAlert()
|
||||
}
|
||||
// Perform security checks *before* showing the main UI
|
||||
if SecurityChecks.isDeviceCompromised() { // Call Objective-C method
|
||||
showSecurityAlert()
|
||||
return false // Prevent app from launching further
|
||||
}
|
||||
//setup method channel
|
||||
setupMethodChannel()
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
func showJailbreakAlert() {
|
||||
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { return }
|
||||
func setupMethodChannel(){
|
||||
guard let controller = window?.rootViewController as? FlutterViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let channel = FlutterMethodChannel(name: "com.mobileapp.store.ride/security",
|
||||
binaryMessenger: controller.binaryMessenger)
|
||||
channel.setMethodCallHandler { call, result in
|
||||
switch call.method {
|
||||
case "isNativeRooted":
|
||||
// We already check this in isDeviceCompromised,
|
||||
// so we can just return the result directly.
|
||||
result(SecurityChecks.isDeviceCompromised())
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func showSecurityAlert() {
|
||||
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else {
|
||||
// Handle the case where rootVC is nil (extremely unlikely, but good practice)
|
||||
exit(0)
|
||||
return
|
||||
}
|
||||
|
||||
let alert = UIAlertController(
|
||||
title: "تحذير أمني",
|
||||
message: "تم اكتشاف أن جهازك يحتوي على جيلبريك. لا يمكن تشغيل التطبيق على هذا الجهاز.",
|
||||
title: "Security Warning".localized, // Use localized strings!
|
||||
message: "Compromised device detected. Exiting.".localized,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
alert.addAction(UIAlertAction(title: "إنهاء التطبيق", style: .destructive) { _ in
|
||||
exit(0)
|
||||
// Add an "OK" button with an obfuscated action
|
||||
alert.addAction(UIAlertAction(title: "OK".localized, style: .destructive) { _ in
|
||||
// More robust exit (see explanation below)
|
||||
self.obfuscatedExit()
|
||||
})
|
||||
|
||||
rootVC.present(alert, animated: true, completion: nil)
|
||||
// Use a presentationDetents to prevent dismissing
|
||||
if #available(iOS 15.0, *) {
|
||||
alert.popoverPresentationController?.sourceView = rootVC.view
|
||||
alert.popoverPresentationController?.sourceRect = CGRect(x: rootVC.view.bounds.midX, y: rootVC.view.bounds.midY, width: 0, height: 0)
|
||||
alert.popoverPresentationController?.permittedArrowDirections = []
|
||||
alert.presentationController?.delegate = self
|
||||
if let presentationController = alert.presentationController as? UISheetPresentationController {
|
||||
presentationController.detents = [.medium()] // Set a fixed height if desired
|
||||
presentationController.preferredCornerRadius = 20;
|
||||
presentationController.prefersEdgeAttachedInCompactHeight = true;// Set corner radius
|
||||
presentationController.prefersGrabberVisible = false // Optional: show a grabber
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
|
||||
rootVC.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
// Obfuscated Exit (Example - you'll need a more robust solution)
|
||||
func obfuscatedExit() {
|
||||
// This is a *very* basic example. A real obfuscation would be MUCH more complex.
|
||||
let selector = NSSelectorFromString(String(format: "%@%@", "ex", "it:"))
|
||||
if responds(to: selector) {
|
||||
perform(selector, with: 0) // Call exit(0) indirectly
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Secure API Key Storage (Example)
|
||||
func getAPIKey() -> String {
|
||||
// DO NOT store API keys directly in your code!
|
||||
// Load it from a more secure location, such as:
|
||||
// 1. A configuration file that is *not* checked into source control.
|
||||
// 2. Keychain Services (recommended for iOS).
|
||||
// 3. A backend server (best option).
|
||||
|
||||
// This is just a placeholder. Replace with your actual key loading.
|
||||
// In a real app, you'd fetch this from a secure source.
|
||||
guard let filePath = Bundle.main.path(forResource: "keys", ofType: "plist"),
|
||||
let plist = NSDictionary(contentsOfFile: filePath),
|
||||
let apiKey = plist["google_maps_api_key"] as? String else {
|
||||
fatalError("Couldn't find keys or API Key") // Ensure a crash if not found
|
||||
}
|
||||
|
||||
return apiKey
|
||||
}
|
||||
|
||||
}
|
||||
// Add for prevent dismissing
|
||||
extension AppDelegate: UIAdaptivePresentationControllerDelegate {
|
||||
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Add String extension
|
||||
extension String {
|
||||
var localized: String {
|
||||
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
10
ios/Runner/Localizable.strings
Normal file
10
ios/Runner/Localizable.strings
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Localizable.strings
|
||||
// Runner
|
||||
//
|
||||
// Created by Hamza Aleghwairyeen on 12/03/2025.
|
||||
//
|
||||
|
||||
"Security Warning" = "تحذير أمني";
|
||||
"Compromised device detected. Exiting." = "تم اكتشاف جهاز مُخترق. جارٍ الخروج.";
|
||||
"OK" = "موافق";
|
||||
@@ -1 +1,2 @@
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
#import "SecurityChecks.h"
|
||||
|
||||
24
ios/Runner/SecurityChecks.h
Normal file
24
ios/Runner/SecurityChecks.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// SecurityChecks.h
|
||||
// Runner
|
||||
//
|
||||
// Created by Hamza Aleghwairyeen on 12/03/2025.
|
||||
//
|
||||
|
||||
//#ifndef SecurityChecks_h
|
||||
//#define SecurityChecks_h
|
||||
//
|
||||
//
|
||||
//#endif /* SecurityChecks_h */
|
||||
|
||||
|
||||
|
||||
// SecurityChecks.h
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h> // Import UIKit
|
||||
|
||||
@interface SecurityChecks : NSObject
|
||||
|
||||
+ (BOOL)isDeviceCompromised; // Combined check
|
||||
|
||||
@end
|
||||
157
ios/Runner/SecurityChecks.m
Normal file
157
ios/Runner/SecurityChecks.m
Normal file
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// SecurityChecks.m
|
||||
// Runner
|
||||
//
|
||||
// Created by Hamza Aleghwairyeen on 12/03/2025.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
|
||||
#import "SecurityChecks.h"
|
||||
#import <sys/stat.h>
|
||||
#include <dlfcn.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
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user