吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5812|回复: 31
上一主题 下一主题
收起左侧

[Android 原创] 2020春节红包第三题-Xposed框架Hook的应用 By:CrazyNut

[复制链接]
跳转到指定楼层
楼主
CrazyNut 发表于 2020-2-9 04:07 回帖奖励
本帖最后由 CrazyNut 于 2020-2-9 04:44 编辑

准备工具以及思路

首先不了解Xposed框架Hook的可以看看大佬的基础教程 - 《教我兄弟学Android逆向12 编写xposed模块》

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

准备工具

1.Android Studio - 以及Xposed框架环境 - 有关Xposed框架环境的搭建和编译在上述帖子都有,我就不在赘述
2.DDMS - 用于查看打印出来的日志信息,有Android Studio配套就有
3.dex2jar - 用于将Dex文件还原为jar
4.jd-gui - 用于查看jar包的java代码
5.IDA - 用于查看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文件

1.jpg

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

2.jpg

即可把dex文件还原为Jar

3.jpg

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

用Jd-gui查看java代码

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

4.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

5.jpg

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

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

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

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

这里直接贴上一段代码 - 《利用Xposed Hook打印Java函数调用堆栈信息的几种方法》

具体思路是:既然按了验证按钮后有土司 提示长度不够,那我直接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[1].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[i].getClassName()
                                        +"----"+stackElements[i].getFileName()
                                        +"----" + stackElements[i].getLineNumber()
                                        +"----" +stackElements[i].getMethodName());
                            }
                        }
                        Log.i("Dump Stack: ", "---------------over----------------");
                    }
                }
        );

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

6.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; // [sp+Ch] [bp-6Ch]
  int v20; // [sp+10h] [bp-68h]
  int v21; // [sp+14h] [bp-64h]
  int v22; // [sp+18h] [bp-60h]
  int v23; // [sp+1Ch] [bp-5Ch]
  int v24; // [sp+20h] [bp-58h]
  int v25; // [sp+24h] [bp-54h]
  int v26; // [sp+28h] [bp-50h]
  int v27; // [sp+2Ch] [bp-4Ch]
  int v28; // [sp+30h] [bp-48h]
  int v29; // [sp+34h] [bp-44h]
  int v30; // [sp+38h] [bp-40h]
  int v31; // [sp+3Ch] [bp-3Ch]
  int v32; // [sp+40h] [bp-38h]
  int v33; // [sp+48h] [bp-30h]
  int v34; // [sp+50h] [bp-28h]
  int v35; // [sp+58h] [bp-20h]

  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函数中所调用的方法入手

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

7.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[0]+"参数B:"+param.args[1]);
                    }

                    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[0]);
                    }

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

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

8.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

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

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

免费评分

参与人数 12威望 +3 吾爱币 +29 热心值 +11 收起 理由
sunbester + 1 + 1 用心讨论,共获提升!
Ercilan + 1 谢谢@Thanks!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
开心糖 + 2 + 1 膜拜大佬
AssassinQ + 1 + 1 我很赞同!
抖擞小小虾 + 1 + 1 热心回复!
abset + 1 + 1 我很赞同!
Ps出来的小赵 + 3 + 1 虽然看不懂,膜拜C佬!!!
cosct + 1 + 1 我很赞同!
qtfreet00 + 3 + 15 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
青春已不在丶 + 1 + 1 热心回复!
阿甘阿Q + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
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判断

后面的高级安卓题就不会了
推荐
 楼主| CrazyNut 发表于 2020-2-9 15:51 |楼主
hlrlqy 发表于 2020-2-9 13:33
所以onClick是在MainActivity注册的为什么还要hook找

emm 如果工程量大的话 就不能一眼看出来谁是谁了啊
当然对这题来说确实不用hook来找
沙发
Hmily 发表于 2020-2-9 04:19
3#
你与明日 发表于 2020-2-9 06:20
妈耶,我把第三题算法全搞出来了.....

点评

坐等大佬文章  详情 回复 发表于 2020-2-9 12:21
快写文章分享下。  详情 回复 发表于 2020-2-9 10:46
4#
qq3976690 发表于 2020-2-9 07:23
羡慕、远处观看
5#
涛之雨 发表于 2020-2-9 08:31
你与明日 发表于 2020-2-9 06:20
妈耶,我把第三题算法全搞出来了.....

大佬求教程
(虽然可能看不懂)
6#
andytong123 发表于 2020-2-9 08:37
原来如此啊,看完后恍然大悟
7#
zhangjj001 发表于 2020-2-9 09:02
收听大佬了
8#
阿甘阿Q 发表于 2020-2-9 09:04
恍然大悟,还是要认真学习
9#
bamyoo 发表于 2020-2-9 09:14
Android逆向一点都不会,要从零开始学了。
10#
苏紫方璇 发表于 2020-2-9 09:47
我也想到了用xposed来hookstring的几个方法,然而没装安卓编程环境,凉凉

点评

+1 然后我用Frida做的  详情 回复 发表于 2020-2-9 11:28
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-23 14:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表