CrazyNut 发表于 2020-2-9 04:07

2020春节红包第三题-Xposed框架Hook的应用 By:CrazyNut

本帖最后由 CrazyNut 于 2020-2-9 04:44 编辑

## 准备工具以及思路

**首先不了解Xposed框架Hook的可以看看大佬的基础教程 - [《教我兄弟学Android逆向12 编写xposed模块》](https://www.52pojie.cn/thread-850885-1-1.html)**

本文不需要会看懂汇编代码,当你看完上面的文章,学会Xposed框架Hook的简单应用后。
就算是从未接触过的萌新跟着流程一会就能搞定,一起来动手吧!

**准备工具**

1.Android Studio - 以及Xposed框架环境 - 有关Xposed框架环境的搭建和编译在上述帖子都有,我就不在赘述
2.DDMS - 用于查看打印出来的日志信息,有Android Studio配套就有
3.**(https://www.lanzouj.com/i96dr2h)** - 用于将Dex文件还原为jar
4.**(https://www.lanzouj.com/i96drza) **- 用于查看jar包的java代码
5.**(https://down.52pojie.cn/Tools/Disassemblers/IDA.txt)** - 用于查看So文件代码
6.一款可以打开Apk的压缩软件
7.一个装有Xposed环境的安卓模拟器或者手机

**思路**

1.首先将APK的dex还原为jar后,可以用jd-gui查看java代码
2.后面可以发现关键代码都在So文件内[剧透] ,然后用IDA打开So文件,找到对应的关键函数
3.加以以分析Hook某关键函数后直接出结果

## 查看APK的java代码
### Dex还原为jar

用压缩软件打开APK可以看到classes.dex文件

!(https://i.loli.net/2020/02/09/ixD6YlptsdZG8er.jpg)

将其解压并扔到dex2jar的根目录下,运行 DexToJar.bat

!(https://i.loli.net/2020/02/09/7TKDCezcS1GAwuZ.jpg)

即可把dex文件还原为Jar

!(https://i.loli.net/2020/02/09/TQxG9bwLu4AoqDC.jpg)

上图红框则是我们需要的文件

### 用Jd-gui查看java代码

然后将其直接拖入jd-gui即可看到java代码

!(https://i.loli.net/2020/02/09/a14ySKH6Y2sCQPl.jpg)

CM代码很少,很容易看到入口函数MainActivity里的代码
这段java代码里面没有任何关键的代码
有一点点安卓基础就看的出来,APK引用了一个叫libcrack_j2c的so文件,所以可以判断关键代码全在so里面
~~~
而且作者方法名都明说了是checkFlag。。 可以看到参数时一个String 肯定是传入的相关flag 返回值也是个bool值,肯定是关键函数咯
当然 因为按钮只有一个搜OnClick也是一样的,从按钮事件入手。
~~~
所以直接上IDA分析so文件找到checkFlag或者OnClick这个函数。
## IDA查看so文件
### 定位到关键函数代码 - 方法 1.字符串大法好

直接找到
验证错误,继续加油
一步到位,香的不行
我的IDA版本暂时搜不了字符串,后面补上。。

### 定位到关键函数代码 - 方法 2.直接搜方法名

上面说到了 作者已经明确的告诉了checkFlag和onclick了这函数了。。明摆着就是关键函数。
所以在so文件没被混淆的情况下,可以直接用IDA打开So文件,搜索方法 checkFlag 或者 onclick

!(https://i.loli.net/2020/02/09/CdyNHoDbzJSsUq7.jpg)

IDA中直接拖入so载入后,在Functions Window 按ctrl+f直接搜方法名一步到位

**不过这里是不推荐用这个方法的。。因为就算So文件没被混淆
正常项目代码量巨大。。根本不知道谁是谁。。。不容易直接一眼就看出来的情况下
这个方法无疑大海捞针,所以下面介绍另一种方法**

### 定位到关键函数代码 - 方法 3.Xposed框架Hook打印堆栈

这里就需要用到Xposed框架来Hook了,还不会请先看上方基础教程

这里直接贴上一段代码 - [《利用Xposed Hook打印Java函数调用堆栈信息的几种方法》](https://blog.csdn.net/QQ1084283172/article/details/79378374)

~~~
具体思路是:既然按了验证按钮后有土司 提示长度不够,那我直接Hook Toast()这个方法 看看什么调用了他,不就可以找到点击事件了?
虽然对这题有点脱了裤子放屁。。。不过这也是一种思路吧
~~~

自己修改对应包名后变成如下

~~~
findAndHookMethod(
                "android.widget.Toast",    //要hook的包名+类名
                lpparam.classLoader,                              //classLoader固定
                "makeText",                        //要hook的方法名
                // 方法参数 没有就不填
                Context.class,
                CharSequence.class,
                int.class,
                new XC_MethodHook() {
                  @Override
                  protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行前执行
                        Log.i("Nut", "NutHookToast:" + param.args.toString());
                  }

                  protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行后执行,改方法的返回值一定要在方法执行完毕后更改
                        // 方法一:
                        Log.i("Dump Stack: ", "---------------start----------------");
                        Throwable ex = new Throwable();
                        StackTraceElement[] stackElements = ex.getStackTrace();
                        if (stackElements != null) {
                            for (int i = 0; i < stackElements.length; i++) {

                              Log.i("Dump Stack"+i+": ", stackElements.getClassName()
                                        +"----"+stackElements.getFileName()
                                        +"----" + stackElements.getLineNumber()
                                        +"----" +stackElements.getMethodName());
                            }
                        }
                        Log.i("Dump Stack: ", "---------------over----------------");
                  }
                }
      );
~~~

Hook后 进去 随便按一下 验证按钮 得到日志如下

!(https://i.loli.net/2020/02/09/J8tWQxK5olD3G1U.jpg)

可以看出只有一个包名为 com.wuaipojie.crackme01 的方法被调用 正是onclick的按钮事件。
然后用方法2直接IDA搜方法就可以了

## 简单分析函数代码

### 按钮OnClick事件方法

这里先从按钮事件开始

搜索onclick找到函数后,汇编代码是看不懂的。。。所以直接按F5查看伪代码,一起就清晰了起来

下面是代码
~~~
int __fastcall Java_com_wuaipojie_crackme01_MainActivity_onClick__Landroid_view_View_2(int a1, int a2, int a3)
{
int *v3; // r4
int v4; // r5
int v5; // r9
int v6; // r0
int v7; // r5
int (__fastcall *v8)(int *, void *); // r2
int v9; // r6
int v10; // r8
int v11; // r6
int v12; // r8
int v13; // r5
int v14; // r5
void *v15; // r1
int v16; // r6
int v17; // r5
int result; // r0
int v19; //
int v20; //
int v21; //
int v22; //
int v23; //
int v24; //
int v25; //
int v26; //
int v27; //
int v28; //
int v29; //
int v30; //
int v31; //
int v32; //
int v33; //
int v34; //
int v35; //

v3 = (int *)a1;
v4 = a3;
v31 = 0;
v29 = 0;
v30 = 0;
v27 = 0;
v28 = 0;
v25 = 0;
v26 = 0;
v23 = 0;
v24 = 0;
v21 = 0;
v22 = 0;
v19 = 0;
v20 = 0;
v5 = (*(int (__fastcall **)(int, int))(*(_DWORD *)a1 + 100))(a1, a2);
v6 = (*(int (__fastcall **)(int *, int))(*v3 + 100))(v3, v4);
if ( !v5 )
    goto LABEL_38;
v7 = v6;
if ( sub_539C(v3, &v31, &v26, 0, "com/wuaipojie/crackme01/MainActivity", "editText", "Landroid/widget/EditText;") )
    goto LABEL_39;
v9 = (*(int (__fastcall **)(int *, int, int))(*v3 + 380))(v3, v5, v26);
if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
    goto LABEL_39;
if ( v7 )
    (*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v7);
if ( !v9 )
    goto LABEL_38;
if ( !v25 && sub_5288(v3, &v30, &v25, 0, (int *)"android/widget/EditText", "getText") )
    goto LABEL_39;
v10 = (*(int (__fastcall **)(int *, int))(*v3 + 144))(v3, v9);               //获取输入框中的文本 存到v10中
if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
    goto LABEL_39;
(*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v9);
if ( !v10 )
    goto LABEL_38;
if ( !v24 && sub_5288(v3, &v29, &v24, 0, (int *)"java/lang/Object", "toString") )
    goto LABEL_39;
v11 = (*(int (__fastcall **)(int *, int))(*v3 + 144))(v3, v10);               //v10 tostring一下 存到 v11
if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
    goto LABEL_39;
(*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v10);
if ( !v11 )
    goto LABEL_38;
if ( !v23 && sub_5288(v3, &v28, &v23, 0, (int *)"java/lang/String", "trim") )
    goto LABEL_39;
v12 = (*(int (__fastcall **)(int *, int))(*v3 + 144))(v3, v11);            //v11 再trim一次 存到 v12
if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
    goto LABEL_39;
(*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v11);
if ( !v12 )
    goto LABEL_38;
if ( !v22 && sub_5288(v3, &v28, &v22, 0, (int *)"java/lang/String", "length") )
    goto LABEL_39;
v13 = (*(int (__fastcall **)(int *, int))(*v3 + 204))(v3, v12);             //获取v12的长度 存到 v13
if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )                     //【其实上面的一堆就是获取了你输入的flag的长度】
    goto LABEL_39;
if ( v13 == 30 )                                  //这里判断 v13 是否为 30也就是判断flag长度是否为30   
{
          //如果长度正确 则进入下面 checkflag的判断流程
    if ( !v21 && sub_5288(v3, &v31, &v21, 0, (int *)"com/wuaipojie/crackme01/MainActivity", "checkFlag") )
      goto LABEL_39;
    v32 = v12;
    v14 = (*(int (__fastcall **)(int *, int))(*v3 + 156))(v3, v5);
    if ( (*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
      goto LABEL_39;
    (*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v12);
    v8 = *(int (__fastcall **)(int *, void *))(*v3 + 668);
    if ( !v14 )
      goto LABEL_40;
    v15 = &unk_22047;
}
else
{
    (*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v12);
    v15 = &unk_2211B;
    v8 = *(int (__fastcall **)(int *, void *))(*v3 + 668);
}
while ( 1 )
{
    v16 = v8(v3, v15);
    if ( v20 || !sub_5288(v3, &v27, &v20, 1, (int *)"android/widget/Toast", "makeText") )
    {
      v33 = v16;
      v32 = v5;
      v34 = 0;
      v17 = (*(int (__fastcall **)(int *, int))(*v3 + 464))(v3, v27);
      if ( !(*(int (__fastcall **)(int *))(*v3 + 912))(v3) )
      {
      if ( v16 )
          (*(void (__fastcall **)(int *, int))(*v3 + 92))(v3, v16);
      if ( v17 )
      {
          if ( v19 || !sub_5288(v3, &v27, &v19, 0, (int *)"android/widget/Toast", "show") )
          {
            (*(void (__fastcall **)(int *, int))(*v3 + 252))(v3, v17);
            (*(void (__fastcall **)(int *))(*v3 + 912))(v3);
          }
      }
      else
      {
LABEL_38:
          sub_4EC0(v3, "java/lang/NullPointerException", "NullPointerException");
      }
      }
    }
LABEL_39:
    result = _stack_chk_guard - v35;
    if ( _stack_chk_guard == v35 )
      break;
LABEL_40:
    v15 = &unk_220FF;
}
return result;
}
~~~

**上面代码已经备注了
可以看到flag 长度要为 30 【欸! 这不是废话吗,已经提示了好不】
就会把flag传入 checkflag这个方法进行判断**

所以接下来是 checkflag 这个方法

### checkflag方法

和上面同样的方法 找到这个方法后F5一下 直接看伪代码

这一段代码就算是F5的伪代码 算法部分我不是很看得懂。。连蒙带猜的做的吧。。
坐等大佬们更详细的分析过程噢,这里说一下我的思路。

因为看不懂具体代码了,所以我考虑从Hook函数中所调用的方法入手

看了几遍后 发现函数最下方的 代码部分最可疑

!(https://i.loli.net/2020/02/09/GZraoLIchWtENfs.jpg)


可以看到 一次调用了 ToString substring equals方法

~~~
之前在看到flag是30位的时候
我就一直在猜想,似乎没有什么加密是30位的啊?? 所以估计是密钥加密后进行了切割。
~~~

这三个函数应该就认证了我的猜想。
~~~
上面的一堆函数估计是对密钥进行加密
toString 之后 马上用 SubString 进行切割
最后用equals方法比较。。。
所以我决定把这三个函数全部Hook打印结果
以上只是猜想,有理解错误的地方请多指正
~~~
## 见证奇迹的Hook
最后Hook的结果证实 对 SubString 的 Hook 确实有用 [剧透]
所以下面只上对 substring 这个方法Hook 的 函数

~~~
      findAndHookMethod(
                "java.lang.String",    //要hook的包名+类名
                lpparam.classLoader,                              //classLoader固定
                "substring",                        //要hook的方法名
                // 方法参数 没有就不填
                int.class,
                int.class,
                new XC_MethodHook() {
                  @Override

                  protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行前执行
                     Log.i("Nut", "substringA 参数A:" + param.args+"参数B:"+param.args);
                  }

                  protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行后执行,改方法的返回值一定要在方法执行完毕后更改
                     Log.i("Nut", "substringA:" + param.getResult());
                  }
                }
      );

      //因为substring有两种重载 所以我无脑一起hook了。。
      findAndHookMethod(
                "java.lang.StringBuilder",    //要hook的包名+类名
                lpparam.classLoader,                              //classLoader固定
                "substring",                        //要hook的方法名
                // 方法参数 没有就不填
                int.class,
                new XC_MethodHook() {
                  @Override

                  protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行前执行
                        Log.i("Nut", "substringB 参数A:" + param.args);
                  }

                  protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        //方法执行后执行,改方法的返回值一定要在方法执行完毕后更改
                        Log.i("Nut", "substringB:" + param.getResult());
                  }
                }
      );
~~~

**随便输一个30位的flag之后验证当当当当 出来了**

!(https://i.loli.net/2020/02/09/Ey6eObqStlXTmnM.jpg)

Log如下
~~~
02-09 04:00:34.060: I/Nut(2098): Already Find Methed
02-09 04:00:34.062: I/Nut(2098): substringA 参数A:1参数B:31
02-09 04:00:34.062: I/Nut(2098): substringA:ed61f6308c74bcf35c71729d4db24c
02-09 04:00:34.062: I/Nut(2098): NutHookToast:验证错误,继续加油
~~~

**可以看到确实是32位的加密 截取了 中间的 1-31位
结果也直接就打印出来了!!最后的equals也就是做了一个字符串比较而已。**

**本题也就此解开! 完结 撒花~**


## 总结

**星球杯大佬说过 : Xposed用的好就是可以为所欲为!
想想自己不知道还要隔多久才能到达这种高度,不禁有些小伤感 QAQ**

**本题也还有很多没弄懂的地方,期待大佬们出更详细的教程!
期待大佬们第四题的详细分析噢!希望能学习一下!**

**谢谢大佬们阅读 , 如有错误的地方请帮忙指正,以免误人子弟!谢谢**

wangyujie96 发表于 2020-2-9 16:35

拖入GDA一看,发现关键函数onclick是空的,看MainActivity的oncreate,发现so,拿出ida动态调试,看符号发现onclick,进去,F5,发现关键函数是checkflag,再进去,结尾处发现string equal,疑似最终判断,call处下断点,修改汇编使得string.equal变为java层字符串转C层的函数,随便输入断下,修改寄存器里的参数,在单步步过,跳转到R0的地址就是密码了

这个题我奋战了3天吧,安卓逆向从0到有,感谢52平台给了我学习的机会和动力,感谢初级题没有坑,除了一个debug判断{:301_998:}

后面的高级安卓题就不会了{:301_998:}

CrazyNut 发表于 2020-2-9 15:51

hlrlqy 发表于 2020-2-9 13:33
所以onClick是在MainActivity注册的为什么还要hook找

emm 如果工程量大的话 就不能一眼看出来谁是谁了啊
当然对这题来说确实不用hook来找

Hmily 发表于 2020-2-9 04:19

繁华被爆护一次。

你与明日 发表于 2020-2-9 06:20

妈耶,我把第三题算法全搞出来了.....

qq3976690 发表于 2020-2-9 07:23

羡慕、远处观看

涛之雨 发表于 2020-2-9 08:31

你与明日 发表于 2020-2-9 06:20
妈耶,我把第三题算法全搞出来了.....

大佬求教程{:301_1003:}
(虽然可能看不懂)

andytong123 发表于 2020-2-9 08:37

原来如此啊,看完后恍然大悟

zhangjj001 发表于 2020-2-9 09:02

收听大佬了

阿甘阿Q 发表于 2020-2-9 09:04

恍然大悟,还是要认真学习

bamyoo 发表于 2020-2-9 09:14

Android逆向一点都不会,要从零开始学了。

苏紫方璇 发表于 2020-2-9 09:47

我也想到了用xposed来hookstring的几个方法,然而没装安卓编程环境,凉凉
页: [1] 2 3 4
查看完整版本: 2020春节红包第三题-Xposed框架Hook的应用 By:CrazyNut