IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 灵魂一问,如何彻底防止反编译?,阿里P8成长路线 -> 正文阅读

[移动开发]灵魂一问,如何彻底防止反编译?,阿里P8成长路线

// 这是context赋值
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
// 创建两个文件夹payload_odex、payload_lib,私有的,可写的文件目录
File odex = this.getDir(“payload_odex”, MODE_PRIVATE);
File libs = this.getDir(“payload_lib”, MODE_PRIVATE);
odexPath = odex.getAbsolutePath();
libPath = libs.getAbsolutePath();
apkFileName = odex.getAbsolutePath() + “/payload.apk”;
File dexFile = new File(apkFileName);
Log.i(“demo”, “apk size:”+dexFile.length());
if (!dexFile.exists())
{
dexFile.createNewFile(); //在payload_odex文件夹内,创建payload.apk
// 读取程序classes.dex文件
byte[] dexdata = this.readDexFileFromApk();

// 分离出解壳后的apk文件已用于动态加载
this.splitPayLoadFromDex(dexdata);
}
// 配置动态加载环境
Object currentActivityThread = RefInvoke.invokeStaticMethod(
“android.app.ActivityThread”, “currentActivityThread”,
new Class[] {}, new Object[] {});//获取主线程对象
String packageName = this.getPackageName();//当前apk的包名
ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect(
“android.app.ActivityThread”, currentActivityThread,
“mPackages”);
WeakReference wr = (WeakReference) mPackages.get(packageName);
// 创建被加壳apk的DexClassLoader对象 加载apk内的类和本地代码(c/c++代码)
DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,
libPath, (ClassLoader) RefInvoke.getFieldOjbect(
“android.app.LoadedApk”, wr.get(), “mClassLoader”));
//把当前进程的mClassLoader设置成了被加壳apk的DexClassLoader
RefInvoke.setFieldOjbect(“android.app.LoadedApk”, “mClassLoader”,
wr.get(), dLoader);

Log.i(“demo”,“classloader:”+dLoader);
try{
Object actObj = dLoader.loadClass(“com.example.sourceapk.MainActivity”);
Log.i(“demo”, “actObj:”+actObj);
}catch(Exception e){
Log.i(“demo”, “activity:”+Log.getStackTraceString(e));
}
} catch (Exception e) {
Log.i(“demo”, “error:”+Log.getStackTraceString(e));
e.printStackTrace();
}
}

这里需要注意的一个问题,就是我们需要找到一个时机,就是在壳程序还没有运行起来的时候,来加载源程序的APK,执行它的onCreate方法,那么这个时机不能太晚,不然的话,就是运行壳程序,而不是源程序了。查看源码我们知道。Application中有一个方法:attachBaseContext这个方法,它在Application的onCreate方法执行前就会执行了,所以我们的工作就需要在这里进行。
A) 从APK中获取到DEX文件

/**

  • 从apk包里面获取dex文件内容(byte)
  • @return
  • @throws IOException
    */
    private byte[] readDexFileFromApk() throws IOException {
    ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();
    ZipInputStream localZipInputStream = new ZipInputStream(
    new BufferedInputStream(new FileInputStream(
    this.getApplicationInfo().sourceDir)));
    while (true) {
    ZipEntry localZipEntry = localZipInputStream.getNextEntry();
    if (localZipEntry == null) {
    localZipInputStream.close();
    break;
    }
    if (localZipEntry.getName().equals(“classes.dex”)) {
    byte[] arrayOfByte = new byte[1024];
    while (true) {
    int i = localZipInputStream.read(arrayOfByte);
    if (i == -1)
    break;
    dexByteArrayOutputStream.write(arrayOfByte, 0, i);
    }
    }
    localZipInputStream.closeEntry();
    }
    localZipInputStream.close();
    return dexByteArrayOutputStream.toByteArray();
    }

B) 从壳程序DEX中得到源程序APK文件

/**

  • 释放被加壳的apk文件,so文件
  • @param data
  • @throws IOException
    */
    private void splitPayLoadFromDex(byte[] apkdata) throws IOException {
    int ablen = apkdata.length;
    //取被加壳apk的长度 这里的长度取值,对应加壳时长度的赋值都可以做些简化
    byte[] dexlen = new byte[4];
    System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);
    ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);
    DataInputStream in = new DataInputStream(bais);
    int readInt = in.readInt();
    System.out.println(Integer.toHexString(readInt));
    byte[] newdex = new byte[readInt];
    //把被加壳的源程序apk内容拷贝到newdex中
    System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);
    //这里应该加上对于apk的解密操作,若加壳是加密处理的话

// 对源程序Apk进行解密
newdex = decrypt(newdex);

// 写入apk文件
File file = new File(apkFileName);
try {
FileOutputStream localFileOutputStream = new FileOutputStream(file);
localFileOutputStream.write(newdex);
localFileOutputStream.close();
} catch (IOException localIOException) {
throw new RuntimeException(localIOException);
}

// 分析被加壳的apk文件
ZipInputStream localZipInputStream = new ZipInputStream(
new BufferedInputStream(new FileInputStream(file)));
while (true) {
ZipEntry localZipEntry = localZipInputStream.getNextEntry(); // 这个也遍历子目录
if (localZipEntry == null) {
localZipInputStream.close();
break;
}
// 取出被加壳apk用到的so文件,放到libPath中(data/data/包名/payload_lib)
String name = localZipEntry.getName();
if (name.startsWith(“lib/”) && name.endsWith(".so")) {
File storeFile = new File(libPath + “/”

  • name.substring(name.lastIndexOf(’/’)));
    storeFile.createNewFile();
    FileOutputStream fos = new FileOutputStream(storeFile);
    byte[] arrayOfByte = new byte[1024];
    while (true) {
    int i = localZipInputStream.read(arrayOfByte);
    if (i == -1)
    break;
    fos.write(arrayOfByte, 0, i);
    }
    fos.flush();
    fos.close();
    }
    localZipInputStream.closeEntry();
    }
    localZipInputStream.close();
    }

