返回

Swift的开端:打造Alamofire抓包工具的初始步骤

IOS

#Swift的开端:打造Alamofire抓包工具的初始步骤

了解Swift Runtime是跨越Swift开发难题的一项必备技能。加入我,利用Frida实现抓包工具,探究Swift HTTP库Alamofire的运作。

Swift Runtime是一个复杂而又全面的系统,它在很大程度上影响着Swift程序的行为。当涉及到网络通信时,Alamofire是一个很受欢迎的HTTP库,它为HTTP请求提供了便捷的方法。然而,Alamofire并不能提供抓包的功能,这使得调试和分析网络通信变得困难。

本文将介绍如何利用Frida实现一个简单的Alamofire抓包工具,这个工具可以打印GET/POST请求,并屏蔽SSL Pinning。

工具及环境

  • Xcode 13
  • Swift 5.5
  • Frida 16.1.10
  • python 3.9.12

创建Frida脚本

首先,我们需要创建一个Frida脚本来注入到Alamofire进程中。这个脚本将负责拦截网络请求并打印请求信息。

# Frida脚本
import frida

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] Request:")
        print("   Method:", message['payload']['method'])
        print("   URL:", message['payload']['url'])
        print("   Headers:")
        for header in message['payload']['headers']:
            print("      ", header, ":", message['payload']['headers'][header])
        print("   Body:")
        print("      ", message['payload']['body'])

    elif message['type'] == 'reply':
        print("[*] Response:")
        print("   Status:", message['payload']['status_code'])
        print("   Headers:")
        for header in message['payload']['headers']:
            print("      ", header, ":", message['payload']['headers'][header])
        print("   Body:")
        print("      ", message['payload']['body'])

# Attach to the Alamofire process
process = frida.get_usb_device().attach("Alamofire")
script = process.create_script("""
    var URLRequest = ObjC.classes.NSURLRequest;
    var NSURLConnection = ObjC.classes.NSURLConnection;
    
    URLRequest.prototype.original_initWithURL = URLRequest.prototype['initWithURL:cachePolicy:timeoutInterval:'];
    URLRequest.prototype.swizzled_initWithURL = function(url, cachePolicy, timeoutInterval) {
        send({'type': 'send', 'payload': {
            'url': url.toString(),
            'method': this.HTTPMethod(),
            'headers': this.allHTTPHeaderFields(),
            'body': this.HTTPBody()
        }});
        
        return this.original_initWithURL.apply(this, arguments);
    };
    URLRequest.prototype.initWithURL = URLRequest.prototype.swizzled_initWithURL;
    
    NSURLConnection.prototype.original_sendAsynchronousRequest = NSURLConnection.prototype['sendAsynchronousRequest:queue:completionHandler:'];
    NSURLConnection.prototype.swizzled_sendAsynchronousRequest = function(request, queue, completionHandler) {
        var task = this.original_sendAsynchronousRequest.apply(this, arguments);
        task.original_completionHandler = completionHandler;
        task.swizzled_completionHandler = function(response, data, error) {
            send({'type': 'reply', 'payload': {
                'status_code': response.statusCode(),
                'headers': response.allHeaderFields(),
                'body': data
            }});
            
            this.original_completionHandler.apply(this, arguments);
        };
        task.completionHandler = task.swizzled_completionHandler;
        
        return task;
    };
    NSURLConnection.prototype.sendAsynchronousRequest = NSURLConnection.prototype.swizzled_sendAsynchronousRequest;
""")
script.on('message', on_message)
script.load()

# Start the Frida server
frida.run()

使用抓包工具

接下来,我们需要使用Frida服务器来注入脚本到Alamofire进程中。

  1. 打开终端,进入Frida服务器所在的目录。
  2. 执行以下命令来启动Frida服务器:
frida-server
  1. 打开Xcode,创建一个新的Swift项目。
  2. 在项目中导入Alamofire库。
  3. 在项目中添加以下代码:
import Alamofire

Alamofire.request("https://example.com") { (response, data, error) in
    print(response)
    print(data)
    print(error)
}
  1. 运行项目。

  2. 在终端中,你会看到Frida服务器打印出请求和响应的信息。

屏蔽SSL Pinning

Alamofire默认情况下会对HTTPS请求进行SSL Pinning,这可以防止中间人攻击。然而,在某些情况下,我们可能需要屏蔽SSL Pinning。我们可以通过修改Frida脚本来实现这一点。

# Frida脚本
import frida

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] Request:")
        print("   Method:", message['payload']['method'])
        print("   URL:", message['payload']['url'])
        print("   Headers:")
        for header in message['payload']['headers']:
            print("      ", header, ":", message['payload']['headers'][header])
        print("   Body:")
        print("      ", message['payload']['body'])

    elif message['type'] == 'reply':
        print("[*] Response:")
        print("   Status:", message['payload']['status_code'])
        print("   Headers:")
        for header in message['payload']['headers']:
            print("      ", header, ":", message['payload']['headers'][header])
        print("   Body:")
        print("      ", message['payload']['body'])

# Attach to the Alamofire process
process = frida.get_usb_device().attach("Alamofire")
script = process.create_script("""
    var URLRequest = ObjC.classes.NSURLRequest;
    var NSURLConnection = ObjC.classes.NSURLConnection;
    
    URLRequest.prototype.original_initWithURL = URLRequest.prototype['initWithURL:cachePolicy:timeoutInterval:'];
    URLRequest.prototype.swizzled_initWithURL = function(url, cachePolicy, timeoutInterval) {
        send({'type': 'send', 'payload': {
            'url': url.toString(),
            'method': this.HTTPMethod(),
            'headers': this.allHTTPHeaderFields(),
            'body': this.HTTPBody()
        }});
        
        return this.original_initWithURL.apply(this, arguments);
    };
    URLRequest.prototype.initWithURL = URLRequest.prototype.swizzled_initWithURL;
    
    NSURLConnection.prototype.original_sendAsynchronousRequest = NSURLConnection.prototype['sendAsynchronousRequest:queue:completionHandler:'];
    NSURLConnection.prototype.swizzled_sendAsynchronousRequest = function(request, queue, completionHandler) {
        var task = this.original_sendAsynchronousRequest.apply(this, arguments);
        task.original_completionHandler = completionHandler;
        task.swizzled_completionHandler = function(response, data, error) {
            send({'type': 'reply', 'payload': {
                'status_code': response.statusCode(),
                'headers': response.allHeaderFields(),
                'body': data
            }});
            
            this.original_completionHandler.apply(this, arguments);
        };
        task.completionHandler = task.swizzled_completionHandler;
        
        return task;
    };
    NSURLConnection.prototype.sendAsynchronousRequest = NSURLConnection.prototype.swizzled_sendAsynchronousRequest;
    
    Interceptor.attachLoad()
""")
script.on('message', on_message)
script.load()

# Start the Frida server
frida.run()

然后,我们需要重新运行项目。在终端中,你会看到Frida服务器打印出请求和响应的信息,即使对于HTTPS请求也是如此。

结语

本文介绍了如何利用Frida实现一个简单的Alamofire抓包工具,这个工具可以打印GET/POST请求,并屏蔽SSL Pinning。希望本文能对你有所帮助。