侃遍天下无二人 发表于 2023-3-9 14:01

记一次带壳子的hook体验

本帖最后由 侃遍天下无二人 于 2023-3-10 11:30 编辑

如果图裂了请下载pdf查看,下次我会换图床的
记一次带壳子的hook体验.pdfhttps://wwpv.lanzoue.com/iuuI90po44qj


# 记一次带壳子的hook体验
## 前言
学完正己老师的[安卓逆向(七)](https://www.52pojie.cn/thread-1740944-1-1.html)后,我们知道xposed的hook可以对加壳应用生效。正好NeatReader的安卓端加了360壳,我们就以它为例应用一下对加壳应用的hook。



NeatReader在安卓端是免费软件,除云存储外没有使用限制。因此本次hook仅实现开启本地会员,使底部的“会员”一栏消失。
## 准备工作
首先下载(https://wwpv.lanzoue.com/i4uHQ0ndk2wb),将它安装到[刷入了fart的](https://www.52pojie.cn/thread-1135060-1-1.html)手机上脱壳,将[所得的dex](https://wwpv.lanzoue.com/iNO9h0ndjzwd
)删掉类重复的(一般也就是大小相同的),并用mt/np做dex修复,然后[全部打包到一个zip中](https://wwpv.lanzoue.com/i0c4U0ndk0tg
),这个zip文件可以直接拖入jadx分析,如下图所示:


现在将app安装到pixel2xl上, 这个手机已经root并解锁bl,刷入了Magisk和 lsposed:

安装好原版app后,可以正常启动,接下来就可以着手分析代码开启本地会员了:

## 分析
分析流程与破解无加固的app大同小异,首先搜索会员,看到明显的提示:

这一行所在的方法如下:

```java
    private void v3(boolean z7) {
      com.gzhi.neatreader.r2.utils.l lVar = com.gzhi.neatreader.r2.utils.l.f9986a;
      lVar.e("账号切换", "updateBottomNavigationTab");
      if (p2()) {
            lVar.e("账号切换", "已登录");
            if (this.H.i() == 1) {
                lVar.e("账号切换", "VIP_USER 隐藏促销按钮");
                k3();
                return;
            } else if (q2()) {
                lVar.e("账号切换", "FREE_USER 显示促销按钮");
                if (z7) {
                  return;
                }
                if (this.f9896x.getMenu().size() == 5) {
                  if (r2()) {
                        return;
                  }
                  p3(com.gzhi.neatreader.r2.utils.g.f9982a.d(this.H.s().b()));
                  return;
                } else if (this.f9896x.getMenu().size() == 4) {
                  if (this.H.s() == null) {
                        this.f9896x.getMenu().add(0, R.id.im_premium, 4, getString(2131755099));
                  } else {
                        this.f9896x.getMenu().add(0, R.id.im_premium, 4, getString(2131755100));
                        p3(true);
                  }
                  e(4);
                  return;
                } else {
                  return;
                }
            } else {
                return;
            }
      }
      lVar.e("账号切换", "未登录, 显示会员按钮");
      if (z7) {
            return;
      }
      k3();
      this.f9896x.getMenu().add(0, R.id.im_premium, 4, getString(2131755099)).setIcon(R.drawable.ic_main_premium);
      e(4);
    }
```

分析可以得知`p2()`用于判断账号是否登录;`this.H.i()`表示当前账号身份,会员为1。
因此我们需要令 p2() 返回true,令 H.i() 返回1。

## hook的代码实现
我们先创建一个NoActivity的项目:

在项目的gradle脚本中增加一行,使后续所有放入 libs的名字带 -api的jar包都能参与编译,且不会被打包:
```groovy
compileOnly fileTree(include: '*-api.jar', dir: 'libs')
```

将教程中的 XposedBridge-89.jar 改名为 XposedBridge-89-api.jar 放入 `app\libs` 目录下,重新用gradle sync同步项目即可完成导包。
然后我们在AndroidManifest中添加元数据:
```xml
    <!-- 是否是xposed模块,xposed根据这个来判断是否是模块 -->
    <meta-data
      android:name="xposedmodule"
      android:value="true" />
    <!-- 模块描述,显示在xposed模块列表那里第二行 -->
    <meta-data
      android:name="xposeddescription"
      android:value="给NeatReader开个本地会员" />
    <!-- 最低xposed版本号(lib文件名可知) -->
    <meta-data
      android:name="xposedminversion"
      android:value="89" />
```
接下来我们新建一个Hook类MyHook,这里相比教程略微做一点变化,我们改为通过`反射调用方法`来注入hook,因为把所有的hook方法都挂在回调函数里不利于阅读。
```java
public class MyHook implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
      if ("com.gzhi.neatreader.r2.main".equals(loadPackageParam.packageName)) {
            // 在attach方法中,可以取得真正的类加载器,而attach本身的加载器是壳代{过}{滤}理的加载器
            XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                  Context context = (Context) param.args;
                  // 这是真正的类加载器
                  ClassLoader classLoader = context.getClassLoader();
                  // 取得当前类下的所有方法
                  Method[] methods = MyHook.class.getDeclaredMethods();
                  // 用反射调用所有以 mod_ 开头, 且只有一个 ClassLoader 作为参数的方法
                  for (Method method : methods) {
                        if (method.getName().startsWith("mod_") && method.getParameterTypes().length == 1 && method.getParameterTypes() == ClassLoader.class) {
                            method.setAccessible(true);
                            method.invoke(MyHook.this, classLoader);
                        }
                  }
                }
            });
      }
    }
}
```
然后在asset目录下新建xposed_init文件,将类的全限定名粘贴进去:

接下来只要在MyHook类中新增符合要求的方法,并将jadx复制的xposed片段粘贴进去就行了:


如图,这个方法将自己设为已登录状态:

```java
    private void mod_ILoggedIn(ClassLoader classLoader){
      XposedHelpers.findAndHookMethod("com.gzhi.neatreader.r2.ui.MainActivity", classLoader, "p2", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                param.setResult(true);
            }
      });
    }
```
同理,这个方法将自己设为会员状态:
```java
    private void mod_IamVIP(ClassLoader classLoader){
      XposedHelpers.findAndHookMethod("com.gzhi.neatreader.r2.utils.SharedPreferenceHelper", classLoader, "i", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                param.setResult(1);
            }
      });
    }
```

编译安装,然后到管理界面激活模块:

激活后再**强行停止并重启**NeatReader,发现会员一栏已经消失了:


侃遍天下无二人 发表于 2023-3-9 20:29

lvbuqing 发表于 2023-3-9 20:08
360壳子我是hook:com.stub.StubApp,a方法或getOrigApplicationContext;这个类里面的所有参数带context的 ...
正己给的那个是通杀的,也就是不区分壳子,没必要专门再hook壳代{过}{滤}理了

lvbuqing 发表于 2023-3-9 20:08

360壳子我是hook:com.stub.StubApp,a方法或getOrigApplicationContext;这个类里面的所有参数带context的,都可以用,我试过了;
有时间试试你这个:XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {

Piz.liu 发表于 2023-3-9 14:31

图裂了..

侃遍天下无二人 发表于 2023-3-9 14:47

Piz.liu 发表于 2023-3-9 14:31
图裂了..

好像真是这样,我这次先传pdf好了,下次换图床

debug_cat 发表于 2023-3-9 15:26

貌似平台无法直接加载自己的的储存桶,就算我没有任何限制,平台也加载不出来,只能一个个图片上传,这一点每次发帖最头疼了。

wfghim 发表于 2023-3-9 16:41

精彩绝伦,可惜丢图了

侃遍天下无二人 发表于 2023-3-9 17:03

debug_cat 发表于 2023-3-9 15:26
貌似平台无法直接加载自己的的储存桶,就算我没有任何限制,平台也加载不出来,只能一个个图片上传,这一点 ...

你是改名了吗,评分区的名字还没变过来

debug_cat 发表于 2023-3-9 17:09

侃遍天下无二人 发表于 2023-3-9 17:03
你是改名了吗,评分区的名字还没变过来

刚刚站长才修改的,应该有缓存

wasm2023 发表于 2023-3-9 17:52

大佬,能否来几篇pc端协议逆向的案例,群里几乎没有

侃遍天下无二人 发表于 2023-3-9 17:58

wasm2023 发表于 2023-3-9 17:52
大佬,能否来几篇pc端协议逆向的案例,群里几乎没有

什么群呀

wasm2023 发表于 2023-3-9 17:59

打错了,是咱论坛几乎没有{:1_925:}
页: [1] 2 3
查看完整版本: 记一次带壳子的hook体验