Skip to content
78 changes: 78 additions & 0 deletions demos/ios/MASVS-RESILIENCE/MASTG-DEMO-0091/MASTG-DEMO-0091.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
platform: ios
title: Frida Detection and Bypass Techniques
code: [swift]
tools: [MASTG-TOOL-0031]
id: MASTG-DEMO-0091
test: MASTG-TEST-0298
---

### Sample

The code snippet below shows sample code that implements Frida detection mechanisms including dynamic library scanning, port detection, thread count analysis, and file system artifact detection.

{{ MastgTest.swift }}

### Steps

1. Build and install the MASTestApp on an iOS simulator or jailbroken device.
2. Run the app without Frida to observe normal behavior (no detection).
3. Attach Frida to the running app using `frida -U -n MASTestApp` or `frida -p <PID>`.
4. Press the "Start" button in the app to trigger detection mechanisms.
5. Observe the security alerts indicating Frida detection.
6. Use the bypass script to demonstrate how detection can be circumvented.

{{ run.sh }}

{{ frida-bypass.js }}

### Observation

The output demonstrates multiple detection results:

**Without Frida:**

```text
✅ No Frida detected - App is running normally
ℹ️ Current thread count: 6

```

**With Frida Attached:**

```text
🚨 SECURITY ALERT!

Frida Detection Results:
1. Frida library detected in memory
2. Frida server port (27042) is open
3. Suspicious thread count detected

```

**With Bypass Script:**

```text
[*] MASTG-DEMO-0091: Frida Detection Bypass
[+] Bypass 1: Hiding Frida libraries
[+] Bypass 2: Blocking Frida port detection
[+] Bypass 3: Normalizing thread count
[✓] All bypasses active!

✅ No Frida detected - App is running normally

```

{{ output.txt }}

### Evaluation

The test demonstrates that:

1. **Detection Methods Work**: The app successfully detects Frida through multiple mechanisms (library scanning, port checking, thread analysis).

2. **Easy to Bypass**: All detection mechanisms can be trivially bypassed using Frida's Interceptor API with simple function hooks, requiring minimal effort and no custom code.

3. **Educational Value**: This demo illustrates why basic detection mechanisms provide limited security value, as they can be circumvented with straightforward hooking techniques.

The detection mechanisms tested here represent common but easily-defeated approaches. More sophisticated detection would require obfuscation, integrity checks, and anti-hooking measures.
Binary file not shown.
174 changes: 174 additions & 0 deletions demos/ios/MASVS-RESILIENCE/MASTG-DEMO-0091/MastgTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import Foundation
import Darwin
import MachO

class MastgTest {

static func mastgTest() -> String {
print("[MASTG-TEST-0091] Testing Frida Detection")

var detections: [String] = []

// Method 1: Check for Frida libraries in memory
if isFridaLibraryLoaded() {
detections.append("Frida library detected in memory")
}

// Method 2: Check for Frida's default port
if isFridaPortOpen() {
detections.append("Frida server port (27042) is open")
}

// Method 3: Check for suspicious thread count
if hasSuspiciousThreadCount() {
detections.append("Suspicious thread count detected")
}

// Method 4: Check for Frida-related named pipes/sockets
if hasFridaArtifacts() {
detections.append("Frida artifacts detected")
}

if detections.isEmpty {
let result = "✅ No Frida detected - App is running normally"
print(result)
return result
} else {
let result = "🚨 SECURITY ALERT!\n\nFrida Detection Results:\n" +
detections.enumerated().map { "\($0.offset + 1). \($0.element)" }.joined(separator: "\n")
print(result)
return result
}
}

// MARK: - Detection Method 1: Check for Frida Libraries

private static func isFridaLibraryLoaded() -> Bool {
let fridaStrings = [
"frida",
"gadget",
"frida-agent",
"libfrida"
]

// Get the number of loaded images (dylibs)
let imageCount = _dyld_image_count()

for i in 0..<imageCount {
// Get the name of each loaded image
if let imageName = _dyld_get_image_name(i) {
let name = String(cString: imageName).lowercased()

for fridaString in fridaStrings {
if name.contains(fridaString) {
print("⚠️ Frida library found: \(name)")
return true
}
}
}
}

return false
}

// MARK: - Detection Method 2: Check Frida Port

private static func isFridaPortOpen() -> Bool {
let fridaPorts: [UInt16] = [27042, 27043] // Default and alternate Frida ports

for port in fridaPorts {
if isPortOpen(port: port) {
print("⚠️ Frida port \(port) is open")
return true
}
}

return false
}

private static func isPortOpen(port: UInt16) -> Bool {
let sockfd = socket(AF_INET, SOCK_STREAM, 0)
guard sockfd != -1 else {
return false
}

defer {
close(sockfd)
}

var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = port.bigEndian
addr.sin_addr.s_addr = inet_addr("127.0.0.1")

let result = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
connect(sockfd, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
}
}

return result == 0
}

// MARK: - Detection Method 3: Check Thread Count

private static func hasSuspiciousThreadCount() -> Bool {
var threadList: thread_act_array_t?
var threadCount: mach_msg_type_number_t = 0

let result = task_threads(mach_task_self_, &threadList, &threadCount)

guard result == KERN_SUCCESS else {
return false
}

defer {
if let list = threadList {
vm_deallocate(
mach_task_self_,
vm_address_t(bitPattern: list),
vm_size_t(threadCount) * vm_size_t(MemoryLayout<thread_t>.size)
)
}
}

print("ℹ️ Current thread count: \(threadCount)")

// Normal iOS app typically has 4-8 threads
// Frida injection usually adds 3-5+ threads
if threadCount > 12 {
print("⚠️ Suspicious thread count: \(threadCount) (expected < 12)")
return true
}

return false
}

