Zyspps for android简单的注册码分析
app名称:zyspps
作者:lao3
分析的版本是v1.0, 不过应该是通杀所有版本
一、注册码校验定位
-
运行后,弹出注册窗口,随便输入用户名和注册码,点击注册后该APP直接强制结束. -
原本想通过资源定位,发现类和方法变量全部混淆成abc之类的,很难读懂. 然后从log也没看出什么关键信息,也可能我漏掉了. -
考虑到大多APP一般是通过killProcess()或者exit()结束APP,所以我们从这里入手试试. 直接上frida hook掉 function hook_java(){
Java.perform(function(){
var process = Java.use("android.os.Process");
process.killProcess.implementation=function(pid)
{
console.log('pid ==>',pid);
}
})
}
function main(){
hook_java();
}
setImmediate(main)
再次点击注册后,APP并没有结束, 证明确实是调用Process.killProcess()方法强制结束APP 通过打印堆栈信息 console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
定位到, 调用com.agis.pldps.j.a.a.a类下的方法a进行校验注册码 package com.agis.pldps.h;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Process;
import com.agis.pldps.b.a.o;
import com.agis.pldps.j.a.a.a;
import com.agis.pldps.j.f;
class b extends o {
b() {
}
public void b(DialogInterface dialogInterface, Bundle bundle) {
if (new a(f.f()).a().booleanValue()) {
super.b(dialogInterface, bundle);
} else {
Process.killProcess(Process.myPid());
}
}
}
到这里就比较清晰了, b.a().a(“regsoftkeyusername”)获取输入的用户名/注册码 public Boolean a() {
this.c = a(this.f288a, b.a().a("regsoftkeyusername"));
if (this.c != null) {
this.b = a(this.c);
TelephonyManager telephonyManager = (TelephonyManager) this.f288a.getSystemService("phone");
String a2 = b.a().a("regsoftkeyvalname");
if (a2 != null && a2.equals(b(String.valueOf(Settings.Secure.getString(this.f288a.getContentResolver(), "android_id").toUpperCase()) + telephonyManager.getDeviceId()))) {
return true;
}
}
return false;
}
注册码校验的完整类代码 package com.agis.pldps.j.a.a;
import android.content.Context;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.util.Log;
import com.agis.pldps.f.b;
import com.esri.core.internal.io.handler.c;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class a {
private Context f288a = null;
private byte[] b = null;
private String c = null;
public a(Context context) {
this.f288a = context;
}
private String a(Context context, String str) {
try {
return a(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec((String.valueOf(str) + context.getPackageName()).toCharArray(), (String.valueOf(str) + Settings.Secure.getString(context.getContentResolver(), "android_id").toUpperCase()).getBytes(), 256, 128)).getEncoded());
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e2) {
e2.printStackTrace();
}
return null;
}
private String a(byte[] bArr) {
return Base64.encodeToString(bArr, 3);
}
private byte[] a(String str) {
return Base64.decode(str, 3);
}
private String b(String str) {
if (str == null || str.length() == 0) {
return str;
}
try {
Cipher instance = Cipher.getInstance("AES");
instance.init(1, new SecretKeySpec(this.b, "AES"));
return a(instance.doFinal(str.getBytes(c.f704a))).toUpperCase();
} catch (Exception e) {
Log.w(a.class.getName(), "encrypt", e);
return null;
}
}
public Boolean a() {
this.c = a(this.f288a, b.a().a("regsoftkeyusername"));
if (this.c != null) {
this.b = a(this.c);
TelephonyManager telephonyManager = (TelephonyManager) this.f288a.getSystemService("phone");
String a2 = b.a().a("regsoftkeyvalname");
if (a2 != null && a2.equals(b(String.valueOf(Settings.Secure.getString(this.f288a.getContentResolver(), "android_id").toUpperCase()) + telephonyManager.getDeviceId()))) {
return true;
}
}
return false;
}
public Boolean b() {
this.c = a(this.f288a, b.a().a("regsoftkeyusername"));
if (this.c != null) {
this.b = a(this.c);
TelephonyManager telephonyManager = (TelephonyManager) this.f288a.getSystemService("phone");
String a2 = b.a().a("regsoftkeyvalname");
if (a2 != null && a2.equals(b(String.valueOf(Settings.Secure.getString(this.f288a.getContentResolver(), "android_id").toUpperCase()) + telephonyManager.getDeviceId()))) {
return true;
}
}
return false;
}
}
二、注册码分析
获取注册用户名,手机android_id,使用PKDF2生成AES密钥…然后对android_id(大写)+imei 进行AES加密,再次对加密结果进行BASE64编码(大写)得到最终的注册码 与 我们输入的注册码进行比对~
三、注册机实现
import base64
from hashlib import pbkdf2_hmac
from Crypto.Cipher import AES
...
...
...
deviceId = '865166028011578'
androidId = 'AA51B2B3149156A4'.upper()
username = 'lao3'
keyGen = KeyGen()
reg_code = keyGen.GenRegCode(username, deviceId, androidId)
print(f'用户名:{username},注册码:{reg_code}')
说明
完整的KeyGen代码因为限制,就没完整上传…样本比较简单,而且分析流程已经很详细…可自己动手实现
|