记一次带壳子的hook体验.pdf https://wwpv.lanzoue.com/iuuI90po44qj记一次带壳子的hook体验
前言
学完正己老师的安卓逆向(七)后,我们知道xposed的hook可以对加壳应用生效。正好NeatReader的安卓端加了360壳,我们就以它为例应用一下对加壳应用的hook。
![](https://img-blog.csdnimg.cn/14f76384075c4effbcd978e88e7ff547.png)
NeatReader在安卓端是免费软件,除云存储外没有使用限制。因此本次hook仅实现开启本地会员,使底部的“会员”一栏消失。
准备工作
首先下载32位的NeatReader,将它安装到刷入了fart的手机上脱壳,将所得的dex删掉类重复的(一般也就是大小相同的),并用mt/np做dex修复,然后全部打包到一个zip中,这个zip文件可以直接拖入jadx分析,如下图所示:
![](https://img-blog.csdnimg.cn/b66c0ac139d143db9836c3e80dc58591.png)
现在将app安装到pixel2xl上, 这个手机已经root并解锁bl,刷入了Magisk和 lsposed:
![](https://img-blog.csdnimg.cn/d1d2a84d2aa745f6869df127a1afe33f.png)
安装好原版app后,可以正常启动,接下来就可以着手分析代码开启本地会员了:
![](https://img-blog.csdnimg.cn/145fc3a610ca4fb9aef2af7077118a9f.png)
分析
分析流程与破解无加固的app大同小异,首先搜索会员,看到明显的提示:
![](https://img-blog.csdnimg.cn/a700751368b8443dbf708233326c8fec.png)
这一行所在的方法如下:
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的项目:
![](https://img-blog.csdnimg.cn/c0e1fef0c15a4f46bfd9ddd326d27752.png)
在项目的gradle脚本中增加一行,使后续所有放入 libs的名字带 -api的jar包都能参与编译,且不会被打包:
compileOnly fileTree(include: '*-api.jar', dir: 'libs')
![](https://img-blog.csdnimg.cn/0738ea10216f41bf9149915e318536bc.png)
将教程中的 XposedBridge-89.jar 改名为 XposedBridge-89-api.jar 放入 app\libs 目录下,重新用gradle sync同步项目即可完成导包。
然后我们在AndroidManifest中添加元数据:
接下来我们新建一个Hook类MyHook,这里相比教程略微做一点变化,我们改为通过反射调用方法来注入hook,因为把所有的hook方法都挂在回调函数里不利于阅读。
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[0];
// 这是真正的类加载器
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()[0] == ClassLoader.class) {
method.setAccessible(true);
method.invoke(MyHook.this, classLoader);
}
}
}
});
}
}
}
然后在asset目录下新建xposed_init文件,将类的全限定名粘贴进去:
![](https://img-blog.csdnimg.cn/4d8246094a174130843fd165a9e72074.png)
接下来只要在MyHook类中新增符合要求的方法,并将jadx复制的xposed片段粘贴进去就行了:
![](https://img-blog.csdnimg.cn/0e974d2dd29a43c58a6b3e2333965f60.png)
如图,这个方法将自己设为已登录状态:
![](https://img-blog.csdnimg.cn/2c2e19cb91e14681a1142043038c295b.png)
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);
}
});
}
同理,这个方法将自己设为会员状态:
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);
}
});
}
编译安装,然后到管理界面激活模块:
![](https://img-blog.csdnimg.cn/a64a91f7c02f4bcf8805c5b8734f9102.png)
激活后再强行停止并重启NeatReader,发现会员一栏已经消失了:
![](https://img-blog.csdnimg.cn/aa74fbe8115e4f60a559ac3df1ea8b91.png)