// MARK: - Detection Method 4: Check for Frida Artifacts

private static func hasFridaArtifacts() -> Bool {
// Check for Frida's named pipes/sockets in /tmp
let fridaPaths = [
"/tmp",
"/var/tmp"
]

for basePath in fridaPaths {
if checkDirectoryForFrida(path: basePath) {
print("⚠️ Frida artifact found in: \(basePath)")
return true
}
}

return false
}

private static func checkDirectoryForFrida(path: String) -> Bool {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: path)
return contents.contains { $0.lowercased().contains("frida") }
} catch {
return false
}
}
}
93 changes: 93 additions & 0 deletions demos/ios/MASVS-RESILIENCE/MASTG-DEMO-0091/frida-bypass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// MASTG-DEMO-0091: Frida Detection Bypass
// This script demonstrates bypassing the detection mechanisms

console.log("[*] MASTG-DEMO-0091: Frida Detection Bypass");
console.log("=".repeat(50));

// Bypass 1: Hide Frida libraries from dyld
console.log("[+] Bypass 1: Hiding Frida libraries");

Interceptor.attach(Module.findExportByName(null, '_dyld_get_image_name'), {
onLeave: function(retval) {
if (retval.isNull()) return;

try {
var imageName = retval.readCString();
if (imageName && imageName.toLowerCase().includes('frida')) {
console.log(" [!] Hiding: " + imageName);
retval.replace(Memory.allocUtf8String('/System/Library/Frameworks/Foundation.framework/Foundation'));
}
} catch(e) {}
}
});

// Bypass 2: Block port detection
console.log("[+] Bypass 2: Blocking Frida port detection");

Interceptor.attach(Module.findExportByName(null, 'connect'), {
onEnter: function(args) {
try {
var sockaddr = args[1];
var port = Memory.readU16(sockaddr.add(2));
port = ((port & 0xFF) << 8) | ((port >> 8) & 0xFF);

if (port === 27042 || port === 27043) {
console.log(" [!] Blocked port check: " + port);
this.shouldFail = true;
}
} catch(e) {}
},
onLeave: function(retval) {
if (this.shouldFail) {
retval.replace(-1);
}
}
});

// Bypass 3: Normalize thread count
console.log("[+] Bypass 3: Normalizing thread count");

Interceptor.attach(Module.findExportByName(null, 'task_threads'), {
onLeave: function(retval) {
try {
if (retval.toInt32() === 0) {
var threadCountPtr = this.context.rdx; // x86_64
var actualCount = Memory.readU32(threadCountPtr);

if (actualCount > 12) {
console.log(" [!] Reducing thread count from " + actualCount + " to 8");
Memory.writeU32(threadCountPtr, 8);
}
}
} catch(e) {}
}
});

// Bypass 4: Hook mastgTest directly
console.log("[+] Bypass 4: Hooking mastgTest()");

if (ObjC.available) {
try {
var MastgTest = ObjC.classes.MastgTest;

if (MastgTest && MastgTest['+ mastgTest']) {
Interceptor.attach(MastgTest['+ mastgTest'].implementation, {
onLeave: function(retval) {
console.log(" [!] mastgTest() intercepted");
var cleanResult = ObjC.classes.NSString.stringWithString_(
"✅ No Frida detected - App is running normally"
);
retval.replace(cleanResult);
console.log(" [✓] Returning clean result");
}
});
console.log(" [✓] mastgTest() hooked successfully");
}
} catch(e) {
console.log(" [!] Error: " + e);
}
}

console.log("\n" + "=".repeat(50));
console.log("[✓] All bypasses active!");
console.log("=".repeat(50) + "\n");
36 changes: 36 additions & 0 deletions demos/ios/MASVS-RESILIENCE/MASTG-DEMO-0091/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
========== Test 1: Clean Run (No Frida) ==========
[MASTG-TEST-0091] Testing Frida Detection
ℹ️ Current thread count: 6
✅ No Frida detected - App is running normally

========== Test 2: With Frida Attached ==========
[MASTG-TEST-0091] Testing Frida Detection
⚠️ Frida library found: /usr/lib/frida/frida-agent.dylib
⚠️ Frida port 27042 is open
ℹ️ Current thread count: 15
⚠️ Suspicious thread count: 15 (expected < 12)
🚨 SECURITY ALERT!

Frida Detection Results:
1. Frida library detected in memory
2. Frida server port (27042) is open
3. Suspicious thread count detected

========== Test 3: With Bypass Script ==========
[*] MASTG-DEMO-0091: Frida Detection Bypass
==================================================
[+] Bypass 1: Hiding Frida libraries
[!] Hiding: /usr/lib/frida/frida-agent.dylib
[+] Bypass 2: Blocking Frida port detection
[!] Blocked port check: 27042
[+] Bypass 3: Normalizing thread count
[!] Reducing thread count from 15 to 8
[+] Bypass 4: Hooking mastgTest()
[✓] Modified result: No Frida detected
==================================================
[✓] All bypasses active!
==================================================

[MASTG-TEST-0091] Testing Frida Detection
ℹ️ Current thread count: 8
✅ No Frida detected - App is running normally
Loading
Loading