上一篇文章地址
https://blog.csdn.net/weixin_44238683/article/details/118397065
讲述内容:
- smali
- 安卓加载原理
- 破解安装签名
一、APP加壳加固
常见加固:
- 腾讯乐加固
- 360加固
- 梆梆
- 爱加密
1、加固app特征?
常见不同厂商对APP的加固特征
爱加密:libexec.so,libexecmain.so,ijiami.dat
梆梆: libsecexe.so,libsecmain.so , libDexHelper.so libSecShell.so
360:libprotectClass.so,libjiagu.so,libjiagu_art.so,libjiagu_x86.so
百度:libbaiduprotect.so
腾讯:libshellx-2.10.6.0.so,libBugly.so,libtup.so, libexec.so,libshell.so stub_tengxun
网易易盾:libnesec.so
主要关注腾讯加固方案,因为腾讯市场广
2、第三方加固/自己加固
一般在哪家应用市场发布,就用哪家的加固方案
有收费跟免费,一般选择免费的加固方案
3、为什么要加固
一定程度保护源代码
二、如何确认是否加固
引力播 app
链接:https://pan.baidu.com/s/1pV03Fy_fV1polyqRCUxYfA
提取码:tqxf
1、方式一:观察so文件
先将apk文件改名为zip并且解压,进入lib文件观察.so
文件,发现是腾讯乐加固
2、方式二:查看classes.dex
jadx工具查看,可以发现都不是原app的classes逻辑
3、方式三:查看androidmainfest.xml
删除zip解压的文件,用aoktool反编译,可以看到 application 启动的是这类函数,可以通过名称TxAppEntry
都知道是加密的
app启动默认流程,是进入application,但是加固的是进入设计好的加固流程,进入内层壳application,oncreate方法解密脱壳操作,才会到真正的原流程,所以在xml文件确定 壳的application
,“我们要清楚壳一定会调用原有application
”
可以看到 meta-data
里面 ,android:value =
才是真正的application
启动,是经过腾讯了加固后才执行的
三、加固加壳原理
加固/加壳后,会生成新的dex文件
加固的过程中需要三个对象:
1、需要加密的Apk(源Apk)
2、壳程序Apk(负责解密Apk工作)
3、加密工具(将源Apk进行加密和壳Dex合并成新的Dex)
1、查看加固/加壳函数
来到,加固/加壳函数,分析下TxAppEntry
,一般加壳会做的事情,打开jadx工具,搜下TxAppEntry
大概浏览一下全部内容,发现有对内存什么的进行操作,看不懂没关系,到最后有一个attachBaseContext
上下文,这个东西可以做hook,
本篇暂时不用hook,可以自行了解
2、加壳函数运行方式
那么通过读上面的文件,其实,整个加壳是怎么保证原本的dex,还存在的呢,在此文件中,会有一个file_size
,假设大小为2M,那么加壳函数,从指定开始就只读取2M,这样就对原代码造成英雄,最后输出成加密的apk
3、逆向脱壳方式
反编译/Hook技术和动态调试
Hook:先取得要Hook函数/方法的控制权,不用破坏程序
动态调试:反调试,汇编,计算内存地址
四、Hook技术
改变程序执行流程的一种技术 在函数被调用前,通过HOOK技术,先得到该函数的控制权,实现该函数的逻辑改写
(比如,函数是请求参数,需要手动刷新才显示,那么改为自动刷新)
分为:
- Hook Java层
- Hook Native层(.so库)
java层容易,native层难度较大,涉及技术太多
五、脱壳
1、脱壳原理:
在壳APK解密源APK后,源APK被加载前,拦截这个过程中的系统函数 把内存种Dex dump出来
2、手动脱壳:
通过动态调试,跟踪计算Dex源文件的内存偏移地址,从内存中Dump出Dex文件
难度大,寄存器,汇编,反调试,反读写
IDA
3、工具脱壳:
HOOK技术/内存特征寻找
简单易操作
基于xposed 脱壳工具:(傻瓜式)
Fdex2:Hook ClassLoader loadClass方法 通用脱壳
dumpDex:https://github.com/WrBug/dumpDex
其它脱壳工具,比如反射大师
重写底层函数:
DexExtractor:重写libdvm.so dexFileParse函数。脱梆梆 爱加密 libart.so 安卓7.0
逆向框架: 筑好底层 提供开发接口
xposed(Java 编译)
frida(Python Javascript 代码注入) 非常强大
主要系统函数都已HOOK/基于xposed frida开发脱壳工具/有大神已开发上层应用模块
开发工具要了解 APP启动 加载过程的原理,细节
脱老版本APP
使用已有工具,技术晚半代至一代
icity脱壳 icity signature实现
Java在线执行
http://www.dooccn.com/java/
六、脱壳实操
1、脱壳所需安装(xposed,脱壳工具,app)
引力播app
打开夜神模拟器,到应用市场安装xposed
安装脱壳工具
链接:https://pan.baidu.com/s/1gJh7rPJiRct-pp_LCXCnqg
提取码:2xnl
使用教程
https://www.lmrjk.cn/post-598.html
2、进行脱壳,得到dex
按着下面操作,得到脱壳后的 dex路径
3、从夜神复制classes.dex到电脑,并且jadx打开
在输出路径,选中,然后来到夜神共享电脑文件路径,粘贴文件
打开电脑,打开jadx工具,将classes.dex拖动到工具
4、分析dex文件
在前面,分析Andriomainfest.xml的时候知道,真正的启动函数在这里MyApplication
于是工具搜索,打开代码,得到真正的启动函数
5、fiddler抓包工具,分析请求接口参数
分别点了2个栏目的新闻
发现,每次请求只有signature变化,其它都没有变化
6、定位signature,分析生成方式原理
搜索发现很多多,经验来说,最好一个一个看过去,就会得到灵感
每个都看下,能得到启发,看到下面这些,sys,sysVersion等等参数都在里面,那么久锁定这个,转到代码处
发现都是a,b,这是经过混淆的
利用jadx工具反混淆(jadx工具反混淆并不能真正返回函数名称,只是唯一标识)
重新定位signature的代码处
package com.iCitySuzhou.suzhou001.p192d;
import android.os.Build;
import com.hualong.framework.C2381a;
import com.hualong.framework.p162b.C2387a;
import com.hualong.framework.p164d.C2394a;
import com.iCitySuzhou.suzhou001.MyApplication;
import java.io.IOException;
import p000a.C0104aa;
import p000a.C0110ac;
import p000a.C0157u;
/* renamed from: com.iCitySuzhou.suzhou001.d.d */
public class C2628d implements C0157u {
/* renamed from: a */
public C0110ac mo26a(C0157u.C0158a aVar) throws IOException {
C0104aa a = aVar.mo103a();
String udid = MyApplication.getInstance().getUDID();
String valueOf = String.valueOf(System.currentTimeMillis() / 1000);
C0104aa a2 = a.mo284e().mo295b("sys", "Android").mo295b("sysVersion", Build.VERSION.RELEASE).mo295b("appVersion", MyApplication.getInstance().getAppVersion()).mo295b("appVersionCode", String.valueOf(MyApplication.getInstance().getAppVersionCode())).mo295b("udid", udid).mo295b("clientType", "android").mo295b("timestamp", valueOf).mo295b("signature", m12059a(udid, valueOf)).mo293a();
C0110ac a3 = aVar.mo104a(a2);
if (C2381a.m11208a()) {
C2394a.m11269c("LoggingInterceptor", " ");
C2394a.m11269c("LoggingInterceptor", " ");
C2394a.m11269c("LoggingInterceptor", String.format("[%s] %s, %d in %.2f ms", new Object[]{a2.mo281b(), a2.mo279a(), Integer.valueOf(a3.mo302b()), Float.valueOf(((float) (a3.mo313l() - a3.mo312k())) / 1000.0f)}));
C2394a.m11269c("LoggingInterceptor", "-----------------------------------");
C2394a.m11269c("LoggingInterceptor", " Request Headers ");
C2394a.m11269c("LoggingInterceptor", "-----------------------------------");
C2394a.m11269c("LoggingInterceptor", String.format("%s", new Object[]{a2.mo282c()}));
C2394a.m11269c("LoggingInterceptor", "-----------------------------------");
C2394a.m11269c("LoggingInterceptor", " Response Headers ");
C2394a.m11269c("LoggingInterceptor", "-----------------------------------");
C2394a.m11269c("LoggingInterceptor", String.format("%s", new Object[]{a3.mo307f()}));
C2394a.m11269c("LoggingInterceptor", " ");
C2394a.m11269c("LoggingInterceptor", " ");
}
return a3;
}
/* renamed from: a */
public static String m12059a(String str, String str2) {
return C2387a.m11218a(String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}));
}
}
发现其原理
其中valueOf为时间戳
mo295b
,大概意思就是将udid
与时间戳
传入此mo295b
函数,进行处理,定位到mo295b
如下
那么其中m1121
函数是格式化字符串,拼接
- uuid设备号
- 时间戳
- 固定值:f1190aca-d08e-4041-8666-29931cd89dde
那么查看m1121
函数,大概是对拼接好的字符串进行位运算
至此得到生成原理
7、还原signature方法
可以通过以下两种方式:
1、如果java会的话,可以读懂java,自己写java代码
2、可以打包此处代码,通过python调用
那么选择,打包,供python调用
打包代码,扣代码不能扣无用的函数
7.1 扣出,运算字符串代码,保存为.java
文件
命名为MySig.java (注意!!!!,命名的类方法需要与java文件保持一直,不然编译不了
)
import java.security.MessageDigest;
/* renamed from: com.hualong.framework.b.a */
public class MySig{
/* renamed from: a */
public static String m11218a(String str) {
if (str == null) {
return null;
}
StringBuffer stringBuffer = new StringBuffer();
try {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(str.getBytes());
for (byte b : instance.digest()) {
stringBuffer.append(Integer.toString((b >>> 4) & 15, 16)).append(Integer.toString(b & 15, 16));
}
} catch (Exception e) {
}
return stringBuffer.toString();
}
}
7.2 编译java文件
编译Java成class
javac MySig.java
把class做成jar
jar cvf MySig.jar *
7.3 新建MySig.py,利用jpype调用jar文件
安装jpype(直接pip install 会报错,参考下面地址)
jpype1 https://blog.csdn.net/weixin_43840367/article/details/90240943
新建MySig.py
from jpype import *
import requests
import time
uuid = 'IMEI863254012240478-IMSI460072240477434'
stamp = str(int(time.time()))
ori_sig = uuid + '&&' + stamp + '&&' + 'f1190aca-d08e-4041-8666-29931cd89dde'
# java虚拟机位置
jvmpath = r'C:\Program Files\Java\jdk-16.0.1\bin\server\jvm.dll'
# java包所在路径
jarpath = 'G:/python_pro/'
# 启动虚拟机,打开jar包
startJVM(jvmpath, "-ea", "-Djava.class=%s" % (jarpath + 'MySig.jar'))
# 打印出路径
print('aa:', "-Djava.class=%s" % (jarpath + 'MySig.jar'))
# 定位到打包函数里面的类方法
JDClass = JClass('MySig')
# 创建类对象
jd = JDClass()
# 执行方法
encry_sig = jd.m11218a(ori_sig)
# 系统打印
java.lang.System.out.println(encry_sig)
# 打印
print(encry_sig)
# 关闭虚拟机
shutdownJVM()
成功!!!!!!!!!!!!!!!!!!!!
给个赞吧!!