Hook入门与抓包_集群智慧网络安全云
全国客户服务热线:4006-054-001 疑难解答:159-9855-7370(7X24受理投诉、建议、合作、售前咨询),173-0411-9111(售前),155-4267-2990(售前),座机/传真:0411-83767788(售后),微信咨询:543646
企业服务导航

Hook入门与抓包

发布日期:2024-05-19 浏览次数: 专利申请、商标注册、软件著作权、资质办理快速响应热线:4006-054-001 微信:15998557370


Hook入门与抓包

Frida介绍 Frida 的 介 绍 是 “Frida 是 平 台 原 生 App 的“Greasemonkey”,专业一点来说就是一种动态插桩工具,可以插入一些代码到原生App的内存空间去动态地监视和修改其行为,这些原生平台可以是Windows、Mac、Linux、Android或者iOS,同时Frida还是开源的。 Frida通过将JavaScript脚本插入到App的内存中来对App的逻辑进行跟踪和监控,甚至重新修改程序的逻辑,实现逆向开发和分析人员想要实现的功能,这样的方式也可以称为Hook(钩住,即通过钩子机制与钩子函数建立关联)。 Frida安装 客户端(电脑主机)安装直接使用命令: pip3 install frida==12.8.0pip3 install frida-tools==5.3.0   服务端(手机端)安装 在GitHub上找到与客户端版本相同且与手机硬件架构相同的服务端进行下载,下载后上传到手机/data/local/tmp 目录下,添加可执行权限,进行运行即可。 adb push fridaadb shellsucd /data/local/tmpchmod 777 frida./frida   Frida操作模式 ◆CLI(命令行)模式 CLI模式直接通过命令行将JS脚本注入到进程中,对APP进程进行操作。 ◆RPC远程调用模式 RPC模式又称为远程过程调用模式,这种模式利用Python对Hook的Js脚本进行包装,但实际上对进程进行操作的还是Js脚本。 Frida注入注入APP进程的方式(attach/spawn) ◆attch(附加)模式 这种模式是建立在目标App已经启动的情况下,frida直接利用ptrace原理将Hook的Js脚本注入程序而完成Hook操作。 frida -U APP进程 -l HookJs脚本.js   ◆Spawn(调用)模式 这种模式将启动App的权力交由Frida来控制,当使用此模式时,即使App已经启动还是会将App重新启动并注入Hook的JS脚本。 frida -U -f App进程 -l HookJs脚本.js --no-pause   Frida Java层Hook 基础API ◆setTimeout(指定注入的函数,延迟时间毫秒) setTimeout函数本身是JS提供的一个函数,通过这个函数可以将FridaHook脚本的函数延迟注入到目标进程,在App有加固情况下比较好用比如: Root检测、Wifi代理检测。 ◆setImmediate(指定注入的函数) 这个函数和setTimeout()函数类似,都是用于指定要执行的函数,不同的是setTimeout可以用于指定Frida注入App多长时间后执行函数,往往用于延时注入。 ◆Java.perform(function) Java.perform() 函数表示将参数中的函数注入到Java运行时,如果没有该函数的包裹注入目标App进程会提示如下图的报错信息: ◆Java.use(类完整路径) 从内存中获取指定类(包名.类名)的handle句柄,相当于Java中通过反射机制获取到的Class对象。 Frida Hook Java层demo 这段代码表示每秒调用一次show()方法,其中一个方法没有返回值和参数,另一个方法有返回值和参数。 package com.example.demo02;import android.os.Bundle;import android.util.Log;import androidx.appcompat.app.AppCompatActivity;public class example extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_choose); while (true){ try { Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } show(); show("---parameter---"); } } public static void show(){ Log.d("---no parameter---", "show方法日志打印"); } public static boolean show(String tag){ Log.d(tag, "show方法日志打印"); return true; }}   Hook 该程序show方法的Frida脚本。 function main(){ // Java.perform() 函数表示将参数中的函数注入到Java运行时 Java.perform(function(){ // Java.use() 函数表示从内存中获取指定类(包名.类名)的handle句柄 var MainActivity = Java.use('com.example.demo02.example') // hook show()重载方法 MainActivity.show.overload().implementation = function(){ // 在控制台打印"Hook show()" console.log("Hook show()") this.show() } // hook show(tag) 重载方法 MainActivity.show.overload("java.lang.String").implementation = function(tag){ // 修改APP内存中的值 var result = this.show("Hook") // // 获取 Hook 到方法的返回值并在控制台打印 var AppTag = this.show(tag) console.log("show(tag) retval => ", AppTag) // 返回修改后的值(Hook) return result } } )}// 指定要注入到APP进程的函数setImmediate(main)   执行如下命令: frida -U com.example.demo02 -l 6_example.js   二   基于Frida开发的动态分析工具Objection  Objection集成的功能主要支持Android和iOS两大移动平台。在对Android的支持中,Objection可以快速完成诸如内存搜索、类和模块搜索、方法Hook以及打印参数、返回值、调用栈等常用功能,是一个非常方便的逆向必备工具和内存漫游神器。 使用命令安装: pip3 install -U objection   Objection默认通过USB连接设备,不必和Frida的命令行一样通过-U参数指定USB模式连接,主要通过-g参数指定注入的进程并通过explore命令进入REPL模式。在进入REPL模式后便可以使用Objection进行Hook的常用命令。 objection -g com.roysue.httpurlconnectiondemo(包名) explore   常用基础命令: jobs命令:作业系统很好用,用于查看和管理当前所执行Hook的任务 查看当前hook任务: jobs list 终止hook任务: jobs kill id内存漫游相关命令: 列出当前内存中所有类:android hooking list classes 搜索内存中包含关键字的类: android hooking search class 关键字 搜索内存中包含关键字的方法: android hooking search methods 关键字 列出指定类中的所有方法: android hooking list class_methods 类完整路径 列出当前APP四大组件: android hooking list activities|services|receivers|providersHook相关命令: Hook指定的方法: android hooking watch class_method 方法名称 --dump-args --dump-backtrace --dump-return Hook某个类的所有方法: android hooking watch class 类名   三   root检测绕过  方法一 使用jadx反编译工具反编译apk文件,搜索root检测相关代码。 找到包名为目标APP包名的那个方法,跟踪代码。 然后根据root检测逻辑编写绕过检测的js代码,具体代码略。 方法二 上面的方法有个缺点就是只能针对特定的APP绕过,下面介绍通用的绕过方式。 直接Hook java.io.FIle类的exists方法,这个方式的好处是: 1.其他APP不敢对这个类进行混淆。 2.这个类一定存在于内存中无需延迟注入。 仔细观察方法一种root检测部分截图的代码,发现使用的是Java基础类File的exists()方法进行检测。 因此我们可以直接对File进行Hook,代码如下: function main(){ Java.perform(function(){ var File = Java.use("java.io.File") // 获取java.io.File File.exists.implementation = function(){ // Hook exists方法并获取path值 var path = this.path.value; // 获取path值 var result = this.exists() // 获取exists()原始值 // console.log(path) if(path == "/system/bin/su" || path == "/system/xbin/su" || path == "/sbin/su" || path == "/vendor/bin/su"){ result = false // 将返回值设为false代表未获取root } return result } })}setImmediate(main)   四   Hook HTTP/S网络请求框架  安卓开发中常用的HTTP/S网络请求框架 ◆HttpURLConnection HttpURLConnection属于原生的网络通信库,从Android 5(2014年)开始,Android官方不再推荐使用HttpClient。Android 6.0的SDK中去掉了HttpClient的支持。在Android 9之后,Android更是彻底取消了对HTTPClient的支持,因此可以说原生的网络通信库就只剩下了HttpURLConnection。 ◆OKHTTP 因为网络通信的操作涉及异步、多线程和效率等问题,在HttpURLConnection中并未对这些操作进行完整的封装,而是交给了开发者去完成,因此就出现了第二类网络通信框架——第三方HTTP(S)网络请求框架。okhttp 是大名鼎鼎的Square公司的开源网络请求框架,有2、3、4几个大版 本,目前主流使用的是okhttp3。相比HttpUrlConnection,okhttp3更加优雅和高效,大部分Android第三方网络框架(比如Retrofit2网络通信框架)也都是基于okhttp3的再封装。 另外,还有Volley网络通信框架,它是在2013年 的Google I/O大会上被推出的基于HttpUrlConnection的一款异步 网络请求框架和图片加载框架,Volley特别适合数据量小、通信频繁的网络操作。 HttpURLConnection基础开发流程 1.通过传入目标网络地址来新建一个URL对象,然后通过 openConnection()函数获取一个HttpURLConnection实例。 2.获取到HttpURLConnection实例后,按照HTTP建立连接的流程设置HTTP请求头和参数信息例如: setRequestMethod()函数设置HTTP请求方法 setRequestProperty() 设置请求参数 setConnectionTimeout() 函数设置连接超时时间 setReadTimeout()函数设置接收超时时间 3.调用HttpURLConnection实例的getInputStream()方法与服务器连接并获取到服务器返回的输入流,对输入流完成读取,最终调用disconnection()方法将HTTP连接关闭掉。 实例: package com.roysue.httpurlconnectiondemo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import java.nio.charset.StandardCharsets;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { while (true){ try { // 通过url字符串创建一个URL对象 URL url = new URL("https://www.baidu.com"); // URL对象的openConnection()方法获取HttpURLConnection对象的实例 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置网络请求头部参数 connection.setRequestMethod("GET"); connection.setRequestProperty("token","r0ysue666"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); connection.connect(); // 开始连接 // 通过HttpURLConnection对象实例的getInputStream()方法获取网络请求响应的数据流 InputStream in = connection.getInputStream(); //if(in.available() > 0){ // 每次写入1024字节 int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; StringBuffer sb = new StringBuffer(); while ((in.read(buffer)) != -1) { sb.append(new String(buffer)); } Log.d("r0ysue666", sb.toString()); // 关闭HTTP网络请求 connection.disconnect(); // } } catch (IOException e) { e.printStackTrace(); } try { Thread.sleep(10*1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }}   HttpURLConnection“自吐”脚本开发 从HttpURLConnection基础开发流程我们发现几个关键的收发包函数: 1.URL类的构造函数,其包含了目标网址的字符串。 2.setRequestMethod()和setRequestProperty()方法设置请求头和请求参数等信息。 3.getInputStream()方法获取response服务器响应数据。 利用Objection动态分析HttpURLConnection 1.URL类的构造函数,其包含了目标网址的字符串。 Objection hook java.net.HttpURL类构造函数的方法。 2.setRequestMethod()和setRequestProperty()方法设置请求头和请求参数等信息。 Hook setRequestProperty()方法,发现无日志信息。 Hookjava.net.HttpURLConnection整个类的方法调用,观察调用了哪些方法。 当hook HttpURLConnection类的方法时,没有打印出预想的setRequestProperty()、setRequestMethod()等方法的调用,这是因为HttpURLConnection是一个抽象类,无法创建对象实例所以内存中没有对象实例,需要Hook它的子类实例。 HttpURLConnection类定义。 通过断点调试查看实现HttpURLConnection的子类实例。 在获取HttpURLConnection对象后断点。 HttpURLConnectoin具体实现类com.android.okhttp.internal.huc.HttpURLConnectionImpl hook com.android.okhttp.internal.huc.HttpURLConnectionImpl类获取请求头部和参数信息。 android hooking watch class com.android.okhttp.internal.huc.HttpURLConnectionImpl   根据Hook HttpURLConnection的结果编写“自吐”脚本 function main(){ Java.perform(function(){ var URL = Java.use("java.net.URL") var HttpURLConnection = Java.use("com.android.okhttp.internal.huc.HttpURLConnectionImpl") var BufferedReader = Java.use("java.io.BufferedReader") var StringBuilder = Java.use("java.lang.StringBuilder") var InputStreamReader = Java.use("java.io.InputStreamReader") // Hook URL构造方法获取请求url URL.$init.overload("java.lang.String").implementation = function(url){ console.log("--NetWork Start--") console.log("--url--", url) this.$init(url) } // Hook setRequestMethod方法获取请求方法 HttpURLConnection.setRequestMethod.implementation = function(method){ console.log(method) this.setRequestMethod(method) } // Hook setRequestProperty方法获取请求参数 HttpURLConnection.setRequestProperty.implementation = function(key, value){ console.log("--param-- : key=>", key, "value=>",value) this.setRequestProperty(key, value) } // Hook getInputStream方法获取响应报文 HttpURLConnection.getInputStream.implementation = function(){ var inputStream = this.getInputStream() var buffer = BufferedReader.$new(InputStreamReader.$new(inputStream)); var sb = StringBuilder.$new() var line = null; while((line = buffer.readLine()) != null){ sb.append(line); } var data = sb.toString() console.log("--response--: " +data) console.log("--Network Stop--") return inputStream } } )}setImmediate(main)   执行结果如下: OkHttp基础开发流程 1.创建一个OkhttpClient客户端对象。 2.创建Request对象设置header、body、url等参数。 3.使用OkhttpClient客户端将Request请求封装成Call对象后,调用enqueue()方法产生一次真实的网络请求,onResponse()等回调方法处理网络请求结果(网络请求可分为同步和异步两种,在Android中主要使用异步)。 MainActivity代码: package com.r0ysue.okhttp3demo;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;import java.io.IOException;public class MainActivity extends AppCompatActivity { private static String TAG = "r0ysue666"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 定位发送请求按钮 Button btn = findViewById(R.id.mybtn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 访问百度首页// String requestUrl = "https://www.baidu.com/"; String requestUrl = "https://sapi.k780,com/"; example myexample = new example(); try { myexample.run(requestUrl); } catch (IOException e) { e.printStackTrace(); } } }); }}   example代码: package com.r0ysue.okhttp3demo;import android.util.Log;import java.io.IOException;import java.net.InetSocketAddress;import java.net.Proxy;import java.util.HashMap;import okhttp3.Call;import okhttp3.Callback;import okhttp3.FormBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;public class example { // TAG即为日志打印时的标签 private static final String TAG = "---okhttp3---"; // 创建OkHttpClient客户端 OkHttpClient client = new OkHttpClient .Builder() .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.110.125", 8080))) .addNetworkInterceptor(new LoggingInterceptor()) .build(); void run(String url) throws IOException { // 创建RequestBody对象(请求体) HashMap paramMap = new HashMap<>(); paramMap.put("app", "weather.today"); paramMap.put("appkey","10003"); paramMap.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4"); paramMap.put("format", "json"); paramMap.put("cityNm","重庆"); FormBody.Builder builder = new FormBody.Builder(); for (String key:paramMap.keySet()){ builder.add(key, paramMap.get(key)); } RequestBody body = builder.build(); // 创建Request对象设置请求头和请求体 Request request = new Request. Builder() .url(url) .header("city","shanghai") .post(body) .build(); // 发起异步请求 // 通过OkHttpClient对象的newCall方法设置Request,调用enqueue方式完成一次网络请求 client.newCall(request).enqueue( new Callback() { @Override public void onFailure(Call call, IOException e) { call.cancel(); } // 网络请求完毕后响应的回调函数 @Override public void onResponse(Call call, Response response) throws IOException { //打印输出 Log.d(TAG, response.body().string()); } } ); }}   ‍ Okhttp3“自吐”脚本开发 从Okhttp3基础开发流程我们发现几个关键的点: 1.请求的服务器URL。 2.使用的协议版本,比如HTTP/1.0或者HTTP/1.1等。 3.请求头Header以及请求的Body数据。 4.请求的返回数据。 如果通过Hook的方式实现另类的“抓包”,那么我们的需求是保留URL、请求Body和headers以及请求的返回数据。可以在okhttpClient.newCall(request)函数中找到我们关注的请求Request对象,而这个对象中会包含我们关注的1、2、3项。但是由于Okhttp3设计的原因,如果按照HttpURLConnection自吐脚本输出Request和Response的难度比较大。 Okhttp3 Hook客户端强行设置代理 通过查看OkHttp3官方API发现可以在构造OkhttpClient对象时设置代理,之后的请求就会发送到代理服务器。 ‍ 一般来说开发者使用OkHttpClient创建客户端对象并不会使用proxy方法设置代理,但是我们可以通过Frida Hook的方式在调用build()方法时强行设置代理,这样就能在不设置WIFI代理或VPN代理的方式抓取到请求包。Frida代码如下: function main(){ Java.perform(function(){ // 在内存中获取okhttp3.OkHttpClient$Builder内部句柄 var Builder = Java.use("okhttp3.OkHttpClient$Builder") // 为创建代理对象做准备 var Proxy = Java.use("java.net.Proxy") var TYPE = Java.use("java.net.Proxy$Type") var InetSocketAddress = Java.use("java.net.InetSocketAddress") var String = Java.use("java.lang.String") console.log(Builder) // hook okhttp3.OkHttpClient$Builder内部类的build方法代表最终构造OkHttpClient对象 Builder.build.implementation = function(){ // 设置代理服务器IP地址 var ip_str = String.$new("192.168.110.125") // 创建代理服务器对象 var Proxy_IP_PORT = InetSocketAddress.$new(ip_str, 8080) // 调用当前对象的proxy强行设置代理 this.proxy(Proxy.$new(TYPE.HTTP.value, Proxy_IP_PORT)) console.log("设置代理成功:协议:", TYPE.HTTP.value) return this.build() } })}setImmediate(main)   运行效果: 文章来源:本文为看雪论坛优秀文章,由 Alvinlight 原创,转载请注明来自看雪社区 黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担! 如侵权请私聊我们删文 END

Hook入门与抓包