首页
论坛
专栏
课程

[原创] 使用网络堆栈自底向上定位数据加解密代码位置

2018-3-29 08:38 2933

[原创] 使用网络堆栈自底向上定位数据加解密代码位置

2018-3-29 08:38
2933
如果我们打算分析一个app发出的网络报文,可能报文是加密的。这个时候如何快速定位到网络请求的数据如何构造的呢。我们可以通过对socket的拦截,在这里输出堆栈,然后通过堆栈看出整个调用链。我使用xposed实现这个功能

1. 拦截socket

  XposedHelpers.findAndHookMethod(Socket.class, "getInputStream", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                if (printStackTrace) {
                    LogUtil.outTrack("socket getInputStream");
                }
            }
}
这里是样例代码,实际上要处理socket,sslsocket等。然后还可以输出网络报文,解决有些时候Charles无法成功抓包的问题。

2. 异步堆栈拦截

我们在分析网络请求的时候,有自顶向下和自底向上两种分析路径。但是自底向上分析经常由于异步任务的原因导致堆栈断层。因为堆栈dump是基于线程的。很多业务都是在ui线程触发事件,准备数据,加解密。然后异步发送网络请求,当我们关心网络请求的时候,常常在网络出口(socket)dump线程堆栈,进而分析业务逻辑调用链。这个时候异步掐断了堆栈,所以我提供了线程堆栈跟踪功能,任何时候,只要你通过我提供的api输出堆栈。我将会输出从主线程到网络线程之间的线程跳跃堆栈。
package com.virjar.xposedhooktool.tool.okhttp;

import com.google.common.collect.Maps;
import com.virjar.xposedhooktool.hotload.SingletonXC_MethodHook;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadPoolExecutor;

import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

/**
 * Created by virjar on 2018/3/27.<br>
 * 异步任务堆栈监听器
 */

public class ThreadPoolHook {
    //注意,千万不要使用InheritableThreadLocal
    private static ThreadLocal<Throwable> stackTraceThreadLocal = new ThreadLocal<>();

    private static Throwable getThreadSubmitEntry() {
        return stackTraceThreadLocal.get();
    }

    public static Throwable stackTraceChain() {
        Throwable submitEntry = getThreadSubmitEntry();
        if (submitEntry == null) {
            return new Throwable();
        }
        return new Throwable(submitEntry);
    }

    public static void monitorThreadPool() {
        XposedHelpers.findAndHookMethod(ThreadPoolExecutor.class, "execute", Runnable.class, new SingletonXC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                Runnable target = (Runnable) param.args[0];
                Throwable parentStackTrace = getThreadSubmitEntry();
                Throwable theStackTrace;
                if (parentStackTrace != null) {
                    theStackTrace = new Throwable("parent submit task stack entry", parentStackTrace);
                } else {
                    theStackTrace = new Throwable("parent submit task stack entry");
                }
                param.args[0] = new RunnableMonitor(target, theStackTrace);
            }
        });


        Method threadInitMethod = null;
        try {
            threadInitMethod = Thread.class.getDeclaredMethod("init", ThreadGroup.class, Runnable.class, String.class, long.class);
            //threadInitMethod = XposedHelpers.findMethodExactIfExists(Thread.class, "init", ThreadGroup.class, Runnable.class, String.class, long.class);
        } catch (Exception e) {
            //ignore
        }
        if (threadInitMethod == null) {
            try {
                threadInitMethod = Thread.class.getDeclaredMethod("create", ThreadGroup.class, Runnable.class, String.class, long.class);
                //threadInitMethod = XposedHelpers.findMethodExactIfExists(Thread.class, "init", ThreadGroup.class, Runnable.class, String.class, long.class);
            } catch (Exception e) {
                //ignore
            }
        }
        if (threadInitMethod != null) {
            XposedBridge.hookMethod(threadInitMethod, new SingletonXC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    Runnable target = (Runnable) param.args[1];
                    //传递了runnable的方式创建线程
                    Throwable parentStackTrace = getThreadSubmitEntry();
                    Throwable theStackTrace;
                    if (parentStackTrace != null) {
                        theStackTrace = new Throwable("parent submit task stack entry", parentStackTrace);
                    } else {
                        theStackTrace = new Throwable("parent submit task stack entry");
                    }
                    if (target != null) {
                        param.args[1] = new RunnableMonitor(target, theStackTrace);
                        return;
                    }

                    // run 方法在thread本身实现
                    Thread thread = (Thread) param.thisObject;
                    stackTraceMap.putIfAbsent(thread, theStackTrace);
                    XposedHelpers.findAndHookMethod(thread.getClass(), "run", new SingletonXC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            if (stackTraceThreadLocal.get() == null) {
                                Throwable throwable = stackTraceMap.get(Thread.currentThread());
                                if (throwable != null) {
                                    stackTraceThreadLocal.set(throwable);
                                }
                            }
                        }

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            stackTraceThreadLocal.remove();
                            stackTraceMap.remove(Thread.currentThread());
                        }
                    });
                }
            });
        }
    }

    private static ConcurrentMap<Thread, Throwable> stackTraceMap = Maps.newConcurrentMap();


    private static class RunnableMonitor implements Runnable {
        private Runnable delegate;
        private Throwable parentThreadStackTrace;

        RunnableMonitor(Runnable delegate, Throwable parentThreadStackTrace) {
            this.delegate = delegate;
            this.parentThreadStackTrace = parentThreadStackTrace;
        }

        @Override
        public void run() {
            if (stackTraceThreadLocal.get() != null) {
                delegate.run();
                return;
            }
            stackTraceThreadLocal.set(parentThreadStackTrace);
            try {
                delegate.run();
            } finally {
                stackTraceThreadLocal.remove();
            }
        }
    }
}

