上一篇 《教我兄弟学Android逆向14 xpose改机开发02-改机代码基础编写》我带你熟悉了一下改机的流程,搭建了改机的基础代码,改机参数数据储存方面我们用的是SharedPreferences存储数据,界面方面写了一个一键新机的按钮,点一键新机的时候会随机生成imei数据存储到xml数据中,然后xpose代码hook到了getDeviceId函数会将返回值设置成xml文件读取的值,这样就完成了对imei的修改。
关于界面方面上节课也是粗略的写了一下,你可以根据自己想实现的功能去写自己想要的界面。
前面两节课我们配置了xpose开发的环境并且编写了xpose改机的第一个demo
那么本节课我会给你介绍一下改机的总体,以及改机点的寻找和编写。
要么学!要么不学!学和不学之间没有中间值 不学就放弃,学就要去认真的学! --致选择
xpose改机的优缺点
优点
易上手,便于使用,所需时间成本比较少。
缺点
很容易被检测,只能在java层去操作,Native层,内核层,C库的一些函数无法被修改。
比如libc的函数open,fopen,access,rename,Unix的popen函数还有一些C层内核层获取设备信息的结构体这些都是无法通过xpose去改的。
那么如果想做一款比较好的改机有什么解决方案呢?
1.用xpose去配合一些可以hook so层函数的工具比如Inlinehook,frida等。
2.针对性修改,逆向出协议数据配合xpose模拟数据发包。
3.自定义安卓系统,全局修改。
一 .改机点介绍
1.硬件信息
手机的唯一标识imei,android,序列号serial,手机品牌,手机机型,制造商,蓝牙名,蓝牙MAC地址,系统版本,系统版本名称,开发代号,源码控制版本号,编译类型,CPU架构,无线电固件版本,设备版本号,主板名称,引导程序版本号,设备参数,设备地址,产品名称,ROM名称,硬件名称,指纹,开发ROM编译用户,设备的ROM生成的时间.....
2.手机卡信息
3.蓝牙信息
4.UA信息
5.电池电量
5.开机时间
6.手机内存的信息
7.屏幕大小
8.传感器
9.位置基站信息
10.wifi信息
11.文件信息
12.环境检测
13.设备信息真实性检测
......
看到这么多信息你应该有疑问了,上面说的这些点都是怎么去找到的?
二.改机点寻找
1.从百度google等搜索引擎去搜索xpose改机代码,github上面也有一些开源的xpose改机代码都可以去参考。
2.逆向一些app去看对方是怎么去获取设备信息的,怎么去检测环境信息的从而针对性的去完善改机代码。
3.研究分析安卓源码,找到一切可以获取设备信息的点,要知道大厂做安全的都是从研究Android源码开始的。
三.改机代码编写
1. 因为改机代码量比较大,所以我们首先写一个抽象类去继承XC_MethodHook后面的hook类都可以去继承这个类。
MethodHook.java
public abstract class MethodHook extends XC_MethodHook {
protected String mpackageName;
protected ClassLoader mclassLoader;
protected DeviceInfoModel deviceInfoModel;
public MethodHook(XC_LoadPackage.LoadPackageParam paramLoadPackageParam, DeviceInfoModel deviceInfoModel)
{
this.mpackageName = paramLoadPackageParam.packageName;
this.mclassLoader = paramLoadPackageParam.classLoader;
this.deviceInfoModel = deviceInfoModel;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
public void hookMethodByClassMethodName(String paramString, String paramString2)
{
try
{
for (Method localMethod : XposedHelpers.findClass(paramString, this.mclassLoader).getDeclaredMethods())
if ((localMethod.getName().equals(paramString2)) && (!Modifier.isAbstract(localMethod.getModifiers())))
{
localMethod.setAccessible(true);
XposedBridge.hookMethod(localMethod, this);
}
}
catch (Throwable localThrowable)
{
Log.e("xposed-MethodHook", "hookMethodByClassMethodName Exception " + paramString);
}
}
public void hookMethodByClassMethodName(String paramString1, String paramString2, Object[] paramArrayOfObject)
{
try
{
Object[] arrayOfObject = new Object[1 + paramArrayOfObject.length];
for (int i = 0; i < arrayOfObject.length; i++)
{
if (i == -1 + arrayOfObject.length)
{
arrayOfObject[(-1 + arrayOfObject.length)] = this;
XposedHelpers.findAndHookMethod(paramString1, this.mclassLoader, paramString2, arrayOfObject);
return;
}
arrayOfObject[i] = paramArrayOfObject[i];
}
}
catch (Throwable localThrowable)
{
Log.e("xposed-MethodHook", "addHookMethodWithParms Exception " + paramString1);
}
}
public void hookMethodByClassMethodName(String paramString, Object[] paramArrayOfObject)
{
try
{
Object[] arrayOfObject = new Object[1 + paramArrayOfObject.length];
for (int i = 0; i < arrayOfObject.length; i++)
{
if (i == -1 + arrayOfObject.length)
{
arrayOfObject[(-1 + arrayOfObject.length)] = this;
XposedHelpers.findAndHookConstructor(paramString, this.mclassLoader, arrayOfObject);
return;
}
arrayOfObject[i] = paramArrayOfObject[i];
}
}
catch (Throwable localThrowable)
{
Log.e("xposed-MethodHook", "addHookConWithParms Exception " + paramString);
}
}
public void addHook(String paramString)
{
try
{
for (Constructor localConstructor : XposedHelpers.findClass(paramString, this.mclassLoader).getDeclaredConstructors())
if (Modifier.isPublic(localConstructor.getModifiers()))
XposedBridge.hookMethod(localConstructor, this);
}
catch (Throwable localThrowable)
{
Log.e("Xhook", "addHook exception", localThrowable);
}
}
public static Field setAccessible(Object arg4, String arg5) {
Field[] v4 = arg4.getClass().getDeclaredFields();
int v0 = v4.length;
int v1;
for(v1 = 0; v1 < v0; ++v1) {
Field v2 = v4[v1];
v2.setAccessible(true);
if(v2.getType().toString().endsWith(arg5)) {
return v2;
}
}
return null;
}
}
2.BuildHook.java
这里包含手机的一些硬件信息的修改和android系统属性的修改,但是缺点的话SystemProperties的get方法很多大厂都是通过native去调用底层的native_get方法检测,这样就要配合其他方式去修改了。
public class BuildHook extends MethodHook
{
public BuildHook(XC_LoadPackage.LoadPackageParam paramLoadPackageParam, DeviceInfoModel deviceInfoModel, final Context context) {
super(paramLoadPackageParam, deviceInfoModel);
hookMethodByClassMethodName("android.os.SystemProperties", "set");
hookMethodByClassMethodName("android.os.SystemProperties", "get");
//hookMethodByClassMethodName("android.os.SystemProperties", "getString");
hookMethodByClassMethodName(Settings.System.class.getName(), "putStringForUser");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
hookMethodByClassMethodName(Settings.Secure.class.getName(), "putStringForUser");
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
hookMethodByClassMethodName(Settings.Global.class.getName(), "putStringForUser");
}
hookMethodByClassMethodName(Settings.System.class.getName(), "getStringForUser");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
hookMethodByClassMethodName(Settings.Secure.class.getName(), "getStringForUser");
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
hookMethodByClassMethodName(Settings.Global.class.getName(), "getStringForUser");
}
hookMethodByClassMethodName("android.provider.Settings$NameValueCache", "getStringForUser");
}
@SuppressLint("SuspiciousIndentation")
private void setBuilds() {
XposedHelpers.setStaticObjectField(Build.class, "BOOTLOADER", "unkown");
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildBrand()))
XposedHelpers.setStaticObjectField(Build.class, "BRAND", this.deviceInfoModel.getBuildBrand());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildBoard()))
XposedHelpers.setStaticObjectField(Build.class, "BOARD", this.deviceInfoModel.getBuildBoard());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildModel()))
XposedHelpers.setStaticObjectField(Build.class, "MODEL", this.deviceInfoModel.getBuildModel());
if (!TextUtils.isEmpty(this.deviceInfoModel.getDisplayId()))
XposedHelpers.setStaticObjectField(Build.class, "DISPLAY", this.deviceInfoModel.getDisplayId());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildProduct()))
XposedHelpers.setStaticObjectField(Build.class, "PRODUCT", this.deviceInfoModel.getBuildProduct());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildManufacturer()))
XposedHelpers.setStaticObjectField(Build.class, "MANUFACTURER", this.deviceInfoModel.getBuildManufacturer());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildDevice()))
XposedHelpers.setStaticObjectField(Build.class, "DEVICE", this.deviceInfoModel.getBuildDevice());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildRelease()))
XposedHelpers.setStaticObjectField(Build.VERSION.class, "RELEASE", this.deviceInfoModel.getBuildRelease());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildSdk()))
XposedHelpers.setStaticObjectField(Build.VERSION.class, "SDK", this.deviceInfoModel.getBuildSdk());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildFingerprint()))
XposedHelpers.setStaticObjectField(Build.class, "FINGERPRINT", this.deviceInfoModel.getBuildFingerprint());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildHardware()))
XposedHelpers.setStaticObjectField(Build.class, "HARDWARE", this.deviceInfoModel.getBuildHardware());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildCodename()))
XposedHelpers.setStaticObjectField(Build.VERSION.class, "CODENAME", this.deviceInfoModel.getBuildCodename());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildIncremental()))
XposedHelpers.setStaticObjectField(Build.VERSION.class, "INCREMENTAL", this.deviceInfoModel.getBuildIncremental());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildHost()))
XposedHelpers.setStaticObjectField(Build.class, "HOST", this.deviceInfoModel.getBuildHost());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildId()))
XposedHelpers.setStaticObjectField(Build.class, "ID", this.deviceInfoModel.getBuildId());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildType()))
XposedHelpers.setStaticObjectField(Build.class, "TYPE", this.deviceInfoModel.getBuildType());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildUser()))
XposedHelpers.setStaticObjectField(Build.class, "USER", this.deviceInfoModel.getBuildUser());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildSerialno()))
XposedHelpers.setStaticObjectField(Build.class, "SERIAL", this.deviceInfoModel.getBuildSerialno());
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildTags()))
XposedHelpers.setStaticObjectField(Build.class, "TAGS", this.deviceInfoModel.getBuildTags());
if (this.deviceInfoModel.getBuildUtc() > 0L)
XposedHelpers.setStaticObjectField(Build.class, "TIME", Long.valueOf(this.deviceInfoModel.getBuildUtc()));
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildAbi())){
XposedHelpers.setStaticObjectField(Build.class, "CPU_ABI", this.deviceInfoModel.getBuildAbi());
}
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildAbi2())){
XposedHelpers.setStaticObjectField(Build.class, "CPU_ABI2", this.deviceInfoModel.getBuildAbi2());
}
if (!TextUtils.isEmpty(this.deviceInfoModel.getBuildSdk())){
if(Integer.parseInt(this.deviceInfoModel.getBuildSdk())<26){
XposedHelpers.setStaticIntField(Build.VERSION.class, "SDK_INT", Integer.parseInt(this.deviceInfoModel.getBuildSdk()));
}
}
if (!TextUtils.isEmpty(this.deviceInfoModel.getSecurity_patch())){
XposedHelpers.setStaticObjectField(Build.VERSION.class, "SECURITY_PATCH", this.deviceInfoModel.getSecurity_patch());
}
}
public static Object IIl1lIIlll(DeviceInfoModel arg4, String arg5) {
if("ro.product.model".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getBuildModel())) {
return arg4.getBuildModel();
}
}
if("ro.boot.serialno".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getSerialno())) {
Log.i("TestYY", "ro.boot.serialno");
return arg4.getSerialno();
}
}
else if("ro.build.id".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getBuildId())) {
return arg4.getBuildId();
}
}
else if("ro.build.display.id".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getDisplayId())) {
return arg4.getDisplayId();
}
}
else if("ro.build.type".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getBuildType())) {
return arg4.getBuildType();
}
}
else if("ro.build.user".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getBuildUser())) {
return arg4.getBuildUser();
}
}
else if("ro.build.host".equals(arg5)) {
if(!TextUtils.isEmpty(arg4.getBuildHost())) {