C) 解密源程序APK

//直接返回数据,读者可以添加自己解密方法
private byte[] decrypt(byte[] srcdata) {
for(int i=0;i<srcdata.length;i++){
srcdata[i] = (byte)(0xFF ^ srcdata[i]);
}
return srcdata;
}

  • 找到源程序的Application程序,让其运行

@Override
public void onCreate() {
{
//loadResources(apkFileName);
Log.i(“demo”, “onCreate”);
// 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。
String appClassName = null;
try {
ApplicationInfo ai = this.getPackageManager()
.getApplicationInfo(this.getPackageName(),
PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
if (bundle != null && bundle.containsKey(“APPLICATION_CLASS_NAME”)) {
appClassName = bundle.getString(“APPLICATION_CLASS_NAME”);//className 是配置在xml文件中的。
} else {
Log.i(“demo”, “have no application class name”);
return;
}
} catch (NameNotFoundException e) {
Log.i(“demo”, “error:”+Log.getStackTraceString(e));
e.printStackTrace();
}
//有值的话调用该Applicaiton
Object currentActivityThread = RefInvoke.invokeStaticMethod(
“android.app.ActivityThread”, “currentActivityThread”,
new Class[] {}, new Object[] {});
Object mBoundApplication = RefInvoke.getFieldOjbect(
“android.app.ActivityThread”, currentActivityThread,
“mBoundApplication”);
Object loadedApkInfo = RefInvoke.getFieldOjbect(
“android.app.ActivityThread$AppBindData”,
mBoundApplication, “info”);
//把当前进程的mApplication 设置成了null
RefInvoke.setFieldOjbect(“android.app.LoadedApk”, “mApplication”,
loadedApkInfo, null);
Object oldApplication = RefInvoke.getFieldOjbect(
“android.app.ActivityThread”, currentActivityThread,
“mInitialApplication”);
//http://www.codeceo.com/article/android-context.html
ArrayList mAllApplications = (ArrayList) RefInvoke
.getFieldOjbect(“android.app.ActivityThread”,
currentActivityThread, “mAllApplications”);
mAllApplications.remove(oldApplication); // 删除oldApplication

ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke
.getFieldOjbect(“android.app.LoadedApk”, loadedApkInfo,
“mApplicationInfo”);
ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke
.getFieldOjbect(“android.app.ActivityThread$AppBindData”,
mBoundApplication, “appInfo”);
appinfo_In_LoadedApk.className = appClassName;
appinfo_In_AppBindData.className = appClassName;
Application app = (Application) RefInvoke.invokeMethod(
“android.app.LoadedApk”, “makeApplication”, loadedApkInfo,
new Class[] { boolean.class, Instrumentation.class },
new Object[] { false, null }); // 执行 makeApplication(false,null)
RefInvoke.setFieldOjbect(“android.app.ActivityThread”,
“mInitialApplication”, currentActivityThread, app);

ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldOjbect(
“android.app.ActivityThread”, currentActivityThread,
“mProviderMap”);
Iterator it = mProviderMap.values().iterator();
while (it.hasNext()) {
Object providerClientRecord = it.next();
Object localProvider = RefInvoke.getFieldOjbect(
“android.app.ActivityThread$ProviderClientRecord”,
providerClientRecord, “mLocalProvider”);
RefInvoke.setFieldOjbect(“android.content.ContentProvider”,
“mContext”, localProvider, app);
}
Log.i(“demo”, “app:”+app);
app.onCreate();
}
}

直接在壳程序的Application中的onCreate方法中进行就可以了。这里还可以看到是通过AndroidManifest.xml中的meta标签获取源程序APK中的Application对象的。 下面来看一下AndroidManifest.xml文件中的内容:

<applicatio
n
android:allowBackup=“true”
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name=“com.example.packapk.ProxyApplication”>

这里我们定义了源程序APK的Application类名。?项目下载

三.运行程序

下面就看看程序的运行步骤:

  • 第一步:得到源程序APK文件和壳程序的DEX文件 运行源程序和壳程序项目,之后得到这两个文件(将壳程序的classes.dex文件改名为SourceApk.dex),然后使用加密工具进行加壳。
  • 第二步:替换壳程序中的classes.dex文件 我们在第一步中得到加壳之后的classes.dex文件之后,将其与PackApk.apk中的原classes.dex文件替换。
  • 第三步:在第二步的时候得到替换之后的PackApk.apk文件,这个文件因为被修改了,所以我们需要重新对它签名,不然运行也是报错的。 签名之后的文件就可以运行了,效果如下:

文件改名为SourceApk.dex),然后使用加密工具进行加壳。

  • 第二步:替换壳程序中的classes.dex文件 我们在第一步中得到加壳之后的classes.dex文件之后,将其与PackApk.apk中的原classes.dex文件替换。
  • 第三步:在第二步的时候得到替换之后的PackApk.apk文件,这个文件因为被修改了,所以我们需要重新对它签名,不然运行也是报错的。 签名之后的文件就可以运行了,效果如下:
    [外链图片转存中…(img-XtvfYlCr-1643027731256)]
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:42:32  更:2022-01-25 10:43:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:50:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码