3. 最终效果

=============>
com.virjar.xposedhooktool.tool.okhttp.ThreadPoolHook.stackTraceChain:31
com.virjar.xposedhooktool.tool.log.LogUtil.getTrack:167
com.virjar.xposedhooktool.tool.log.LogUtil.outTrack:162
com.virjar.xposedhooktool.tool.socket.NetDataPrinter$5.beforeHookedMethod:359
de.robv.android.xposed.XposedBridge.handleHookedMethod:340
com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream:-1
org.apache.http.impl.io.SocketInputBuffer.<init>:75
org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer:88
org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer:175
org.apache.http.impl.SocketHttpClientConnection.bind:111
org.apache.http.impl.conn.DefaultClientConnection.openCompleted:134
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection:177
org.apache.http.impl.conn.AbstractPoolEntry.open:169
org.apache.http.impl.conn.AbstractPooledConnAdapter.open:124
org.apache.http.impl.client.DefaultRequestDirector.execute:370
org.apache.http.impl.client.AbstractHttpClient.execute:560
org.apache.http.impl.client.AbstractHttpClient.execute:492
org.apache.http.impl.client.AbstractHttpClient.execute:470
com.hkairlines.apps.BaseActivity.e:2148
com.hkairlines.apps.BaseActivity.a:163
com.hkairlines.apps.BaseActivity$46.run:2128
com.virjar.xposedhooktool.tool.okhttp.ThreadPoolHook$RunnableMonitor.run:129
java.lang.Thread.run:818
cause parent submit task stack entry

com.virjar.xposedhooktool.tool.okhttp.ThreadPoolHook$2.beforeHookedMethod:77
de.robv.android.xposed.XposedBridge.handleHookedMethod:340
java.lang.Thread.create:-1
java.lang.Thread.<init>:215
com.hkairlines.apps.BaseActivity.d:2125
com.hkairlines.apps.BaseActivity.onStart:281
android.app.Instrumentation.callActivityOnStart:1238
android.app.Activity.performStart:6374
android.app.ActivityThread.performLaunchActivity:2446
android.app.ActivityThread.handleLaunchActivity:2543
android.app.ActivityThread.access$1000:156
android.app.ActivityThread$H.handleMessage:1407
android.os.Handler.dispatchMessage:102
android.os.Looper.loop:157
android.app.ActivityThread.main:5653
java.lang.reflect.Method.invoke:-2
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run:746
com.android.internal.os.ZygoteInit.main:636
de.robv.android.xposed.XposedBridge.main:107
上面是某航空公司航班报价查询堆栈拦截,可以看到在,在activity启动的时候,触发航班列表查询,其代码地址是:"com.hkairlines.apps.BaseActivity.d:2125"&&"com.hkairlines.apps.BaseActivity.onStart:281"
而且使用的是异步线程,这个时候新建了一个线程,并且在新线程中访问网络,而最终网络发出点为:"com.hkairlines.apps.BaseActivity.e:2148"


[招聘]欢迎市场人员加入看雪学院团队!

打赏 + 1.00
打赏次数 1 金额 + 1.00
收起 
赞赏  junkboy   +1.00 2019/02/12
最新回复 (11)
lfyyy 2018-3-29 09:32
2
0
bjhrwzh 2018-3-29 10:03
3
0
思路不错,mark~
PanzerT 2018-3-29 10:18
4
0
mark~
长泽雅美 2018-3-29 10:25
5
0
很好
Monkeylord 2018-3-29 18:04
6
0
思路不错。不过我有更好的办法/手动滑稽
virjar 1 2018-3-29 19:09
7
0
Monkeylord 思路不错。不过我有更好的办法/手动滑稽
Lucaks 1 2018-3-29 23:59
8
0
菜年richor 5天前
9
0
Monkeylord 思路不错。不过我有更好的办法/手动滑稽
求TCP定位发送位置的方法,大佬开个价
virjar 1 5天前
10
0
virjar
求,交流一下嘛
virjar 1 5天前
11
0
virjar
https://gitee.com/virjar/ucrack/tree/master/app/src/main/java/com/virjar/ucrack/plugin/socket   update link
miyuecao 4天前
12
0
思路牛叉,学习了
游客
登录 | 注册 方可回帖
返回