618购入了新机Xiaomi K50,真滴便宜又好用,续航很强界面很丝滑。正好拿来做逆向测试机?
怎么可能!这也太奢侈了!不过淘汰的Xiaomi Note 7 pro正好可以胜任这个工作,之前使用的老爷机Samsung SM-N9009 可以退居二线,进仓库吃灰了。
老爷机虽然用起来很卡(跟虚拟机有一拼),但有它的好处,360一键root这种玩意儿就可以随便搞定,轻松打造测试环境,毕竟只有Android6.0,还是好欺负。但是新手机Android版本号直接飙到了Android9,老方法不太好使了,而且小米系统还被魔改过,系统文件结构跟正常机子不一样,比如boot.img中解包不出default.prop(老鸟应该懂),这使得很多脚本工具都用不了了,因此必须重新研究测试环境怎么搭。
而且之前的老机子Hook框架没搭起来,就靠一个断点调试手撸太费劲了,这次怎么说也得把Xposed整明白。
跟主流的一键root不同,magisk可以管理整个系统的root权限树,并且可以选择性地加装或卸载安全选项,比如你可以将手机在可调式和不可调试间切换。然而magisk安装需要重刷系统镜像,也就导致了门槛较高,但我却认为这是一劳永逸的事情,对系统进行越权操作时,重刷系统是迟早地事。然而由于Magisk对原有的文件系统做了蒙版(内存覆盖),使得很多对系统权限要求较高的操作,如xposed Hook框架需要改写系统system文件,又比如设置真机可调式需要解包boot.img重写default.prop配置文件等等,依托于面具框架蒙版机制,都可以轻松执行,因此Magisk是我最推荐的高版本root方案。
在没有面具框架之前,Hook框架从头装,default.prop从头刷,而在面具框架中,你只需要导入几个zip包就能把这些事情一键解决。
所谓”磨刀不误砍柴工“,面具框架就是最重要的刀具,装了就可以砍瓜切菜,否则就要跟垃圾虚拟机纠缠不清、纠结于system系统文件编辑权限不足、为xposed无法联网问题而苦恼。
一般网上第一步会告诉你如何dump出原版boot.img文件然后用fastboot刷入手机,我绝对不推荐这个方法,安全测试面临很多越权需求,要求对手机文件系统做出较大变更,你无法保证每一步都是对的,即便是”老江湖“也会因为疲劳而犯错,因此必须要学会如何从存档点开始玩,否则手机变砖了早晚还得重刷系统,之前修改的boot.img还得重新弄。这里以红米Note7 pro为例,简单讲解一下手机系统开发版fastboot线刷流程。
首先需要在开发者选项中设置设备解锁状态,需要SIM卡联网绑定小米账号,获取解锁权限,注意小米账号的注册手机必须与联网SIM卡手机号一致,否则会解锁失败。解锁后关机,按住**电源键+音量-**键,进入FastBoot模式。
在小米官网申请解锁手机,无需审核即可下载Bootloader解锁工具(申请了个寂寞),打开工具登录账号一键解锁。
之后到官网上下载对应机型的系统开发版镜像,一般是压缩包格式的不要被第三方平台骗了,官网就有开发版,而且是免费的,那些收费的平台给的镜像纯纯nt,买下来后发现机型不适配,直接吐血,我承认我被坑的很惨
最后下载好小米刷机工具导入系统文件夹即可一键刷机,这里注意系统解压路径中不要带空格,最好直接解压在硬盘根目录下,我没有特意测过,反正因为工具要调用fastboot命令行,因此对路径的要求比较苛刻。
目前安卓12、11、10、9、8、7通用。
FastBoot刷Magisk的优点
无需第三方Recovery
不影响系统升级(完整包升级)
BootLoader已解锁(必须解锁,小米手机参考:magiskcn.com/unlock-mi)
小米10Pro12+512
MIUI12.5开发版21.5.20(安卓11)(支持稳定版,解锁BL一样可刷)
Windows电脑一台
adb push boot.img /storage/emulated/0/
想纯用手机操作,可以打开MT管理器,找到我们下载好的系统包,点开zip包,长按 boot.img 提取出来到 Dowmload 目录
如果系统包里面没有boot.img,只有payload.bin,请参考这个教程提取:magiskcn.com/payload-boot
手机下载安装:Magisk最新版
打开Magisk【安装 – 选择并修补一个文件 – 弹窗文件管理窗口(找到刚刚提取的boot.img)- 开始】
修补结束,会生成一个名字为(magisk_patched-版本号_随机字符.img)的文件(每次生成的随机字符都不一样)
把boot.img和magisk_patched-2X000_xxxxx.img两个文件复制到电脑
fastboot flash boot magisk_patched-2X000_xxxxx.img
Sending 'boot' (131072 KB) OKAY [ 3.049s]
Writing 'boot' OKAY [ 0.587s]
Finished. Total time: 4.582s
重启手机(开机有震动基本没问题了)耐心等手机开机。(显示Magisk的版本,就是刷好了的)
(A/B)(Ramdisk)(SAR)
这是手机分区,老款手机会显示“否”。新出的手机一般都显示“是”。不影响使用。
PS:很多人可能不知道fastboot工具怎么下载,看见各种教程在那秀操作,自己却连工具在哪都不知道,多气啊。其实这玩意儿就算AndroidSDK工具组件platform-tools中的一个,和ADB放在一起,只要你安装了AndroidSDK,这些工具基本都有了,也可以在Android官网单独下载platform-tools,这样可以随意更换工具版本,还是很重要的
动态调试对App逆向有多重要呢?就像民以食为天,有调试才能谈逆向,否则就是闹着玩。安装面具框架后,开启调试权限就会简单很多,安装MagiskHide Props Config 模块即可永久修改真机可调式权限。
官网下载:[MODULE] MagiskHide Props Config - SafetyNet, prop edits, and more - v6.1.2 | XDA Forums
通过命令将zip包推送到手机上
从Magisk→模块→本地安装→选择zip包即可安装
重启手机,进入adb shell,执行props
getprop ro.debuggable查看属性值
重启后再次查看,如果不是1则:设置ro.debuggable=1,ro.secure=0,安装adbd-Insecure-v2.00之后再执行adb root就可以啦。
死用教程自然是走不远的,笔者在开启调试权限时就遇到了boot.img镜像中无法解包出default.prop文件的问题,这一问题进而导致了默认的MagiskHide props中没有ro.debuggable这个选项的问题,一度被这个问题卡了很久,测试了很多方法都以失败告终,多次变砖搞得人心力憔悴。
然功夫不负有心人,经过研究发现,上述所谓MagiskHide Props Config永久映射方案,相当于再启动前帮你自动运行如下面具的props Mask命令,其本质上是一种props变量的动态覆盖方案。
adb shell #adb进入命令行模式
su #切换至超级用户
magisk resetprop ro.debuggable 1
stop;start; #一定要通过该方式重启
如果查看default.prop文件可以发现,依然为ro.debuggable=0。小米手机系统经过特殊魔改,boot.img下无法直接解包出default.prop文件,小米系统会从其他位置获取default.prop文件属性,我们不知道这个文件在哪,因此静态default.prop覆盖方案肯定行不通。既然这样,何不使用MagiskHide Props Config进行动态覆盖,你default.prop爱咋地咋地,我直接改内存去了。
理解了原理就好办,只需要自己重新定义一个custom props 名字就叫ro.debuggable,把原先的环境变量值覆盖掉,这事儿就解决了,就这么简单。
Hook相比调试权限没有那么必要,但在逆向大项目时也变得必不可少,它可谓效率神器。调试器虽然可以动态运行Java方法,但劣势在于只能针对一两个入口点做测试,Smali插座可以任意位置插入代码,但它的缺点却多得数不过来,重新打包App耗时长、App签名保护问题、要熟练掌握Smali语法,批量插入代码难度大,代码功能复杂时很难插入等等。
那么Hook就是一个可以快速、批量插入代码片段且无需打包App的代码修改方案,而且使用Java语言即可编写,开发效率非常高,也没有代码长度限制,想开发多长都行,唯一的缺点就是没法打包Apk发给别人用,害,到时候用smali重新写一下不久完了,有些App业务逻辑都得摸索半天,还各种加密,还想改两下就给别人用,别做梦了。
EDXposed和Xposed就是一个东西,只是前者能支持在高版本的android上运行,得益于面具框架支持,无需入侵系统的system文件夹即可发挥Xposed的功能,在网上搜索一下EDXposed的模块开发教程,你会发现几乎没有,因为它和Xposed的模块开发方式一模一样,有些人可能不明白为什么Android换了这么多代,启动器也换成EDXposed了,原有的Xposed模块也不用升级就能兼容呢?还不是因为底层几乎没变过,xposed直接与虚拟机通信,这属于很底层的机制了,这玩意儿要是大改那还不得闹翻天。
安装本身没什么难度,难在找资源,不要以为Xposed和Magisk人人都提,它们就是大平台产品了。充其量也就是个人爱好者开发的项目,单纯因为好用所以被大家传的广,个人项目的特点就是:服务不稳定、更新不稳定。原作者的资源链说不定啥时候就失效了,它们的从属模块更是没有一个所谓官网进行统一管理,都是些工程师自己写着玩的,很多模块仓库都是个人建的,说没就没,所以把这些资源收集好,说不定哪天链接就GG了。
自从新版面具下架了模块搜索功能之后,面具的模块下载位置就成了信息差,你知道有这个模块不难,但你就是不知道在哪下载,应该有第三方搜索工具可以解决这个问题,不过我懒得弄了,直接下载别人分享的资源:
https://pan.baidu.com/s/1Bfop9zcA6eccDy8Y5i09Gg 提取码:o6s0
打开Magisk - 模块 - 从本地导入模块 - 选择riru.zip和edxposed.zip进行安装,这里注意一定要先装riru.zip,否则edxposed.zip装不上,然后重启手机
重启后安装EdXposedInstaller.apk,进入App提示已激活,就安装成功了
在互联网上,关于Xposed模块编写的教程可谓是一抓一大把。但由于时间的推移,很多工具和方法都发生了变化(如Eclipse退出安卓编程舞台,AndroidStudio 不断升级导致其一些设置也随之变化等)也正因此,网上的教程往往有一些时限性,比如现如今 provide 这个关键字已经被舍弃了却仍有人在用,还有些说要把jar包放到lib文件夹而非libs文件夹……种种错误或者落伍的教程对新手产生了很大的误导。 笔者近日收到过朋友初学Xposed模块编写时的求助,看了一些他找的参考教程,觉得多多少少都存在点问题,因此萌生了写一篇关于在最新AndriodStudio 开发环境下实现Xposed模块开发入门的文章。
Xposed 框架的原理就不多说了,它部署在ROOT后的安卓手机上,通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。可以让我们在不修改APK源码的情况下,通过自己编写的模块来影响程序运行的框架服务,实现类似于自动抢红包、微信消息自动回复等功能。
其实,从本质上来讲,Xposed 模块也是一个 Android 程序。但与普通程序不同的是,想要让写出的Android程序成为一个Xposed 模块,要额外多完成以下四个硬性任务:
1、让手机上的xposed框架知道我们安装的这个程序是个xposed模块。
2、模块里要包含有xposed的API的jar包,以实现下一步的hook操作。
3、这个模块里面要有对目标程序进行hook操作的方法。
4、要让手机上的xposed框架知道,我们编写的xposed模块中,哪一个方法是实现hook操作的。
这就引出我即将要介绍的四大件(与前四步一一对照):
1、AndroidManifest.xml
2、XposedBridgeApi-xx.jar 与 build.gradle
3、实现hook操作的具体代码
4、xposed_Init
牢记以上四大件,按照顺序一个一个实现,就能完成我们的第一个Xposed模块编写。下面我们就开始吧!
首先打开AndroidStudio(以版本3.1为例,还在用老版本的请升级),建立一个工程,提示我们选择“Activity”,那就选一个Empty Activity吧。(这个是模块的界面,随意选择即可)。
我们可以把项目查看方式设置为Project模式,以方便查看。然后在 “项目名称/app/src/main/”目录下找到AndroidManifest.xml,双击之,并在指定位置插入以下三段代码:
<meta-data android:name="xposedmodule" android:value="true" />
<meta-data android:name="xposeddescription" android:value="这是一个Xposed例程" />
<meta-data android:name="xposedminversion" android:value="82" />
插入之后,如果你把手机连上AndroidStudio ,点击“编译”或者“运行”的话,手机就会启动刚刚编写的这个程序。而在手机里的Xposed框架中也会显示出这个模块:
说明Xposed框架已经认出了我们写的程序。但先别高兴太早——虽然框架已经觉得他是一个Xposed模块了,但我们自己心里清楚,这个模块还啥都不会干呢。下一步,我们让这个模块长点本事。
我们知道,Xposed模块主要功能是用来Hook其他程序的各种函数。但是,如何让前一步中的那个“一穷二白”的模块长本事呢?那就要引入 XposedBridgeApi.jar 这个包,你可以理解为一把兵器,模块有了这把宝刀才能施展出Hook本领。以前,都需要手动下载诸如XposedBridgeApi-54.jar 、 XposedBridgeApi-82.jar 等jar包,然后手工导入到libs目录里,才能走下一步道路。其实在AndroidStudio 3.1里面,我们完全不用这么麻烦,只需要多写一行代码,就让AndroidStuido自动给我们配置XposedBridgeApi.jar !下面操作开始(序号接着上一节):
在 “项目名称/app/src/main/”目录下找到build.gradle,在图示位置加上:
repositories {
jcenter()
}
以及
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
这句代码是告诉AndroidStuido使用jcenter作为代码仓库,从这个仓库里远程寻找 de.robv.android.xposed:api:82 这个API。这个网上很少有Xposed教程介绍它的!(我们不用自己找XposedBridgeApi.jar了。注意!此处要用compileOnly这个修饰符!网上有些写的是provide ,现在已经停用了!)如图:
写完之后, build.gradle会提示文件已经修改,是否同步。点击 “sync now”,同步即可:
【ps:如果网络不通,或者同步不畅,就不要进行第三步的repositories { jcenter()}这个步骤了,改做这个步骤:】
手动下载XposedBridgeApi-82.jar ,拖放到“项目名称/app/libs/”里面(不是网上说的单独建立lib文件夹,那是很久以前的故事了!),然后右键“Add As Library” 自行添加这个jar包。而compileOnly 'de.robv.android.xposed:api:82’和 compileOnly 'de.robv.android.xposed:api:82:sources’这两句仍然照常添加。
在“施展刀法”(编写hook代码)之前,我们先要立一个靶子。在界面上画一个按钮,并在MainAcitiviy里写代码如下:
package com.example.root.xposd_hook_new;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public String toastMessage() {
return "我未被劫持";
}
}
这个靶子很简单:MainActivity界面有个按钮,点击按钮后会弹出一个toast提示,该提示的内容由 toastMessage() 方法提供,而toastMessage()的返回值为“我未被劫持”:
下面我们正式开始“施展刀法”(编写hook代码) 来hook我们的MainActivity并修改这个类的toastMessage()方法,让它的返回值为“你已被劫持”:
在MainActivity的同级路径下新建一个类“HookTest.java”,代码如下:
package com.example.root.xposd_hook_new;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class HookTest implements IXposedHookLoadPackage {
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (loadPackageParam.packageName.equals("com.example.root.xposd_hook_new")) {
XposedBridge.log(" has Hooked!");
Class clazz = loadPackageParam.classLoader.loadClass(
"com.example.root.xposd_hook_new.MainActivity");
XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//XposedBridge.log(" has Hooked!");
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult("你已被劫持");
}
});
}
}
}
由代码可知,我们是通过IXposedHookLoadPackage接口中的handleLoadPackage方法来实现Hook并篡改程序的输出结果的。代码中“com.example.root.xposd_hook_new ”是目标程序的包名,“com.example.root.xposd_hook_new.MainActivity” 是想要Hook的类, "toastMessage"是想要Hook的方法。我们在afterHookedMethod方法(用来定义Hook了目标方法之后的操作)中,修改了toastMessage()方法的返回值为“你已被劫持”。