更新代码
481
README.md
@@ -62,4 +62,483 @@ Android API Security(.so),安卓APP/API安全加密so库,防二次打包,
|
|||||||
APISecurity.init(this)//验证三步走
|
APISecurity.init(this)//验证三步走
|
||||||
1.验证签名是否符合自己预设
|
1.验证签名是否符合自己预设
|
||||||
2.验证包名
|
2.验证包名
|
||||||
3验证apk源文件签名信息
|
3验证apk源文件签名信息
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
过以下命令就可以拿到指定类的所有属性、方法的签名了,很方便有木有?!
|
||||||
|
javap -s -p 完整类名
|
||||||
|
descriptor就是我们需要的签名了,注意签名中末尾的分号不能省略
|
||||||
|
方法签名的规律就是,括号不可以省略:
|
||||||
|
(参数类型签名)返回值类型签名
|
||||||
|
C/C++访问Java的属性、方法
|
||||||
|
有以下几种情况:
|
||||||
|
|
||||||
|
访问Java类的非静态属性。
|
||||||
|
|
||||||
|
访问Java类的静态属性。
|
||||||
|
|
||||||
|
访问Java类的非静态方法。
|
||||||
|
|
||||||
|
访问Java类的静态方法。
|
||||||
|
|
||||||
|
间接访问Java类的父类的方法。
|
||||||
|
|
||||||
|
访问Java类的构造方法。
|
||||||
|
|
||||||
|
一、访问Java的非静态属性
|
||||||
|
Java方法中,通过调用accessField,利用C修改静态属性
|
||||||
|
|
||||||
|
public String str = "Li lu";
|
||||||
|
|
||||||
|
//访问非静态属性str,修改它的值
|
||||||
|
public native void accessField();
|
||||||
|
C代码如下:(头文件可以不写,直接写实现)
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_accessField
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
|
||||||
|
//通过对象拿到Class
|
||||||
|
jclass clz = (*env)->GetObjectClass(env, jobj);
|
||||||
|
//拿到对应属性的ID
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, clz, "str", "Ljava/lang/String;");
|
||||||
|
//通过属性ID拿到属性的值
|
||||||
|
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
|
||||||
|
|
||||||
|
//通过Java字符串拿到C字符串,第三个参数是一个出参,用来告诉我们GetStringUTFChars内部是否复制了一份字符串
|
||||||
|
//如果没有复制,那么出参为isCopy,这时候就不能修改字符串的值了,因为Java中常量池中的字符串是不允许修改的(但是jstr可以指向另外一个字符串)
|
||||||
|
char* cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
|
||||||
|
//在C层修改这个属性的值
|
||||||
|
char res[20] = "I love you : ";
|
||||||
|
strcat(res, cstr);
|
||||||
|
|
||||||
|
//重新生成Java的字符串,并且设置给对应的属性
|
||||||
|
jstring jstr_new = (*env)->NewStringUTF(env, res);
|
||||||
|
(*env)->SetObjectField(env, jobj, fid, jstr_new);
|
||||||
|
|
||||||
|
//最后释放资源,通知垃圾回收器来回收
|
||||||
|
//良好的习惯就是,每次GetStringUTFChars,结束的时候都有一个ReleaseStringUTFChars与之呼应
|
||||||
|
(*env)->ReleaseStringUTFChars(env, jstr, cstr);
|
||||||
|
}
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
System.out.println(test.str);
|
||||||
|
//修改非静态属性str
|
||||||
|
test.accessField();
|
||||||
|
System.out.println(test.str);
|
||||||
|
|
||||||
|
}
|
||||||
|
二、访问Java的静态属性
|
||||||
|
Java代码如下:
|
||||||
|
|
||||||
|
//访问静态属性NUM,修改它的值
|
||||||
|
public static int NUM = 1;
|
||||||
|
|
||||||
|
public native void accessStaticField();
|
||||||
|
C代码如下:
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_accessStaticField
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
//与上面类似,只不过是某些方法需要加上Static
|
||||||
|
jclass clz = (*env)->GetObjectClass(env, jobj);
|
||||||
|
jfieldID fid = (*env)->GetStaticFieldID(env, clz, "NUM", "I");
|
||||||
|
jint jInt = (*env)->GetStaticIntField(env, clz, fid);
|
||||||
|
jInt++;
|
||||||
|
(*env)->SetStaticIntField(env, clz, fid, jInt);
|
||||||
|
}
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
System.out.println(NUM);
|
||||||
|
test.accessStaticField();
|
||||||
|
System.out.println(NUM);
|
||||||
|
|
||||||
|
}
|
||||||
|
三、访问Java的非静态方法
|
||||||
|
Java代码如下,通过调用accessMethod,在底层用C语言调用genRandomInt方法
|
||||||
|
|
||||||
|
//产生指定范围的随机数
|
||||||
|
public int genRandomInt(int max){
|
||||||
|
System.out.println("genRandomInt 执行了...max = "+ max);
|
||||||
|
return new Random().nextInt(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void accessMethod();
|
||||||
|
C代码如下:
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_accessMethod
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
jclass clz = (*env)->GetObjectClass(env, jobj);
|
||||||
|
//拿到方法的ID,最后一个参数是方法的签名
|
||||||
|
jmethodID mid = (*env)->GetMethodID(env, clz, "genRandomInt", "(I)I");
|
||||||
|
//调用该方法,最后一个是可变参数,就是调用该方法所传入的参数
|
||||||
|
//套路是如果返回是:Call返回类型Method
|
||||||
|
jint jInt = (*env)->CallIntMethod(env, jobj, mid, 100);
|
||||||
|
printf("output from C : %d", jInt);
|
||||||
|
}
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
test.accessMethod();
|
||||||
|
|
||||||
|
}
|
||||||
|
四、访问Java的静态方法
|
||||||
|
Java代码如下,通过调用accessStaticMethod,在底层用C语言调用getUUID方法
|
||||||
|
|
||||||
|
public native void accessStaticMethod();
|
||||||
|
|
||||||
|
//产生UUID字符串
|
||||||
|
public static String getUUID(){
|
||||||
|
System.out.println("getUUID 执行了...");
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
C代码如下:
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_accessStaticMethod
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
jclass clz = (*env)->GetObjectClass(env, jobj);
|
||||||
|
jmethodID mid = (*env)->GetStaticMethodID(env, clz, "getUUID", "()Ljava/lang/String;");
|
||||||
|
|
||||||
|
//调用java的静态方法,拿到返回值
|
||||||
|
jstring jstr = (*env)->CallStaticObjectMethod(env, clz, mid);
|
||||||
|
|
||||||
|
//把拿到的Java字符串转换为C的字符串
|
||||||
|
char* cstr= (*env)->GetStringUTFChars(env, jstr, NULL);
|
||||||
|
|
||||||
|
//后续操作,产生以UUID为文件名的文件
|
||||||
|
char fielName[100];
|
||||||
|
sprintf(fielName, "D:\\%s.txt", cstr);
|
||||||
|
FILE* f = fopen(fielName, "w");
|
||||||
|
fputs(cstr, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
printf("output from C : File had saved", jstr);
|
||||||
|
}
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
test.accessStaticMethod();
|
||||||
|
|
||||||
|
}
|
||||||
|
五、间接访问Java类的父类的方法
|
||||||
|
Java代码如下:
|
||||||
|
|
||||||
|
父类:
|
||||||
|
|
||||||
|
package com.test;
|
||||||
|
|
||||||
|
public class Human {
|
||||||
|
protected void speek() {
|
||||||
|
System.out.println("Human Speek");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
子类:
|
||||||
|
|
||||||
|
package com.test;
|
||||||
|
|
||||||
|
public class Man extends Human {
|
||||||
|
@Override
|
||||||
|
protected void speek() {
|
||||||
|
// 可以通过super关键字来访问父类的方法
|
||||||
|
// super.speek();
|
||||||
|
System.out.println("Man Speek");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
在TestJni类中有Human属性:
|
||||||
|
|
||||||
|
//父类的引用指向子类的对象
|
||||||
|
Human man= new Man();
|
||||||
|
|
||||||
|
public native void accessNonvirtualMethod();
|
||||||
|
如果是直接使用man.speek()的话,访问的是子类Man的方法
|
||||||
|
但是通过底层C的方式可以间接访问到父类Human的方法,跳过子类的实现,甚至你可以直接哪个父类(如果父类有多个的话),这是Java做不到的。
|
||||||
|
|
||||||
|
下面是C代码实现,无非就是属性和方法的访问:
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_accessNonvirtualMethod
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
//先拿到属性man
|
||||||
|
jclass clz=(*env)->GetObjectClass(env, jobj);
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, clz, "man", "Lcom/test/Human;");
|
||||||
|
jobject man = (*env)->GetObjectField(env, jobj, fid);
|
||||||
|
|
||||||
|
//拿到父类的类,以及speek的方法id
|
||||||
|
jclass clz_human = (*env)->FindClass(env, "com/test/Human");
|
||||||
|
jmethodID mid = (*env)->GetMethodID(env, clz_human, "speek", "()V");
|
||||||
|
|
||||||
|
//调用自己的speek实现
|
||||||
|
(*env)->CallVoidMethod(env, man, mid);
|
||||||
|
//调用父类的speek实现
|
||||||
|
(*env)->CallNonvirtualVoidMethod(env, man, clz_human, mid);
|
||||||
|
}
|
||||||
|
当有这个类的对象的时候,使用(*env)->GetObjectClass(),相当于Java中的test.getClass()
|
||||||
|
|
||||||
|
当有没有这个类的对象的时候,(*env)->FindClass(),相当于Java中的Class.forName("com.test.TestJni")
|
||||||
|
|
||||||
|
这里直接使用CallVoidMethod,虽然传进去的是父类的Method ID,但是访问的让然是子类的实现。
|
||||||
|
|
||||||
|
最后,通过CallNonvirtualVoidMethod,访问不覆盖的父类方法(C++使用virtual关键字来覆盖父类的实现),当然你也可以指定哪个父类(如果有多个父类的话)。
|
||||||
|
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
//这时候是调用子类Man的方法
|
||||||
|
test.man.speek();
|
||||||
|
//但是通过JNI的方式,可以访问到父类的speek方法
|
||||||
|
test.accessNonvirtualMethod();
|
||||||
|
|
||||||
|
}
|
||||||
|
六、访问Java类的构造方法
|
||||||
|
Java代码如下,通过调用accessConstructor,在底层用C语言调用java.util.Date产生一个当前的时间戳,并且返回。
|
||||||
|
|
||||||
|
//调用Date的构造函数
|
||||||
|
public native long accessConstructor();
|
||||||
|
C代码如下:
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_com_test_JniTest_accessConstructor
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
|
||||||
|
jclass clz_date = (*env)->FindClass(env, "java/util/Date");
|
||||||
|
//构造方法的函数名的格式是:<init>
|
||||||
|
//不能写类名,因为构造方法函数名都一样区分不了,只能通过参数列表(签名)区分
|
||||||
|
jmethodID mid_Date = (*env)->GetMethodID(env, clz_date, "<init>", "()V");;
|
||||||
|
|
||||||
|
//调用构造函数
|
||||||
|
jobject date = (*env)->NewObject(env, clz_date, mid_Date);
|
||||||
|
|
||||||
|
//注意签名,返回值long的属性签名是J
|
||||||
|
jmethodID mid_getTime= (*env)->GetMethodID(env, clz_date, "getTime", "()J");
|
||||||
|
//调用getTime方法
|
||||||
|
jlong jtime = (*env)->CallLongMethod(env, date, mid_getTime);
|
||||||
|
|
||||||
|
return jtime;
|
||||||
|
}
|
||||||
|
最后在Java中测试:
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
//直接在Java中构造Date然后调用getTime
|
||||||
|
Date date = new Date();
|
||||||
|
System.out.println(date.getTime());
|
||||||
|
//通过C语音构造Date然后调用getTime
|
||||||
|
long time = test.accessConstructor();
|
||||||
|
System.out.println(time);
|
||||||
|
|
||||||
|
}
|
||||||
|
总结
|
||||||
|
属性、方法的访问的使用是和Java的反射API类似的。
|
||||||
|
|
||||||
|
综合进阶案例——JNI返回中文乱码问题
|
||||||
|
测试乱码问题:
|
||||||
|
|
||||||
|
public native void testChineseIn(String chinese);//传进去
|
||||||
|
public native String testChineseOut();//取出来会乱码
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
//传中文进去,然后转为C字符串,直接在C层输出是没有问题的
|
||||||
|
JniTest test = new JniTest();
|
||||||
|
test.testChineseIn("我爱你");
|
||||||
|
//C层将C字符串转换为JavaString然后输出,就会乱码
|
||||||
|
System.out.println(test.testChineseOut());
|
||||||
|
|
||||||
|
}
|
||||||
|
C代码如下:
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_test_JniTest_testChineseIn
|
||||||
|
(JNIEnv * env, jobject jobj, jstring chinese){
|
||||||
|
|
||||||
|
char* c_chinese = (*env)->GetStringUTFChars(env, chinese, NULL);
|
||||||
|
printf("%s", c_chinese);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_test_JniTest_testChineseOut
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
|
||||||
|
char* c_str = "我爱你";
|
||||||
|
jstring j_str = (*env)->NewStringUTF(env, c_str);
|
||||||
|
return j_str;
|
||||||
|
}
|
||||||
|
结果输出,其中第一条是C返回的乱码,第二条是传进去在C层打印的结果:
|
||||||
|
|
||||||
|
ÎҰ®Ä
|
||||||
|
我爱你
|
||||||
|
可以看到C执行的速度要比Java快。
|
||||||
|
原因分析,调用NewStringUTF的时候,产生的是UTF-16的字符串,但是我们需要的时候UTF-8字符串。
|
||||||
|
解决办法,通过Java的String类的构造方法来进行字符集变换。
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_test_JniTest_testChineseOut
|
||||||
|
(JNIEnv * env, jobject jobj){
|
||||||
|
|
||||||
|
//需要返回的字符串
|
||||||
|
char* c_str = "我爱你";
|
||||||
|
//jstring j_str = (*env)->NewStringUTF(env, c_str);
|
||||||
|
|
||||||
|
//通过调用构造方法String string = new String(byte[], charsetName);来解决乱码问题
|
||||||
|
|
||||||
|
//0.找到String类
|
||||||
|
jclass clz_String = (*env)->FindClass(env, "java/lang/String");
|
||||||
|
jmethodID mid = (*env)->GetMethodID(env, clz_String, "<init>", "([BLjava/lang/String;)V");
|
||||||
|
|
||||||
|
//准备new String的参数:byte数组以及字符集
|
||||||
|
//1.创建字节数组,并且将C的字符串拷贝进去
|
||||||
|
jbyteArray j_byteArray = (*env)->NewByteArray(env, strlen(c_str));
|
||||||
|
(*env)->SetByteArrayRegion(env, j_byteArray, 0, strlen(c_str), c_str);
|
||||||
|
//2.创建字符集的参数,这里用Windows的more字符集GB2312
|
||||||
|
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
|
||||||
|
|
||||||
|
//调用
|
||||||
|
jstring j_new_str = (*env)->NewObject(env, clz_String, mid, j_byteArray, charsetName);
|
||||||
|
return j_new_str;
|
||||||
|
|
||||||
|
}
|
||||||
|
原文链接:http://www.apkbus.com/blog-0-65619.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SystemProperties.getInt("ro.build.version.sdk", 0);
|
||||||
|
Build.VERSION.SDK_INT > Build.VERSION_CODES.Q
|
||||||
|
###C++代码取Android系统版本号:
|
||||||
|
```
|
||||||
|
#include <jni.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <sys/system_properties.h>
|
||||||
|
|
||||||
|
// 日志打印
|
||||||
|
#include <android/log.h>
|
||||||
|
#define LOG_TAG "TAG_LOG"
|
||||||
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||||
|
|
||||||
|
versionI() {
|
||||||
|
// 1. 获取 SDK 版本号 , 存储于 C 字符串 sdk_verison_str 中
|
||||||
|
char sdk[128] = "0";
|
||||||
|
|
||||||
|
// 获取版本号方法
|
||||||
|
__system_property_get("ro.build.version.sdk", sdk);
|
||||||
|
|
||||||
|
//将版本号转为 int 值
|
||||||
|
int sdk_verison = atoi(sdk);
|
||||||
|
}
|
||||||
|
|
||||||
|
# begin build properties (开始设置系统性能)
|
||||||
|
# autogenerated (通过设置形成系统信息)
|
||||||
|
ro.=GRI40 (版本ID)
|
||||||
|
ro.build.=GRJ22 (版本号)
|
||||||
|
ro.build.version.incremental=eng.buildbot.20110619.060228 (版本增量)
|
||||||
|
ro.build.version.sdk=10 (sdk版本)
|
||||||
|
ro.build.version.codename=REL (版本代号)
|
||||||
|
ro.build.version.release=2.3.4 (Android 2.3.4系统)
|
||||||
|
ro.build.date=Sun Jun 19 06:02:58 UTC 2011 (制作者及制作时间)
|
||||||
|
ro.build.date.utc=0
|
||||||
|
ro.build.type=user (编译模式,如user,userdebug,eng,test模式)
|
||||||
|
ro.build.user=buildbot (编译账户)
|
||||||
|
ro.build.host=bb1 (编译主机系统)
|
||||||
|
ro.build.tags=test-keys (编译标签)
|
||||||
|
ro.product.model=HTC Wildfire (HTC内部手机代号)
|
||||||
|
ro.product.brand=htc_wwe (手机品牌)
|
||||||
|
ro.product.name=htc_buzz (手机正式名称)
|
||||||
|
ro.product.device=buzz (采用的设备)
|
||||||
|
ro.product.board=buzz (采用的处理器)
|
||||||
|
ro.product.cpu.abi=armeabi-v6j (cpu的版本)
|
||||||
|
ro.product.cpu.abi2=armeabi (cpu的品牌)
|
||||||
|
ro.product.manufacturer=HTC (手机制造商)
|
||||||
|
ro.product.locale.language=zh (手机默认语言)
|
||||||
|
ro.product.locale.region=CN (地区语言)
|
||||||
|
ro.wifi.channels= (WIFI连接的渠道)
|
||||||
|
ro.board.platform=msm7k (主板平台)
|
||||||
|
# ro.build.product is obsolete; use ro.product.device (旧代码ro.build.product,使用代码ro.product.device)
|
||||||
|
ro.build.product=buzz (建立产品)
|
||||||
|
# Do not try to parse ro.build.description or .fingerprint (不要试图修改description和fingerprint)
|
||||||
|
ro.build.description=passion-user 2.3.3 GRI40 102588 release-keys (用户的KEY)
|
||||||
|
ro.build.fingerprint=google/passion/passion:2.3.3/GRI40/102588:user/release-keys (系统指纹)
|
||||||
|
# end build properties (性能代码完毕)
|
||||||
|
#
|
||||||
|
# system.prop for buzz (系统技术支持由BUZZ提供)
|
||||||
|
#
|
||||||
|
# Density in DPI of the LCD of this board. This is used to scale the UI (高密度的液晶的DPI板。这是用来大规模UI的)
|
||||||
|
# appropriately. If this property is not defined, the default value is 160 dpi. (appropriately.如果这个属性没有定义,缺省值是160 dpi的分辨率)
|
||||||
|
ro.sf.lcd_density=240 (显示屏分辨率,数值越大分辨率越底,240就是800*480的)
|
||||||
|
# View configuration for QVGA. (屏幕的设置)
|
||||||
|
view.fading_edge_length=8
|
||||||
|
view.touch_slop=15 (触摸屏灵敏度,数值越大越灵敏)
|
||||||
|
view.minimum_fling_velocity=25 (滑动速度)
|
||||||
|
view.scroll_friction=0.008 (滑动误差)
|
||||||
|
# RIL specific configuration. (特定设置)
|
||||||
|
rild.libpath=/system/lib/libhtc_
|
||||||
|
ro.ril.ecc.HTC-WWE=999
|
||||||
|
ro.ril.ecc.HTC-ELL=92,93,94
|
||||||
|
ro.ril.enable.a52.HTC-ITA=1
|
||||||
|
ro.ril.enable.a53.HTC-ITA=1
|
||||||
|
ro.ril.enable.a52=0
|
||||||
|
ro.ril.enable.a53=1
|
||||||
|
ro.ril.vmail.23415=1571,BT
|
||||||
|
ro.ril.hsdpa.category=8 (hsdpa全称High Speed Downlink Packet Access中文意思:高速下行分组接入,设置的数越大传输越快)
|
||||||
|
ro.ril.htcmaskw1.bitmask=429496
|
||||||
|
ro.ril.htcmaskw1=14449
|
||||||
|
ro.ril.def.agps.mode=2 (打开AGPS服务支持,可改为ro.ril.def.agps.mode=0 改后能省电但GPS定位速度会变慢)
|
||||||
|
ro.ril.gprsclass=12 (GPRS设置)
|
||||||
|
# For HSDPA low throughput (HSDPA低输量)
|
||||||
|
ro.ril.disable.power.collapse=1 (关闭电源)
|
||||||
|
# Modify MMS APN retry timer from 5s to 2s. (修改短信的APN设置5秒为2秒)
|
||||||
|
ro.gsm.2nd_data_retry_config=max_retries=3, 2000, 2000, 2000
|
||||||
|
# Time between scans in seconds. Keep it high to minimize battery drain.(扫描在几秒之内,可降低用电量)
|
||||||
|
# This only affects the case in which there are remembered access points, (这个修改仅能影响此文件)
|
||||||
|
# but none are in range.(但是没有一项是在范围内的)
|
||||||
|
wifi.interface=eth0 (WIFI界面)
|
||||||
|
wifi.supplicant_scan_interval=45 (WIFI扫描间隔时间,这里设置是45秒。把这个时间设置长点能省电)
|
||||||
|
# Mobile data interfaces (移动数据的接口)
|
||||||
|
mobiledata.interfaces=rmnet0,rmnet1,rmnet2
|
||||||
|
# Allow or deny tethering. (允许和拒绝绑定)
|
||||||
|
ro.tether.denied=false
|
||||||
|
# Default network type. (默认的网络类型)
|
||||||
|
# 0 => WCDMA Preferred. (0=WCDMA优先)
|
||||||
|
ro.telephony.default_network=0
|
||||||
|
# Enable Google-specific location features, (谷歌特定地点的设置)
|
||||||
|
# like NetworkLocationProvider and LocationCollector.(如网络服务器提供商和服务器位置)
|
||||||
|
ro.c o m.google.locationfeatures=1
|
||||||
|
# The OpenGL ES API level that is natively supported by this device. (开放式绘图介面)
|
||||||
|
# This is a 16.16 fixed point number. (界面有16个点,16个不动点数量)
|
||||||
|
ro.opengles.version=65536 (开放式绘图介面参数)
|
||||||
|
# Disable fs check on boot by default. (开机时默认禁用FS检查)
|
||||||
|
sys.checkfs.fat=false
|
||||||
|
# Performance settings. (性能设置)
|
||||||
|
dalvik.vm.execution-mode=int:jit
|
||||||
|
dalvik.vm.heapsize=24m (虚拟内存大小,可设置为16m或24m或32m或48m)
|
||||||
|
persist.sys.use_dithering=1
|
||||||
|
persist.sys.purgeable_assets=1
|
||||||
|
# Increase SKIA decode memory capability for progressive jpg file.
|
||||||
|
ro.media.dec.jpeg.memcap=20000000
|
||||||
|
#
|
||||||
|
# ADDITIONAL_BUILD_PROPERTIES (其他性能设置)
|
||||||
|
no_require_sim=true (手机卡保护设置)
|
||||||
|
ro.rommanager.developerid=cyanogenmodnightly (固件管理器开发者是CM大神)
|
||||||
|
ro.url.legal=http://www./intl/%s/mobile/android/basic/phone-legal.html
|
||||||
|
ro.url.legal.android_privacy=http://www]/intl/%s/mobile/android/basic/privacy.html
|
||||||
|
ro. com.google.clientidbase=android-google (谷歌客户身份)
|
||||||
|
ro. com.android.wifi-watchlist=GoogleGuest (WIFI用户名单)
|
||||||
|
ro.setupwizard.enterprise_mode=1 (默认情景模式)
|
||||||
|
ro. com.android.dateformat=MM-dd-yyyy (默认时间格式,改为yyyy-MM-dd,显示效果就是XXXX年XX月XX日)
|
||||||
|
ro. com.android.dataroaming=false (漫游设置)
|
||||||
|
ro.config.ringtone=Playa.ogg (默认铃声设置,文件在/system/media/audio/ringtones 把喜欢的铃声放这里,比如123. MP3放入ringtones文件夹中,这里代码改为ro.config.ringtone=123. mp3)
|
||||||
|
ro.config.notification_sound=regulus.ogg (默认提示音,文件在/system/media/audio/notifications 修改方法同上)
|
||||||
|
ro.config.alarm_alert=Alarm_Beep_03.ogg (默认闹铃,文件在/system/media/audio/alarms 修改方法同上)
|
||||||
|
ro.modversion=CyanogenMod-7-06192011-NIGHTLY-buzz (版本信息,改这个能让你大名出现系统关于中,改为ro.modversion=xxxxx)
|
||||||
|
ro.setupwizard.mode=OPTIONAL (安装向导模式)
|
||||||
|
net. bt. name=Android (系统名称)
|
||||||
|
dalvik.vm.stack-trace-file=/data/anr/traces.txt
|
||||||
|
```
|
||||||
@@ -1,4 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="cn.android.security"/>
|
package="cn.android.security">
|
||||||
|
<application>
|
||||||
|
<!--android:authorities="${applicationId}.library-installer"
|
||||||
|
android:authorities="cn.android.security.InitProvider"-->
|
||||||
|
<provider
|
||||||
|
android:name=".InitProvider"
|
||||||
|
android:authorities="${applicationId}.InitProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:multiprocess="true" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.android.security;
|
package cn.android.security;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
@@ -21,6 +22,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -49,15 +51,15 @@ public class APISecurity {
|
|||||||
* 被Native调用的Java方法
|
* 被Native调用的Java方法
|
||||||
*/
|
*/
|
||||||
public void javaMethod(String msg) {
|
public void javaMethod(String msg) {
|
||||||
Log.e("错误代码", msg);
|
Log.e("mhyLog错误代码", msg);
|
||||||
// System.exit(1);
|
// System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void verify(Context context) {
|
public static void verify(Context context) {
|
||||||
Log.e("mhyLog", "hash"+AppSigning.getSignatureHash(context));
|
Log.e("mhyLog", "hash:"+AppSigning.getSignatureHash(context));
|
||||||
runCommand();
|
//runCommand();
|
||||||
Log.e("包路径文件签名", getApkSignatures(context, context.getPackageName()));
|
Log.e("mhyLog包文件签名", getApkSignatures(context,context.getPackageName()));
|
||||||
Log.e("已安装APP签名", getInstalledAPKSignature(context, context.getPackageName()));
|
Log.e("mhyLog已安装签名", getInstalledAPKSignature(context, context.getPackageName()));
|
||||||
//通过获取其他应用的签名 如果一样那么被hook了
|
//通过获取其他应用的签名 如果一样那么被hook了
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ public class APISecurity {
|
|||||||
public static String getInstalledAPKSignature(Context context, String packageName) {
|
public static String getInstalledAPKSignature(Context context, String packageName) {
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
try {
|
try {
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNING_CERTIFICATES);
|
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||||
if (appInfo == null || appInfo.signingInfo == null)
|
if (appInfo == null || appInfo.signingInfo == null)
|
||||||
return "";
|
return "";
|
||||||
@@ -86,47 +88,91 @@ public class APISecurity {
|
|||||||
|
|
||||||
/** 防破签名 2
|
/** 防破签名 2
|
||||||
* C调用Java 从源安装文件获取签名信息
|
* C调用Java 从源安装文件获取签名信息
|
||||||
|
* 有bug
|
||||||
* */
|
* */
|
||||||
public static String getApkSignatures(Context context, String packname) {
|
public static String getApkSignatures(Context context, String packname) {
|
||||||
String sign = "";
|
String path="";
|
||||||
String path = null;
|
if (!TextUtils.isEmpty(packname)) {
|
||||||
// try {//获取此包安装路径
|
try {//获取此包安装路径
|
||||||
// //第一种方法
|
//第一种方法
|
||||||
// ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
|
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
|
||||||
// path = applicationInfo.sourceDir;
|
path = applicationInfo.sourceDir;
|
||||||
// //第二种方法
|
//第二种方法
|
||||||
//// ApplicationInfo applicationInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA).applicationInfo;
|
// ApplicationInfo applicationInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA).applicationInfo;
|
||||||
//// path = applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
|
// path = applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
|
||||||
// Log.e("其他已知包名apk的安装路径", applicationInfo.sourceDir+ "&---&" + applicationInfo.publicSourceDir);
|
// Log.e("mhyLog其他已知包名apk的安装路径", applicationInfo.sourceDir + "&---&" + applicationInfo.publicSourceDir);
|
||||||
// } catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
// e.printStackTrace();
|
e.printStackTrace();
|
||||||
// //第三中方法
|
//第三中方法
|
||||||
// path=context.getPackageResourcePath();
|
// path = context.getPackageResourcePath();
|
||||||
// Log.e("在apk中获取自身安装路径", path);
|
path= context.getApplicationInfo().sourceDir;
|
||||||
// }
|
// Log.e("mhyLOg在apk中获取自身安装路径", path);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
//第三中方法
|
//第三中方法
|
||||||
path=context.getPackageResourcePath();
|
// path= context.getApplicationInfo().sourceDir;
|
||||||
// Log.e("在apk中获取自身安装路径", path);
|
path=context.getPackageResourcePath();
|
||||||
|
}
|
||||||
File apkFile = new File(path);
|
File apkFile = new File(path);
|
||||||
if (apkFile != null && apkFile.exists()) {
|
if (apkFile.exists()) {
|
||||||
Log.e("包安装路径", apkFile.getAbsolutePath());
|
Log.e("mhyLog包安装路径", apkFile.getAbsolutePath());
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);
|
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||||
if (pkgInfo != null && pkgInfo.signingInfo != null && pkgInfo.signingInfo.getApkContentsSigners().length > 0) {
|
if (pkgInfo != null && pkgInfo.signingInfo != null) {
|
||||||
sign = AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
|
return AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
|
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
|
||||||
if (pkgInfo != null && pkgInfo.signatures != null && pkgInfo.signatures.length > 0) {
|
if (pkgInfo != null && pkgInfo.signatures != null) {
|
||||||
sign = AppSigning.getSignatureString(pkgInfo.signatures, AppSigning.SHA1);
|
return AppSigning.getSignatureString(pkgInfo.signatures, AppSigning.SHA1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sign;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动构建 Context
|
||||||
|
*/
|
||||||
|
@SuppressLint({"DiscouragedPrivateApi","PrivateApi"})
|
||||||
|
public static Context createContext() throws ClassNotFoundException,
|
||||||
|
NoSuchMethodException,
|
||||||
|
InvocationTargetException,
|
||||||
|
IllegalAccessException,
|
||||||
|
NoSuchFieldException,
|
||||||
|
NullPointerException{
|
||||||
|
|
||||||
|
// 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread
|
||||||
|
Class activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||||
|
Method currentActivityThreadMethod =
|
||||||
|
activityThreadClass.getDeclaredMethod("currentActivityThread");
|
||||||
|
currentActivityThreadMethod.setAccessible(true);
|
||||||
|
Object mainThreadObj = currentActivityThreadMethod.invoke(null);
|
||||||
|
|
||||||
|
// 反射获取 mainThread 实例中的 mBoundApplication 字段
|
||||||
|
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||||
|
mBoundApplicationField.setAccessible(true);
|
||||||
|
Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj);
|
||||||
|
|
||||||
|
// 获取 mBoundApplication 的 packageInfo 变量
|
||||||
|
if (mBoundApplicationObj == null) throw new NullPointerException("mBoundApplicationObj 反射值空");
|
||||||
|
Class mBoundApplicationClass = mBoundApplicationObj.getClass();
|
||||||
|
Field infoField = mBoundApplicationClass.getDeclaredField("info");
|
||||||
|
infoField.setAccessible(true);
|
||||||
|
Object packageInfoObj = infoField.get(mBoundApplicationObj);
|
||||||
|
|
||||||
|
// 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo)
|
||||||
|
if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空");
|
||||||
|
if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空");
|
||||||
|
Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod(
|
||||||
|
"createAppContext",
|
||||||
|
mainThreadObj.getClass(),
|
||||||
|
packageInfoObj.getClass());
|
||||||
|
createAppContextMethod.setAccessible(true);
|
||||||
|
return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//需要读取应用列表权限
|
//需要读取应用列表权限
|
||||||
public void getAppList(Context context) {
|
public void getAppList(Context context) {
|
||||||
@@ -146,8 +192,6 @@ public class APISecurity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过指令获取已安装的包
|
* 通过指令获取已安装的包
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static ArrayList<String> runCommand() {
|
private static ArrayList<String> runCommand() {
|
||||||
list.clear();
|
list.clear();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.Signature;
|
import android.content.pm.Signature;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -54,29 +55,31 @@ public class AppSigning {
|
|||||||
* 获取相应的类型的签名信息(把签名的byte[]信息转换成16进制)
|
* 获取相应的类型的签名信息(把签名的byte[]信息转换成16进制)
|
||||||
*/
|
*/
|
||||||
public static String getSignatureString(Signature[] sigs, @SigniType String type) {
|
public static String getSignatureString(Signature[] sigs, @SigniType String type) {
|
||||||
for (Signature sig : sigs) {
|
|
||||||
Log.e("mhyLog","getSignatureString:"+sig.toCharsString());
|
|
||||||
}
|
|
||||||
byte[] hexBytes = sigs[0].toByteArray();
|
|
||||||
String fingerprint = "error!";
|
String fingerprint = "error!";
|
||||||
try {
|
if (sigs.length>0) {
|
||||||
StringBuffer buffer = new StringBuffer();
|
for (Signature sig : sigs) {
|
||||||
MessageDigest digest = MessageDigest.getInstance(type);
|
Log.e("mhyLog", "Signature64:" + Base64.encodeToString(sig.toCharsString().getBytes(),Base64.DEFAULT));
|
||||||
if (digest != null) {
|
}
|
||||||
digest.reset();
|
byte[] hexBytes = sigs[0].toByteArray();
|
||||||
digest.update(hexBytes);
|
try {
|
||||||
byte[] byteArray = digest.digest();
|
StringBuffer buffer = new StringBuffer();
|
||||||
for (int i = 0; i < byteArray.length; i++) {
|
MessageDigest digest = MessageDigest.getInstance(type);
|
||||||
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
|
if (digest != null) {
|
||||||
buffer.append("0").append(Integer.toHexString(0xFF & byteArray[i])); //补0,转换成16进制
|
digest.reset();
|
||||||
} else {
|
digest.update(hexBytes);
|
||||||
buffer.append(Integer.toHexString(0xFF & byteArray[i]));//转换成16进制
|
byte[] byteArray = digest.digest();
|
||||||
}
|
for (int i = 0; i < byteArray.length; i++) {
|
||||||
}
|
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
|
||||||
fingerprint = buffer.toString().toLowerCase()/*.toUpperCase()*/; //转换成大写
|
buffer.append("0").append(Integer.toHexString(0xFF & byteArray[i])); //补0,转换成16进制
|
||||||
|
} else {
|
||||||
|
buffer.append(Integer.toHexString(0xFF & byteArray[i]));//转换成16进制
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fingerprint = buffer.toString().toLowerCase()/*.toUpperCase()*/; //转换成大写
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
return fingerprint;
|
return fingerprint;
|
||||||
}
|
}
|
||||||
@@ -168,10 +171,11 @@ public class AppSigning {
|
|||||||
return str.toString();
|
return str.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 防破签名 3 使原生检测签名不被hook 助力防破签名1而存在
|
/** 防破签名 3 使原生检测签名能获取真实值 助力防破签名1而存在
|
||||||
* 通过重置PackageManager防止getPackageInfo方法被代理设置
|
* 通过重置PackageManager防止getPackageInfo方法被代理设置
|
||||||
* 亲测MT管理器(当前2.9.1)的一键去签名校验(包括加强版)无效!
|
* 亲测MT管理器(当前2.9.1)的一键去签名校验(包括加强版)无效!
|
||||||
* 当然如果别人反编译把代码删除的话那就没办法了
|
* 当然如果别人反编译把代码删除的话那就没办法了
|
||||||
|
* getBaseContext()
|
||||||
*/
|
*/
|
||||||
public static void resetPackageManager(Context baseContext) {
|
public static void resetPackageManager(Context baseContext) {
|
||||||
try {
|
try {
|
||||||
@@ -207,7 +211,7 @@ public class AppSigning {
|
|||||||
* 检测 PackageManager 代理
|
* 检测 PackageManager 代理
|
||||||
*/
|
*/
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
private boolean checkPMProxy(Context context){
|
public static boolean checkPMProxy(Context context){
|
||||||
String truePMName = "android.content.pm.IPackageManager$Stub$Proxy";
|
String truePMName = "android.content.pm.IPackageManager$Stub$Proxy";
|
||||||
String nowPMName = "";
|
String nowPMName = "";
|
||||||
try {
|
try {
|
||||||
@@ -227,7 +231,9 @@ public class AppSigning {
|
|||||||
|
|
||||||
/** 防破签名 2
|
/** 防破签名 2
|
||||||
* 安装路径获取签名
|
* 安装路径获取签名
|
||||||
|
* PackageParser 28新api
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
public static String getAPKSignatures(String apkPath) {
|
public static String getAPKSignatures(String apkPath) {
|
||||||
String PATH_PackageParser = "android.content.pm.PackageParser";
|
String PATH_PackageParser = "android.content.pm.PackageParser";
|
||||||
try {
|
try {
|
||||||
@@ -252,7 +258,7 @@ public class AppSigning {
|
|||||||
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), PackageManager.GET_SIGNATURES);
|
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), PackageManager.GET_SIGNATURES);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 28) {
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);//true skipVerify
|
||||||
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, Build.VERSION.SDK_INT > 28);
|
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, Build.VERSION.SDK_INT > 28);
|
||||||
|
|
||||||
// Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
// Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||||
|
|||||||
115
apisecurity/src/main/java/cn/android/security/InitProvider.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. 安卓
|
||||||
|
* FileName: ${NAME}
|
||||||
|
* Author: ${USER}
|
||||||
|
* Date: ${DATE} ${TIME}
|
||||||
|
* Description: ${DESCRIPTION}
|
||||||
|
* History:
|
||||||
|
* <author> <time> <version> <desc>
|
||||||
|
* 作者姓名 修改时间 版本号 描述
|
||||||
|
* 本代码未经许可,不得私自修改何使用
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.android.security;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created By Mahongyin
|
||||||
|
* Date 2021/3/2 17:24
|
||||||
|
* ContentProvider的onCreate会先于Appliction的onCreate调用 防止application被掉包后 仍有这里可以重置pm代理+检测
|
||||||
|
*/
|
||||||
|
public class InitProvider extends ContentProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
Context application = getContext().getApplicationContext();
|
||||||
|
if (application == null) {
|
||||||
|
application = getApplicationByReflect();
|
||||||
|
}
|
||||||
|
Log.e("mhyLog", "initContentProvider:");
|
||||||
|
// chekSignature(application);
|
||||||
|
checkApplication();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void chekSignature(Context application){
|
||||||
|
//防hook 并检测
|
||||||
|
if (AppSigning.checkPMProxy(application)) {
|
||||||
|
AppSigning.resetPackageManager(application);
|
||||||
|
String sing = APISecurity.getInstalledAPKSignature(application, application.getPackageName());
|
||||||
|
Log.e("mhyLog手动InitProvider", sing);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 校验 application
|
||||||
|
*/
|
||||||
|
private boolean checkApplication(){
|
||||||
|
//在这里使用反射 获取比较靠谱 如果 被替换换 就查出来了
|
||||||
|
Application nowApplication = getApplicationByReflect();
|
||||||
|
String trueApplicationName = "MyApplication";//自己的Application类名 防止替换
|
||||||
|
String nowApplicationName = nowApplication.getClass().getSimpleName();
|
||||||
|
Log.e("mhyLogAppName", "反射获取:"+nowApplicationName);
|
||||||
|
return trueApplicationName.equals(nowApplicationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过反射获取 当前application
|
||||||
|
*/
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
public Application getApplicationByReflect() {
|
||||||
|
try {
|
||||||
|
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||||
|
Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
|
||||||
|
Object app = activityThread.getMethod("getApplication").invoke(thread);
|
||||||
|
if (app == null) {
|
||||||
|
throw new NullPointerException("you should init first");
|
||||||
|
}
|
||||||
|
return (Application) app;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
throw new NullPointerException("you should init first");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||||
|
return query(uri, projection, selection, selectionArgs, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
return getType(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||||
|
return insert(uri, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||||
|
return delete(uri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||||
|
return update(uri, values, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:name=".HookApplication"
|
android:name=".MyApplication"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
#include <cstring>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
//系统信息
|
||||||
|
#include <sys/system_properties.h>
|
||||||
|
|
||||||
//log定义
|
//log定义
|
||||||
#define LOG "APISECURITY" // 这个是自定义的LOG的TAG
|
#define LOG "mhyLog_APISECURITY" // 这个是自定义的LOG的TAG
|
||||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__)
|
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__)
|
||||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__)
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__)
|
||||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG,__VA_ARGS__)
|
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG,__VA_ARGS__)
|
||||||
@@ -14,8 +17,8 @@
|
|||||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__)
|
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__)
|
||||||
|
|
||||||
//此处改为你的APP签名
|
//此处改为你的APP签名
|
||||||
//#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//真实test.keystore
|
#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//真实test.keystore
|
||||||
#define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255"//区分da小写
|
//#define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255"//区分da小写
|
||||||
#define ALGORITHM_SHA1 "SHA1"
|
#define ALGORITHM_SHA1 "SHA1"
|
||||||
#define ALGORITHM_MD5 "MD5"
|
#define ALGORITHM_MD5 "MD5"
|
||||||
|
|
||||||
@@ -28,19 +31,34 @@
|
|||||||
static bool isInit = false;
|
static bool isInit = false;
|
||||||
static char *secret;
|
static char *secret;
|
||||||
|
|
||||||
|
jint version() {
|
||||||
void printByte(JNIEnv *env, jbyteArray jbytes) {
|
// 1. 获取 SDK 版本号 , 存储于 C 字符串 sdk_verison_str 中
|
||||||
//转换成char
|
char sdk[128] = "0";
|
||||||
jsize array_size = env->GetArrayLength(jbytes);
|
// 获取版本号方法
|
||||||
jbyte *sha1 = env->GetByteArrayElements(jbytes, nullptr);
|
__system_property_get("ro.build.version.sdk", sdk);
|
||||||
|
//将版本号转为 int 值
|
||||||
char *hexA = new char[array_size * 2 + 1]();
|
int sdk_verison = atoi(sdk);
|
||||||
for (int i = 0; i < array_size; ++i) {
|
return sdk_verison;
|
||||||
sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]);
|
|
||||||
}
|
|
||||||
LOGD("printByte:%s", hexA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jint getDeviceVersion(JNIEnv *env) {
|
||||||
|
jclass build_claz = env->FindClass("android/os/Build$VERSION");
|
||||||
|
jfieldID sdk_int = env->GetStaticFieldID(build_claz, "SDK_INT", "I");
|
||||||
|
return env->GetStaticIntField(build_claz, sdk_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
//void printByte(JNIEnv *env, jbyteArray jbytes) {
|
||||||
|
// //转换成char
|
||||||
|
// jsize array_size = env->GetArrayLength(jbytes);
|
||||||
|
// jbyte *sha1 = env->GetByteArrayElements(jbytes, nullptr);
|
||||||
|
//
|
||||||
|
// char *hexA = new char[array_size * 2 + 1]();
|
||||||
|
// for (int i = 0; i < array_size; ++i) {
|
||||||
|
// sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]);
|
||||||
|
// }
|
||||||
|
// LOGD("printByte:%s", hexA);
|
||||||
|
//}
|
||||||
|
|
||||||
char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
|
char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
|
||||||
jclass message_digest_class = env->FindClass("java/security/MessageDigest");
|
jclass message_digest_class = env->FindClass("java/security/MessageDigest");
|
||||||
jmethodID methodId = env->GetStaticMethodID(message_digest_class, "getInstance",
|
jmethodID methodId = env->GetStaticMethodID(message_digest_class, "getInstance",
|
||||||
@@ -130,19 +148,23 @@ jstring getApkPath(JNIEnv *env, jobject applicationInfo_object) {
|
|||||||
jstring apkPath = (jstring) env->GetObjectField(applicationInfo_object, sourceDir);
|
jstring apkPath = (jstring) env->GetObjectField(applicationInfo_object, sourceDir);
|
||||||
return apkPath;
|
return apkPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
//context.getPackageResourcePath()
|
//context.getPackageResourcePath()
|
||||||
jstring getApkResPath(JNIEnv *env, jclass context_class, jobject context_object) {
|
jstring getApkResPath(JNIEnv *env, jclass context_class, jobject context_object) {
|
||||||
jmethodID methodId = env->GetMethodID(context_class, "getPackageResourcePath", "()Ljava/lang/String;");
|
jmethodID methodId = env->GetMethodID(context_class, "getPackageResourcePath",
|
||||||
|
"()Ljava/lang/String;");
|
||||||
jstring apkPath = (jstring) env->CallObjectMethod(context_object, methodId);
|
jstring apkPath = (jstring) env->CallObjectMethod(context_object, methodId);
|
||||||
return apkPath;
|
return apkPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
//弃用 安装路径 package_info= pm.getPackageInfo(packname, 0x80).applicationInfo
|
//弃用 安装路径 package_info= pm.getPackageInfo(packname, 0x80).applicationInfo
|
||||||
jstring getAbsolutePath(JNIEnv *env, jobject package_info) {
|
jstring getAbsolutePath(JNIEnv *env, jobject package_info) {
|
||||||
jclass package_info_class = env->GetObjectClass(package_info);//packageInfo
|
jclass package_info_class = env->GetObjectClass(package_info);//packageInfo
|
||||||
jfieldID field = env->GetFieldID(package_info_class, "applicationInfo",
|
jfieldID field = env->GetFieldID(package_info_class, "applicationInfo",
|
||||||
"Landroid/content/pm/ApplicationInfo;");//applicationInfo获取类对象 以获取方法
|
"Landroid/content/pm/ApplicationInfo;");//applicationInfo获取类对象 以获取方法
|
||||||
env->DeleteLocalRef(package_info_class);
|
env->DeleteLocalRef(package_info_class);
|
||||||
jobject apppack_info_object = (jstring) env->GetObjectField(package_info,field);//applicationInfo
|
jobject apppack_info_object = (jstring) env->GetObjectField(package_info,
|
||||||
|
field);//applicationInfo
|
||||||
jclass apppack_info_class = env->GetObjectClass(apppack_info_object);//ApplicationInfo
|
jclass apppack_info_class = env->GetObjectClass(apppack_info_object);//ApplicationInfo
|
||||||
jfieldID appfield = env->GetFieldID(apppack_info_class, "sourceDir",
|
jfieldID appfield = env->GetFieldID(apppack_info_class, "sourceDir",
|
||||||
"Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
|
"Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
|
||||||
@@ -156,7 +178,36 @@ jstring getAbsolutePath(JNIEnv *env, jobject package_info) {
|
|||||||
/**
|
/**
|
||||||
* 获取签名信息
|
* 获取签名信息
|
||||||
*/
|
*/
|
||||||
|
//需要sdk28 新API
|
||||||
|
jobject getSignature28(JNIEnv *env, jobject package_info) {
|
||||||
|
jclass package_info_class = env->GetObjectClass(package_info);
|
||||||
|
jfieldID signingInfo = env->GetFieldID(package_info_class, "signingInfo",
|
||||||
|
"Landroid/content/pm/SigningInfo;");
|
||||||
|
env->DeleteLocalRef(package_info_class);
|
||||||
|
jobject signingInfo_object = env->GetObjectField(package_info, signingInfo);
|
||||||
|
LOGE("%s", "fieldId:signingInfo");
|
||||||
|
if (signingInfo_object == nullptr) {
|
||||||
|
return nullptr;//为空咋回事?
|
||||||
|
}
|
||||||
|
// jclass signingInfo_class = env->GetObjectClass(signingInfo_object);
|
||||||
|
jclass signingInfo_class = env->FindClass("android/content/pm/SigningInfo");
|
||||||
|
jmethodID methodId = env->GetMethodID(signingInfo_class, "getApkContentsSigners",
|
||||||
|
"()[Landroid/content/pm/Signature;");
|
||||||
|
env->DeleteLocalRef(signingInfo_class);
|
||||||
|
jobjectArray signature_object_array = (jobjectArray) env->CallObjectMethod(signingInfo_object,
|
||||||
|
methodId);
|
||||||
|
LOGE("%s", "methodId:getApkContentsSigners");
|
||||||
|
if (signature_object_array == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
return env->GetObjectArrayElement(signature_object_array, 0);
|
||||||
|
}
|
||||||
|
|
||||||
jobject getSignature(JNIEnv *env, jobject package_info) {
|
jobject getSignature(JNIEnv *env, jobject package_info) {
|
||||||
|
// jint sdk = getDeviceVersion(env);
|
||||||
|
// if (sdk >= 28) {
|
||||||
|
// LOGE("sdk版本%d", sdk);
|
||||||
|
// return getSignature28(env, package_info);
|
||||||
|
// }
|
||||||
jclass package_info_class = env->GetObjectClass(package_info);
|
jclass package_info_class = env->GetObjectClass(package_info);
|
||||||
jfieldID fieldId = env->GetFieldID(package_info_class, "signatures",
|
jfieldID fieldId = env->GetFieldID(package_info_class, "signatures",
|
||||||
"[Landroid/content/pm/Signature;");
|
"[Landroid/content/pm/Signature;");
|
||||||
@@ -240,7 +291,7 @@ jboolean getApkPathSignatures(JNIEnv *env, jobject context_object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用
|
* 调用 init
|
||||||
*/
|
*/
|
||||||
extern "C" JNIEXPORT jboolean JNICALL
|
extern "C" JNIEXPORT jboolean JNICALL
|
||||||
Java_cn_android_security_APISecurity_init(
|
Java_cn_android_security_APISecurity_init(
|
||||||
@@ -290,32 +341,32 @@ Java_cn_android_security_APISecurity_init(
|
|||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
/*********接着调用Java方法验证 安装目录apk文件de签名**/
|
/*********接着调用Java方法验证 安装目录apk文件de签名**/
|
||||||
// jclass cls_util = env->FindClass(
|
jclass cls_util = env->FindClass(
|
||||||
// "cn/android/security/APISecurity");
|
"cn/android/security/APISecurity");
|
||||||
// //注意,这里的使用的斜杠而不是点
|
//注意,这里的使用的斜杠而不是点
|
||||||
// if (cls_util == nullptr) {
|
if (cls_util == nullptr) {
|
||||||
// return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
// }
|
}
|
||||||
// jobject j_obj = env->AllocObject(cls_util);
|
jobject j_obj = env->AllocObject(cls_util);
|
||||||
// //**这里是关键**类,方法,(参数类型)返回类型
|
//**这里是关键**类,方法,(参数类型)返回类型
|
||||||
// jmethodID mtd_static_method = env->GetStaticMethodID(cls_util,
|
jmethodID mtd_static_method = env->GetStaticMethodID(cls_util,
|
||||||
// "getApkSignatures",
|
"getApkSignatures",
|
||||||
// "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;");
|
"(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;");
|
||||||
// if (mtd_static_method == nullptr) {
|
if (mtd_static_method == nullptr) {
|
||||||
// return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
// }
|
}
|
||||||
// //调用Java方法
|
//调用Java方法
|
||||||
// jstring sigin = static_cast<jstring>(env->CallStaticObjectMethod(cls_util, mtd_static_method,
|
jstring sigin = static_cast<jstring>(env->CallStaticObjectMethod(cls_util, mtd_static_method,
|
||||||
// context_object, package_name));
|
context_object, package_name));
|
||||||
// const char *ss = env->GetStringUTFChars(sigin, nullptr);
|
const char *ss = env->GetStringUTFChars(sigin, nullptr);
|
||||||
// //删除引用
|
//删除引用
|
||||||
// env->DeleteLocalRef(cls_util);
|
env->DeleteLocalRef(cls_util);
|
||||||
// env->DeleteLocalRef(j_obj);
|
env->DeleteLocalRef(j_obj);
|
||||||
////调用Java方法结束
|
//调用Java方法结束
|
||||||
// if (strcmp(ss, SHA1) != 0) {
|
if (strcmp(ss, SHA1) != 0) {
|
||||||
// LOGE("非法调用3,SHA1: %s", ss);
|
LOGE("非法调用3,SHA1: %s", ss);
|
||||||
// return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
// }
|
}
|
||||||
/*******************调用Java方法结束***/
|
/*******************调用Java方法结束***/
|
||||||
//加强验证
|
//加强验证
|
||||||
if (!getApkPathSignatures(env, context_object)) {
|
if (!getApkPathSignatures(env, context_object)) {
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
package cn.android.sample;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 项目名 FileTransfer
|
|
||||||
* 所在包 bin.mt.apksignaturekillerplus
|
|
||||||
* 作者 mahongyin
|
|
||||||
* 时间 2020-03-16 10:55
|
|
||||||
* 邮箱 mhy.work@qq.com
|
|
||||||
* 描述 说明: 将application替换到AndroidMainfest.xml 代码里改放APPlication
|
|
||||||
* 代码里替换要hook的签名值 始终返回原始签名,达到欺骗
|
|
||||||
*/
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.Signature;
|
|
||||||
import android.util.Base64;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
|
|
||||||
import cn.android.security.AppSigning;
|
|
||||||
//继承原application
|
|
||||||
|
|
||||||
public class HookApplication extends Application implements InvocationHandler {
|
|
||||||
private static final int GET_SIGNATURES = 64;
|
|
||||||
private String appPkgName = "";
|
|
||||||
private Object base;
|
|
||||||
private byte[][] sign;
|
|
||||||
static HookApplication app;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
app = this;
|
|
||||||
//在签名校验被hook 之后重置PackageManager
|
|
||||||
/*在这里 重置PackageManager 只要在验证前重置即可*/
|
|
||||||
AppSigning.resetPackageManager(getBaseContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Context getContext() {
|
|
||||||
if (app == null) {
|
|
||||||
app = new HookApplication();
|
|
||||||
app.onCreate();
|
|
||||||
}
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void attachBaseContext(Context context) {
|
|
||||||
//在这里hook 签名校验被
|
|
||||||
hook(context);
|
|
||||||
super.attachBaseContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局hook 签名校验
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
*/
|
|
||||||
private void hook(Context context) {
|
|
||||||
try {
|
|
||||||
String relay="308203273082020fa003020102020477d6d1f6300d06092a864886f70d01010b05003044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f301e170d3139303330353032343132345a170d3434303232373032343132345a3044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f30820122300d06092a864886f70d01010105000382010f003082010a0282010100a3ac52268a32e8420a20a727c184c133d513998a207e198f5a535d628a436ba5e095e7ba3f92535234a83fb6272e70ed6113d8f6facc3dee2cfc076a3bd93dad3520fd5d9d9ae4c48afe56e7b421f5de2adfbc23e450f7a5f71e0afdec047b1ce8d7be62ef754a9d43bf36d9b9e0728fc268cb845b464cce1370573dfafd6c40b2efb98ba1f20c5a63c417264b69d86adb839241dc37d1a7113295a9c51623e51e9408f9623ed49a63a3ba6269172872088213332f38370af530d5be56e54115b0884ace6813911bfc6873bea28207741f4b2471b797bab156e4c6ead91659076553cee1db82c0cebdd17b64802a20c7ee6a3414f959133e6c435efe9241ab7d0203010001a321301f301d0603551d0e041604143806aea351c74f2a8b83fa26c0a9e3d3820b6699300d06092a864886f70d01010b050003820101006ebcc664b996f15c1e03d041eebbdf74a0976d117d68f34d21ef67855b614f5a2bfede66c9d4ea78fe3b50e3673890dfa2eb9eaf4321b30eb76be6f5944004b6501b2629ae4f2c6750f784ea2f9be6c26318258f98772fd3ff0c6ea817fb76d9ae02daa1fa1b91653d531db345f52aa4e7b21e8f92387a2d15d1afd5556213b0c32aadd529bac330516536948bcf85398fb86a65dbae95ef0e5582a87e26b1dbcceeaf77e6e93c63042acdf49c74927561df508020547426ad37776e360feb219523ef4e2a6f5f41a43cd0c0514c53f8644c71014080cfbe036f120a6daad6e12d6b1a07939ca840af2b3373388c0ed6b18594dd838122174304d5eb720f1cef";
|
|
||||||
String singnStr = "AQAAAjAwggIsMIIBlaADAgECAgMY2gowDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQ04xCzAJ\nBgNVBAgTAmhlMQwwCgYDVQQHEwNzanoxDDAKBgNVBAoTA2VkdTEPMA0GA1UECxMGc2Nob29sMRIw\nEAYDVQQDEwltYWhvbmd5aW4wHhcNMTgxMDExMDcwMjE1WhcNNDMxMDA1MDcwMjE1WjBbMQswCQYD\nVQQGEwJDTjELMAkGA1UECBMCaGUxDDAKBgNVBAcTA3NqejEMMAoGA1UEChMDZWR1MQ8wDQYDVQQL\nEwZzY2hvb2wxEjAQBgNVBAMTCW1haG9uZ3lpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\nr0aFNvrxBnBEEbAANDcsrmBlcQBGJKsvT5onXngek2ZbkWZx8/1o8nbgCBSjAZvnXEYYjjkC5k+A\nIne1PJUF5bPKTjIQepNmtK+KVHsAJLjn6rG4fQ3oaeu0vvNBehuzt54bACbzkXZj9nV5rs8OllD9\nRronLsOb3DVJ95DyLIMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCpF6kB++zR0FW4eZaJCEAnQNP0\nGtwAnrXEpvP7ePcakk/JT/e56uTS/OAbpmM/tWETvPtx9hOB4RoPwRl3Q0G1ieCMeVyIABmGAeOk\ntARqtiExfHvorrmk4mxVIiPTwUJSWzAKuhLV93pMxTFZSZK0iTJFVVM/l8Wh3CTdFtpW+w==\n";
|
|
||||||
DataInputStream dataInputStream = new DataInputStream(
|
|
||||||
new ByteArrayInputStream(Base64.decode(singnStr, 0)));
|
|
||||||
byte[][] bArr = new byte[(dataInputStream.read() & 255)][];
|
|
||||||
for (int i = 0; i < bArr.length; i++) {
|
|
||||||
bArr[i] = new byte[dataInputStream.readInt()];
|
|
||||||
dataInputStream.readFully(bArr[i]);
|
|
||||||
}
|
|
||||||
Class cls = Class.forName("android.app.ActivityThread");
|
|
||||||
Object invoke = cls.getDeclaredMethod("currentActivityThread", new Class[0]).invoke(null, new Object[0]);
|
|
||||||
Field declaredField = cls.getDeclaredField("sPackageManager");
|
|
||||||
declaredField.setAccessible(true);
|
|
||||||
Object obj = declaredField.get(invoke);
|
|
||||||
Class cls2 = Class.forName("android.content.pm.IPackageManager");
|
|
||||||
this.base = obj;
|
|
||||||
this.sign = bArr;
|
|
||||||
this.appPkgName = context.getPackageName();
|
|
||||||
Object newProxyInstance = Proxy.newProxyInstance(cls2.getClassLoader(), new Class[]{cls2}, this);
|
|
||||||
declaredField.set(invoke, newProxyInstance);
|
|
||||||
PackageManager packageManager = context.getPackageManager();
|
|
||||||
Field declaredField2 = packageManager.getClass().getDeclaredField("mPM");
|
|
||||||
declaredField2.setAccessible(true);
|
|
||||||
declaredField2.set(packageManager, newProxyInstance);
|
|
||||||
System.out.println("PmsHook success.");
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("PmsHook failed.");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object obj, Method method, Object[] objArr) throws Throwable {
|
|
||||||
if ("getPackageInfo".equals(method.getName())) {//方法名对上
|
|
||||||
String str = (String) objArr[0];
|
|
||||||
if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPkgName.equals(str)) {
|
|
||||||
PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr);
|
|
||||||
packageInfo.signatures = new Signature[this.sign.length];
|
|
||||||
for (int i = 0; i < packageInfo.signatures.length; i++) {
|
|
||||||
packageInfo.signatures[i] = new Signature(this.sign[i]);
|
|
||||||
}
|
|
||||||
return packageInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return method.invoke(this.base, objArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -21,12 +21,12 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
tv = findViewById(R.id.sample_text);
|
tv = findViewById(R.id.sample_text);
|
||||||
|
|
||||||
if(APISecurity.init(this)){
|
if(APISecurity.init(getApplicationContext())){
|
||||||
tv.setText("初始化ok");
|
tv.setText("初始化ok");
|
||||||
}else {
|
}else {
|
||||||
tv.setText("初始化fail");
|
tv.setText("初始化fail");
|
||||||
}
|
}
|
||||||
APISecurity.verify(this);
|
APISecurity.verify(getApplicationContext());
|
||||||
findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() {
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
229
app/src/main/java/cn/android/sample/MyApplication.java
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package cn.android.sample;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目名 FileTransfer
|
||||||
|
* 所在包 bin.mt.apksignaturekillerplus
|
||||||
|
* 作者 mahongyin
|
||||||
|
* 时间 2020-03-16 10:55
|
||||||
|
* 邮箱 mhy.work@qq.com
|
||||||
|
* 描述 说明: 将application替换到AndroidMainfest.xml 代码里改放APPlication
|
||||||
|
* 代码里替换要hook的签名值 始终返回原始签名,达到欺骗
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.Signature;
|
||||||
|
import android.content.pm.SigningInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
import cn.android.security.APISecurity;
|
||||||
|
//继承原application
|
||||||
|
|
||||||
|
public class MyApplication extends Application implements InvocationHandler {
|
||||||
|
private static final int GET_SIGNATURES = 64;
|
||||||
|
private String appPkgName = "";
|
||||||
|
private String appPath = "";
|
||||||
|
private Object base;
|
||||||
|
private byte[][] sign;
|
||||||
|
static MyApplication app;
|
||||||
|
|
||||||
|
|
||||||
|
public MyApplication() {
|
||||||
|
// 在构造函数里提早检测
|
||||||
|
// earlyCheckSign();
|
||||||
|
}
|
||||||
|
|
||||||
|
void earlyCheckSign() {
|
||||||
|
// 手动构造 context
|
||||||
|
try {
|
||||||
|
Context context = APISecurity.createContext();
|
||||||
|
//用新 context 校验签名的过程(正常的检测一样)
|
||||||
|
String sing = APISecurity.getInstalledAPKSignature(context, context.getPackageName());
|
||||||
|
Log.e("mhyLog手动构造", sing);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void attachBaseContext(Context context) {
|
||||||
|
//在这里hook 签名校验被
|
||||||
|
// hook(context);
|
||||||
|
super.attachBaseContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = this;
|
||||||
|
Application nowApplication = app;
|
||||||
|
// SystemProperties.getInt("ro.build.version.sdk", 0);
|
||||||
|
//再这看 不一定靠谱
|
||||||
|
Log.e("mhyLog","检查App名:"+nowApplication.getClass().getSimpleName());
|
||||||
|
//在签名校验被hook 之后重置PackageManager
|
||||||
|
/*在这里 重置PackageManager 只要在验证前重置即可*/
|
||||||
|
// AppSigning.resetPackageManager(getBaseContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局hook 签名校验
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
private void hook(Context context) {
|
||||||
|
try {
|
||||||
|
// String realy = "308203273082020fa003020102020477d6d1f6300d06092a864886f70d01010b05003044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f301e170d3139303330353032343132345a170d3434303232373032343132345a3044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f30820122300d06092a864886f70d01010105000382010f003082010a0282010100a3ac52268a32e8420a20a727c184c133d513998a207e198f5a535d628a436ba5e095e7ba3f92535234a83fb6272e70ed6113d8f6facc3dee2cfc076a3bd93dad3520fd5d9d9ae4c48afe56e7b421f5de2adfbc23e450f7a5f71e0afdec047b1ce8d7be62ef754a9d43bf36d9b9e0728fc268cb845b464cce1370573dfafd6c40b2efb98ba1f20c5a63c417264b69d86adb839241dc37d1a7113295a9c51623e51e9408f9623ed49a63a3ba6269172872088213332f38370af530d5be56e54115b0884ace6813911bfc6873bea28207741f4b2471b797bab156e4c6ead91659076553cee1db82c0cebdd17b64802a20c7ee6a3414f959133e6c435efe9241ab7d0203010001a321301f301d0603551d0e041604143806aea351c74f2a8b83fa26c0a9e3d3820b6699300d06092a864886f70d01010b050003820101006ebcc664b996f15c1e03d041eebbdf74a0976d117d68f34d21ef67855b614f5a2bfede66c9d4ea78fe3b50e3673890dfa2eb9eaf4321b30eb76be6f5944004b6501b2629ae4f2c6750f784ea2f9be6c26318258f98772fd3ff0c6ea817fb76d9ae02daa1fa1b91653d531db345f52aa4e7b21e8f92387a2d15d1afd5556213b0c32aadd529bac330516536948bcf85398fb86a65dbae95ef0e5582a87e26b1dbcceeaf77e6e93c63042acdf49c74927561df508020547426ad37776e360feb219523ef4e2a6f5f41a43cd0c0514c53f8644c71014080cfbe036f120a6daad6e12d6b1a07939ca840af2b3373388c0ed6b18594dd838122174304d5eb720f1cef";
|
||||||
|
// realy= Base64.encodeToString(realy.getBytes(),Base64.DEFAULT);
|
||||||
|
String singnStr = "AQAAAjAwggIsMIIBlaADAgECAgMY2gowDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAmhlMQwwCgYDVQQHEwNzanoxDDAKBgNVBAoTA2VkdTEPMA0GA1UECxMGc2Nob29sMRIwEAYDVQQDEwltYWhvbmd5aW4wHhcNMTgxMDExMDcwMjE1WhcNNDMxMDA1MDcwMjE1WjBbMQswCQYDVQQGEwJDTjELMAkGA1UECBMCaGUxDDAKBgNVBAcTA3NqejEMMAoGA1UEChMDZWR1MQ8wDQYDVQQLEwZzY2hvb2wxEjAQBgNVBAMTCW1haG9uZ3lpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAr0aFNvrxBnBEEbAANDcsrmBlcQBGJKsvT5onXngek2ZbkWZx8/1o8nbgCBSjAZvnXEYYjjkC5k+AIne1PJUF5bPKTjIQepNmtK+KVHsAJLjn6rG4fQ3oaeu0vvNBehuzt54bACbzkXZj9nV5rs8OllD9RronLsOb3DVJ95DyLIMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCpF6kB++zR0FW4eZaJCEAnQNP0GtwAnrXEpvP7ePcakk/JT/e56uTS/OAbpmM/tWETvPtx9hOB4RoPwRl3Q0G1ieCMeVyIABmGAeOktARqtiExfHvorrmk4mxVIiPTwUJSWzAKuhLV93pMxTFZSZK0iTJFVVM/l8Wh3CTdFtpW+w==";
|
||||||
|
DataInputStream dataInputStream = new DataInputStream(
|
||||||
|
new ByteArrayInputStream(Base64.decode(singnStr, Base64.DEFAULT)));
|
||||||
|
byte[][] bArr = new byte[(dataInputStream.read() & 255)][];
|
||||||
|
for (int i = 0; i < bArr.length; i++) {
|
||||||
|
bArr[i] = new byte[dataInputStream.readInt()];
|
||||||
|
dataInputStream.readFully(bArr[i]);
|
||||||
|
}
|
||||||
|
//hook全局sPackageManager对象
|
||||||
|
Class cls = Class.forName("android.app.ActivityThread");
|
||||||
|
Object invoke = cls.getDeclaredMethod("currentActivityThread", new Class[0]).invoke(null, new Object[0]);
|
||||||
|
Field declaredField = cls.getDeclaredField("sPackageManager");
|
||||||
|
declaredField.setAccessible(true);
|
||||||
|
Object obj = declaredField.get(invoke);
|
||||||
|
Class cls2 = Class.forName("android.content.pm.IPackageManager");
|
||||||
|
this.base = obj;
|
||||||
|
this.sign = bArr;
|
||||||
|
this.appPkgName = context.getPackageName();
|
||||||
|
this.appPath = context.getPackageResourcePath();
|
||||||
|
Object newProxyInstance = Proxy.newProxyInstance(cls2.getClassLoader(), new Class[]{cls2}, this);
|
||||||
|
declaredField.set(invoke, newProxyInstance);
|
||||||
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
Field declaredField2 = packageManager.getClass().getDeclaredField("mPM");
|
||||||
|
declaredField2.setAccessible(true);
|
||||||
|
declaredField2.set(packageManager, newProxyInstance);
|
||||||
|
System.out.println("PmsHook success.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("PmsHook failed.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint({"PrivateApi","DiscouragedPrivateApi"})
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object obj, Method method, Object[] objArr) throws Throwable {
|
||||||
|
if ("getPackageInfo".equals(method.getName())) {//方法名对上
|
||||||
|
Log.e("mhyLogHook","getPackageInfo");
|
||||||
|
String str = (String) objArr[0];
|
||||||
|
if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPkgName.equals(str)) {
|
||||||
|
PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
//packageInfo.signingInfo.getApkContentsSigners();//mSigningDetails.signatures
|
||||||
|
//mSigningDetails.signatures;//PackageParser.SigningDetails
|
||||||
|
SigningInfo signingInfo= packageInfo.signingInfo;
|
||||||
|
// Class<?> sgInfo_clazz=SigningInfo.class;//getClass()&Class.forName(“类完整的路径”)仅限于引用类型的对象
|
||||||
|
// Field mSigningDetails_f=sgInfo_clazz.getDeclaredField("mSigningDetails");
|
||||||
|
// mSigningDetails_f.setAccessible(true);
|
||||||
|
// Object mSigningDetails = mSigningDetails_f.get(signingInfo);//PackageParser.SigningDetails
|
||||||
|
|
||||||
|
Class<?> pkgParserCls = Class.forName("android.content.pm.PackageParser");
|
||||||
|
Constructor<?> pkgParserCt = pkgParserCls.getConstructor();//null;
|
||||||
|
Object pkgParser = pkgParserCt.newInstance();//null;
|
||||||
|
// pkgParserCt =
|
||||||
|
// pkgParser =
|
||||||
|
Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, int.class);
|
||||||
|
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(getPackageResourcePath()), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||||
|
|
||||||
|
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||||
|
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, true);
|
||||||
|
|
||||||
|
// Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||||
|
// pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, false);
|
||||||
|
|
||||||
|
Field mSigningDetailsField = pkgParserPkg.getClass().getDeclaredField("mSigningDetails"); // SigningDetails
|
||||||
|
mSigningDetailsField.setAccessible(true);
|
||||||
|
Object mSigningDetails = mSigningDetailsField.get(pkgParserPkg);
|
||||||
|
|
||||||
|
Field infoField = mSigningDetails.getClass().getDeclaredField("signatures");
|
||||||
|
infoField.setAccessible(true);
|
||||||
|
Signature[] info = new Signature[this.sign.length];
|
||||||
|
for (int i = 0; i < this.sign.length; i++) {
|
||||||
|
info[i] = new Signature(this.sign[i]);
|
||||||
|
}
|
||||||
|
// Signature[] info = (Signature[]) infoField.get(mSigningDetails);//获取属性值
|
||||||
|
infoField.set(mSigningDetails, info);
|
||||||
|
}// else { //双hook
|
||||||
|
packageInfo.signatures = new Signature[this.sign.length];
|
||||||
|
for (int i = 0; i < packageInfo.signatures.length; i++) {
|
||||||
|
packageInfo.signatures[i] = new Signature(this.sign[i]);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return packageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if ("getPackageArchiveInfo".equals(method.getName())) {//从安装路径获取apk签名方法
|
||||||
|
Log.e("mhyLogHook","getPackageArchiveInfo");
|
||||||
|
// getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags)
|
||||||
|
String str = (String) objArr[0];//第一个参数
|
||||||
|
if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPath.equals(str)) {
|
||||||
|
PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
SigningInfo signingInfo= packageInfo.signingInfo;
|
||||||
|
// Class<?> sgInfo_clazz=SigningInfo.class;
|
||||||
|
// Field mSigningDetails_f=sgInfo_clazz.getDeclaredField("mSigningDetails");
|
||||||
|
// mSigningDetails_f.setAccessible(true);
|
||||||
|
// Object mSigningDetails = mSigningDetails_f.get(signingInfo);
|
||||||
|
|
||||||
|
Class<?> pkgParserCls = Class.forName("android.content.pm.PackageParser");
|
||||||
|
Constructor<?> pkgParserCt = pkgParserCls.getConstructor();//null;
|
||||||
|
Object pkgParser = pkgParserCt.newInstance();//null;
|
||||||
|
// pkgParserCt =
|
||||||
|
// pkgParser =
|
||||||
|
Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, int.class);
|
||||||
|
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(getPackageResourcePath()), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||||
|
|
||||||
|
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||||
|
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, true);
|
||||||
|
|
||||||
|
// Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||||
|
// pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, false);
|
||||||
|
|
||||||
|
Field mSigningDetailsField = pkgParserPkg.getClass().getDeclaredField("mSigningDetails"); // SigningDetails
|
||||||
|
mSigningDetailsField.setAccessible(true);
|
||||||
|
Object mSigningDetails = mSigningDetailsField.get(pkgParserPkg);
|
||||||
|
|
||||||
|
Field infoField = mSigningDetails.getClass().getDeclaredField("signatures");
|
||||||
|
infoField.setAccessible(true);
|
||||||
|
Signature[] info = new Signature[this.sign.length];
|
||||||
|
for (int i = 0; i < this.sign.length; i++) {
|
||||||
|
info[i] = new Signature(this.sign[i]);
|
||||||
|
}
|
||||||
|
// Signature[] info = (Signature[]) infoField.get(mSigningDetails);//获取属性值
|
||||||
|
infoField.set(mSigningDetails, info);
|
||||||
|
} //else { //双hook
|
||||||
|
packageInfo.signatures = new Signature[this.sign.length];
|
||||||
|
for (int i = 0; i < packageInfo.signatures.length; i++) {
|
||||||
|
packageInfo.signatures[i] = new Signature(this.sign[i]);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return packageInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return method.invoke(this.base, objArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="78.5885"
|
|
||||||
android:endY="90.9159"
|
|
||||||
android:startX="48.7653"
|
|
||||||
android:startY="61.0927"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 15 KiB |
BIN
jni属性签名.jpg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |