背景
由于某些需要, 进入某大楼的时候必须用他们物业指定的APP刷门禁, 我安装的时候是6.5.0版本, 这会儿启动速度也还可以, 也没太多花里胡哨的功能. 然而没过多久就迎来了一波强制版本更新, 更新后的版本启动速度慢到令人发指, 且增加了商城, 我就怒了, 区区门禁软件竟然这样肆意妄为, 开工!
目标
去除更新检查, 去掉实名认证
某加在7.0.0版本更名为某品, 破解版本为6.5.0, 在此保持称呼其为某加
受害者版本
6.5.0
开始破解
首先尝试使用Android反编译大杀器Jadx, 打开Jadx, 直接把某加安装包即.apk文件拖进去, 发现软件没有加壳, 可以看到Jadx顺利反编译出了某加的源码
7.0版本开始APP加了壳, 没记错的话是某数字安全软件的壳, 脱壳过于麻烦于是使用6.5.0版本破解
直接按 Ctrl + S 将源码全部保存, 然后导入IDEA, 开始分析代码
禁用强制升级
首先根据弹窗文字"发现新版本"搜索代码
发现全文匹配的仅有一处, 位于布局文件dialog_check_update.xml
查找使用该布局文件的地方, 仅有一个类UpdateNoticeDialog
查看UpdateNoticeDialog的代码, 可以看出这是一个自定义的对话框, 和APP显示的更新弹出框一致
接着查找使用到了UpdateNoticeDialog类的地方, 发现多处引用
而APP弹出更新提示的时候, 我正位于APP的首页, 可以推断出HomeActivity应该就是我要寻找的弹框处, 查看HomeActivity的代码如下
可以看到这里将UpdateNoticeDialog定义为成员变量, 然后在checkUpdate中对UpdateNoticeDialog赋值, 且其他操作都位于checkUpdate中, 现在看看checkUpdate完整代码如下
这个方法并没有做除了更新检查以外的事, 那么我只需要在Xposed中替换这个方法就可以了, 这样当APP调用checkUpdate时, 将执行我定义的方法, Xposed模块代码如下
findAndHookMethod("com.马赛克.马赛克.home.HomeActivity", loadPackageParam.classLoader,
"checkUpdate",
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});
这里直接使用replaceHookedMethod, 用自己定义的方法代替checkUpdate执行, 写好后重新运行可以发现首页更新弹窗已经被屏蔽了.
然而后来手贱不小心退出登录了, 再启动APP时发现登录的时候也有更新认证, 现在可行的方法有二, 一是找到登录界面, 用同样的方法去除更新, 但是这样一来繁琐, 二来他可能还有其他的地方有更新检查, 所以使用第二种方法
根据Android开发文档可以得知, 可以在APP的AndroidManifest.xml中可以定义当前APP的版本号, 然后可以在Java代码中使用PackageInfo来获得当前APP的版本信息, 而定义版本主要分为版本号versionCode和版本名versionName, 一般使用versionCode大小来判断是否需要更新, 而versionName则主要用作展示, 查看某加APP的AndroidManifest.xml文件, 发现其版本定义如下
那么一般的做法只要将versionCode的值修改成足够大, 就能达到免更新的目的, 但是实际操作下来发现这APP比较鸡贼, 判断版本是否更新并不是简单通过versionCode大小来判断, 不过当时为了偷懒也没有进一步分析版本检查代码, 我只知道最新版本的versionName是7.0.0, 所以直接使用Xposed将返回的versionName修改为固定的7.0.0
findAndHookMethod("android.app.ApplicationPackageManager", loadPackageParam.classLoader,
"getPackageInfo",
String.class,
int.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
PackageInfo packageInfo = (PackageInfo) param.getResult();
if (null != packageInfo && getTargetPackage().equals(packageInfo.packageName)) {
packageInfo.versionCode = Integer.MAX_VALUE;
packageInfo.versionName = "7.0.0";
}
}
});
此处使用Xposed直接拦截Android系统的包管理器, 当某加需要获取版本名时, 直接返回7.0.0, 不过这样做的缺点是如果某加未来版本更新到7.0.0以上时, 需要修改代码, 然而从2020年12月破解至今, 某加都没有再发布过版本更新, 省了我不少事
至此重新打包运行, 发现登录的更新检查也被去除了
然而新的风暴又出现了, 由于刚才的不慎退出, 现在需要重新上传实名认证资料了, 包括身份证号和照片, 由于我之前已经填过一次, 而且上传后需要重新审核, 所以不愿意再次填写, 于是开始寻找认证部分的代码
跳过实名认证
认证弹窗如下
当我点击右下角的显示门禁卡二维码按钮的时候, 就会弹出认证弹窗, 我们知道Activity会通过setContentView加载一个布局文件, 此页面为APP首页, 所以在HomeActivity中搜索setContentView, 发现并没有结果
但是HomeActivity继承自BaseActivity_2019_8_22, 于是在父类中找到了setContentView
可以看到这里加载的布局是activity_only_linear_layout.xml, 查看这个布局文件, 里面的代码非常简单
根据名称可以很容易的推断出id为fab_show_qr_code的按钮组件就是首页显示二维码的组件, 搜索这个id查看他的点击事件
根据判断逻辑, 如果CacheUtils.getStatus()返回为1, 就展示二维码弹窗, 不为1则执行checkStatus(), 以防万一, 看看checkStatus()的代码如下
显然checkIdentityStatus()这名字就很直白的告诉我这个就是用来检查身份认证的了, 在回来看看CacheUtils.getStatus()代码如下
这里的逻辑也很简单, 就是从登录信息中获取认证状态, 那么我只要用Xposed将getStatus()的返回值修改为1就可以了, Xposed代码如下
findAndHookMethod("com.excegroup.community.utils.CacheUtils", loadPackageParam.classLoader, "getStatus",
String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult("1");
}
});
拦截代码将在getStatus()执行之后执行, 将其返回值为修改为1, 现在重新运行, 程序一切正常, 也没有身份认证弹窗了, 点击显示二维码按钮正常显示门禁二维码


















