初中生
 
- 金币
- 258
- 好评
- 1
- 信誉
- 100
|
本帖最后由 菜菜大人. 于 2021-9-15 19:54 编辑
说来惭愧,本来是想去为我的项目atools https://wwi.lanzoui.com/b00z7gd1i
着手hook强力签名验证的,但是一时间不知道从何开始
结果之后就开始写签名验证了  
1.还是老生常谈的ApplicationInfo.signatures
private void checkSignatureAndHash() {
try {
@SuppressLint("PackageManagerGetSignatures") var sign = getPackageManager()
.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
@SuppressLint("PackageManagerGetSignatures") Signature[] signatures = getPackageManager().getPackageInfo(getPackageName(),
PackageManager.GET_SIGNATURES).signatures;
this.signature = signatures[0].toCharsString();
this.signHash = signature.hashCode() + "";
// Log.i(TAG, "signature==>" + this.signature + "\n");
// Log.i(TAG, "signHash==>" + this.signHash + "\n");
//Log.i(TAG,"sign==>"+sign+"...\n");
//308202e4308201cc020101300d06092a864886f70d01010
// Log.i(TAG, "sign2CharsString==>" + sign.toCharsString() + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
优点:常用
这个api已经被玩烂了,光靠这个完全不够
2.通过安卓9新增的API SigningInfo,一样去验证signatures
//只在安卓9以上起作用,当不起作用时则直接返回true
private boolean checkSigningInfo() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
var info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
SigningInfo signingInfo = info.signingInfo;
Signature[] signatures = signingInfo.getApkContentsSigners();
signingSign = signatures[0].toCharsString();
return signingSign.startsWith("308202c1308201a9a003");
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}//安卓版本太低不支持获取signingInfo
return true;
}
3.验证AndroidManifest.xml的applicationName属性
private boolean checkApplicationName() {
return getApplicationInfo().name.equals("caicai.App");
}
优点:可以验证是否被破解
缺点:返回值亦可被Hook
4.验证文件MD5值
//每次更新代码时值都会改变,因此比较困难才能拿到md5值并准确验证
private String myMD5() {
try {
//获取apk安装路径还有getPackageCodePath
var srcdir = getPackageManager().getPackageInfo(getPackageName(), 0).applicationInfo.sourceDir;
//获取文件md5值
var md5 = FileUtils.getMd5(new File(srcdir));
Log.i(TAG, "APK的md5值==" + md5);
return md5;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
System.exit(-1);
}
return "";
}
优点:md5值几乎不能被伪造
缺点:每次增删代码都会更改md5值,不推荐
5.验证软件最新修改时间,如
private final String apkLastModifyTime = "2021-09-09";
private boolean checkLastModifiedTime(Context ctx) {
try {
ZipFile zipFile = new ZipFile(ctx.getPackageCodePath());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
FileTime lastModifiedTime = zipFile.getEntry("classes.dex").getLastModifiedTime();
var timeResult = lastModifiedTime.to(TimeUnit.MILLISECONDS);
Date date = new Date(timeResult);
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
var modifyTime = simpleDateFormat.format(date);
Log.i(TAG, "classes.dex修改时间:" + modifyTime);
}
File file = new File(ctx.getPackageCodePath());
var fileLastModifiedTime = file.lastModified();
Date date = new Date(fileLastModifiedTime);
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
var fileModifyTime = simpleDateFormat.format(date);
Log.i(TAG, "apk修改时间:" + fileModifyTime);
return fileModifyTime.equals(apkLastModifyTime);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
优点:不会像检测MD5值一样难以把控
不足:开发时需要经常去修改apkLastModifyTime
6.native验证apk签名
// cpp/jni code
jboolean checkSignNative(JNIEnv *env, jobject instance, jobject context) {
jclass context_cls = env->GetObjectClass(context);
jmethodID getPackageManager_mid = env->GetMethodID
(context_cls, "getPackageManager", "()Landroid/content/pm/PackageManager;");
jobject packageManager_instance = env->CallObjectMethod(context, getPackageManager_mid);
jclass packageManager_cls = env->GetObjectClass(packageManager_instance);
//获取getPackageInfo方法
jmethodID getPackageInfo_mid = env->GetMethodID
(packageManager_cls, "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
jmethodID getPackageName_mid = env->GetMethodID
(context_cls, "getPackageName", "()Ljava/lang/String;");
auto pkgName = static_cast<jstring>((env->CallObjectMethod(context, getPackageName_mid)));
jobject packageInfo = env->CallObjectMethod(packageManager_instance, getPackageInfo_mid,
pkgName, 64);
//获取pkgInfo
jclass pkgInfo_cls = env->GetObjectClass(packageInfo);
//获取signatures字段
jfieldID signatures_fid = env->GetFieldID(pkgInfo_cls, "signatures",
"[Landroid/content/pm/Signature;");
jobject signatures_instance = env->GetObjectField(packageInfo, signatures_fid);
jobjectArray signatures = reinterpret_cast<jobjectArray>(signatures_instance);
//获取签名
jobject signature = env->GetObjectArrayElement(signatures, 0);
jclass string_calss = env->FindClass("java/lang/String");
jstring src_signature_str = env->NewStringUTF("308202c1308201a9a003");
jclass signature_calss = env->FindClass("android/content/pm/Signature");
jmethodID toCharsString_methodID = env->
GetMethodID(signature_calss, "toCharsString", "()Ljava/lang/String;");
jstring sign_str = (jstring) env->CallObjectMethod(signature, toCharsString_methodID);
//因为我们比较前20位,所以截取前20位字符串
jmethodID startWith_mid = env->GetMethodID(string_calss, "startsWith",
"(Ljava/lang/String;)Z");
jmethodID subString_mid = env->GetMethodID(string_calss, "substring",
"(II)Ljava/lang/String;");
//jmethodID equals_mid=env->GetMethodID(string_calss,"equals", "(Ljava/lang/Object;)Z");
jstring sign20 = (jstring) env->CallObjectMethod(sign_str, subString_mid, 0, 20);
bool yes=env->CallBooleanMethod(sign20,startWith_mid,src_signature_str);
if(yes){
//签名正确
return JNI_TRUE;
}else{
//签名错误
return JNI_FALSE;
}
}
private native boolean checkSignNative(Context ctx);
优点:需要电脑端ida进行破解!!!
缺点:仅使用Java API一样能被hook,且代码繁琐
7.验证软件大小,当用户破解软件时无可避免会引起软件大小的变化,所以
private boolean checkApkSize(){
var apk=new File(getPackageCodePath());
var size=apk.length();
var realSize=4231000;
//4231000
Log.i(TAG, "apk大小:" +size);
//总不可能大于原先的两倍吧?
return size<realSize*2&size>=realSize;
}
缺点:需要经常关注软件大小
样本也在链接中
...暂时想不到了 看了这么多了还不关注我?  
如有其他验证手段也可以拿出分享
|
-
查看全部评分
总评分:好评 +1
金币 +1
|