对帖子 [还能不能有点职业道德]菜鸟学破解---破解辅助ID注册码 破解的完善
看了[还能不能有点职业道德]菜鸟学破解---破解辅助ID注册码这篇帖子后,发现楼主使用的工具破解的,而且评论区有一些人有疑惑,本着分享的精神写了这篇文章,原贴地址: https://www.52pojie.cn/thread-677470-1-1.html
这篇文章只适合和我一样的新手玩家!!!for(int n=0;n<50;n++){ String paramString=Integer.toString(((int)(Math.random() * 1000000000 % 100000)));
SecretKeySpec localSecretKeySpec;
try {
localSecretKeySpec = new SecretKeySpec("Format2044153997".getBytes("ASCII"), "AES");
Cipher localCipher = Cipher.getInstance("AES");
localCipher.init(1, localSecretKeySpec);
System.out.println(byte2hex(localCipher.doFinal(paramString.getBytes())).toLowerCase());
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String byte2hex(byte[] paramArrayOfByte) {
StringBuffer str1 = new StringBuffer();
for(int i=0;i<paramArrayOfByte.length;i++){
String str2 = Integer.toHexString(paramArrayOfByte & 0xFF);
if (str2.length() == 1) {
str1.append("0");
}
str1.append(str2);
}
return new String(str1).toUpperCase();
}@jiangwei212 @wlpkcheng
APP下载地址: 链接: https://pan.baidu.com/s/1jHMBUWu 密码: 2333
在对该软件进行破解过程中我使用了三种方式破解,有两种方式是类似的,另一种方式是逆向出算法,并且对这款app有多个注册码的原因做出了解释。
破解过程:
1. 这个app没有进行加固,直接使用工具AndroidKiller进行反编译
2. 反编译后,出现的是smail代码,为了直观,我们先转换成java代码查看,AndroidKiller这款工具可以直接将smail代码转换成java代码
3. 查看java代码,理清逻辑关系:
直接查看onCreate方法:
public void onCreate(Bundle paramBundle)
{
this.key = 2004;
super.onCreate(paramBundle);
SharedPreferences localSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean bool = localSharedPreferences.getBoolean("F1501993228010", false);
if (localSharedPreferences.getBoolean("F1501993240355", false))
{
enter();
return;
}
this.temp = localSharedPreferences.getString("sss", "");
Date localDate = new Date();
long l1 = -1;
long l2 = -1;
try
{
l3 = Long.parseLong(de(this.temp));
l1 = l3;
}
catch (Exception paramBundle)
{
long l3;
break label93;
}
catch (NumberFormatException paramBundle)
{
label93:
break label93;
}
if (bool) {
this.temp = localSharedPreferences.getString("ttt", "");
}
try
{
l3 = Long.parseLong(de(this.temp));
l2 = l3;
}
catch (Exception paramBundle)
{
LinearLayout localLinearLayout;
Object localObject;
break label129;
}
catch (NumberFormatException paramBundle)
{
label129:
label325:
break label129;
enter();
return;
}
if (localDate.getTime() - l1 > l2)
{
dia("注册码已过期!", (DialogInterface.OnClickListener)null);
localSharedPreferences.edit().putBoolean("F1501993228010", false).commit();
localSharedPreferences.edit().putString("sss", "").commit();
localSharedPreferences.edit().putString("ttt", "").commit();
this.id = localSharedPreferences.getInt("id", -1);
if (this.id == -1)
{
this.id = ((int)(Math.random() * 1000000000 % 100000));
localSharedPreferences.edit().putInt("id", this.id).commit();
}
localLinearLayout = new LinearLayout(this);
localLinearLayout.setOrientation(1);
localObject = getAssets();
paramBundle = (InputStream)null;
}
try
{
localObject = ((AssetManager)localObject).open("background.jpg");
paramBundle = (Bundle)localObject;
}
catch (IOException localIOException)
{
break label325;
}
if (paramBundle != null) {
localLinearLayout.setBackgroundDrawable(Drawable.createFromStream(paramBundle, ""));
}
this.tv = new TextView(this);
this.tv.setTextSize(20);
this.tv.setText("此枪战爆头辅助为不止心殇9.28开发,激活需要ID密码25块💰,要买加我抠抠2824130236 ___\n / ▲\n/ ̄ ヽ ■■\n● ■■\nヽ___ ■■\n )=|\n / ||\n ∩∩__とノ\n しし———┘\n");
this.tv.append("你的ID:" + this.id);
localLinearLayout.addView(this.tv);
this.edit = new EditText(this);
localLinearLayout.addView(this.edit);
paramBundle = new Button(this);
paramBundle.setText("复制ID");
paramBundle.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View paramAnonymousView)
{
((ClipboardManager)FAppProtect.this.getSystemService("clipboard")).setText(FAppProtect.this.id + "");
Toast.makeText(FAppProtect.this, FAppProtect.this.id + "", 2000).show();
}
});
localLinearLayout.addView(paramBundle);
this.enter = new Button(this);
this.enter.setText("进入");
localLinearLayout.addView(this.enter);
this.enter.setOnClickListener(new View.OnClickListener()
{
private final Date val$date;
private final SharedPreferences val$sp;
@Override
public void onClick(View paramAnonymousView)
{
int i = FAppProtect.this.id / 2;
i = FAppProtect.this.key;
i = FAppProtect.this.key;
paramAnonymousView = FAppProtect.de(FAppProtect.this.edit.getText().toString());
if (paramAnonymousView == null) {
return;
}
String[] arrayOfString = paramAnonymousView.split("z");
try
{
Integer.parseInt(arrayOfString, 16);
Toast.makeText(FAppProtect.this, "启动成功,一起去浪吧,小伙子!", 2000).show();
paramAnonymousView = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我😡";
if (arrayOfString.length > 1) {
paramAnonymousView = "软件无限期使用" + FAppProtect.formatDuring(Long.parseLong(arrayOfString));
}
FAppProtect.this.dia(paramAnonymousView, new DialogInterface.OnClickListener()
{
private final Date val$date;
private final SharedPreferences val$sp;
private final String[] val$y;
@Override
public void onClick(DialogInterface paramAnonymous2DialogInterface, int paramAnonymous2Int)
{
if (this.val$y.length > 1) {
this.val$sp.edit().putBoolean("F1501993228010", true).commit();
}
for (;;)
{
try
{
this.val$sp.edit().putString("ttt", FAppProtect.en(this.val$y)).commit();
FAppProtect.this.temp = ("" + this.val$date.getTime());
}
catch (Exception paramAnonymous2DialogInterface)
{
try
{
this.val$sp.edit().putString("sss", FAppProtect.en(FAppProtect.this.temp)).commit();
this.val$sp.edit().putInt("id", -1).commit();
FAppProtect.this.enter();
return;
paramAnonymous2DialogInterface = paramAnonymous2DialogInterface;
}
catch (Exception paramAnonymous2DialogInterface)
{
continue;
}
}
this.val$sp.edit().putBoolean("F1501993240355", true).commit();
}
}
});
return;
}
catch (NumberFormatException paramAnonymousView)
{
FAppProtect.this.enter.setText("失败,点击重试!");
}
}
});
new Button(this);
setContentView(localLinearLayout);
}
}
在代码前几行,我发现了下面这段代码
if (localSharedPreferences.getBoolean("F1501993240355", false)) {
enter();
return;
}
如果localSharedPreferences.getBoolean("F1501993240355", false) 为真,表示激活过直接进入enter()函数,enter函数展示的就是激活后的界面,如下:
所以这段代码是我们破解的关键,有两种修改smail代码的方式,一是if (localSharedPreferences.getBoolean("F1501993240355", false)) 修改成if (!localSharedPreferences.getBoolean("F1501993240355", false)),二是if (localSharedPreferences.getBoolean("F1501993240355", false))修改成if (localSharedPreferences.getBoolean("F1501993240355", true))。
4. 修改smail语法
在FAppProject.smail中oncreate方法中找到下面关键代码的对应出。(比较简单方法是查找“F1501993240355”关键字,可以快速定位)
if (localSharedPreferences.getBoolean("F1501993240355", false)){
enter();
return;
}
图中的两种方式,任意选一种都可以。
以上就是两种通过修改smail方式来达到破解的目的的方法。
5. 进行算法的逆向
点击进入按钮后触发onclick事件,对应代码是
public void onClick(View paramAnonymousView)
{
int i = FAppProtect.this.id / 2;
i = FAppProtect.this.key;
i = FAppProtect.this.key;
paramAnonymousView = FAppProtect.de(FAppProtect.this.edit.getText().toString());
if (paramAnonymousView == null) {
return;
}
String[] arrayOfString = paramAnonymousView.split("z");
try
{
Integer.parseInt(arrayOfString, 16);
Toast.makeText(FAppProtect.this, "启动成功,一起去浪吧,小伙子!", 2000).show();
paramAnonymousView = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我😡";
if (arrayOfString.length > 1) {
paramAnonymousView = "软件无限期使用" + FAppProtect.formatDuring(Long.parseLong(arrayOfString));
}
FAppProtect.this.dia(paramAnonymousView, new DialogInterface.OnClickListener()
{
private final Date val$date;
private final SharedPreferences val$sp;
private final String[] val$y;
@Override
public void onClick(DialogInterface paramAnonymous2DialogInterface, int paramAnonymous2Int)
{
if (this.val$y.length > 1) {
this.val$sp.edit().putBoolean("F1501993228010", true).commit();
}
for (;;)
{
try
{
this.val$sp.edit().putString("ttt", FAppProtect.en(this.val$y)).commit();
FAppProtect.this.temp = ("" + this.val$date.getTime());
}
catch (Exception paramAnonymous2DialogInterface)
{
try
{
this.val$sp.edit().putString("sss", FAppProtect.en(FAppProtect.this.temp)).commit();
this.val$sp.edit().putInt("id", -1).commit();
FAppProtect.this.enter();
return;
paramAnonymous2DialogInterface = paramAnonymous2DialogInterface;
}
catch (Exception paramAnonymous2DialogInterface)
{
continue;
}
}
this.val$sp.edit().putBoolean("F1501993240355", true).commit();
}
}
});
return;
}
catch (NumberFormatException paramAnonymousView)
{
FAppProtect.this.enter.setText("失败,点击重试!");
}
}
});
大概流程是先获取用户输入的激活码,然后调用de方法,在de方法中会调用byte2hex方法,然后判断de方法返回值是否是NULL,不是则表明输入激活码正确,大家发现么有判断激活码是否正确和给我们的id没有任何关系。
那如何获取正确的激活码呢?通过查看代码,发现有一个en函数和de函数,de是解密函数,那en就是加密函数了(虽然有en加密函数,但是没有被调用,我们可以直接查看en函数,来得到激活码,刚开始我一直研究de函数,没有发现有en加密函数,所以走了很多弯路)
涉及到的加密代码有:
public static String en(String paramString)
throws Exception
{
if ("Format2044153997" == null) {
return (String)null;
}
if ("Format2044153997".length() != 16) {
return (String)null;
}
SecretKeySpec localSecretKeySpec = new SecretKeySpec("Format2044153997".getBytes("ASCII"), "AES");
Cipher localCipher = Cipher.getInstance("AES");
localCipher.init(1, localSecretKeySpec);
return byte2hex(localCipher.doFinal(paramString.getBytes())).toLowerCase();
}
public static String byte2hex(byte[] paramArrayOfByte)
{
String str1 = "";
int i = 0;
if (i >= paramArrayOfByte.length) {
return str1.toUpperCase();
}
String str2 = Integer.toHexString(paramArrayOfByte & 0xFF);
if (str2.length() == 1) {}
for (str1 = new StringBuffer().append(str1).append("0").toString() + str2;; str1 = str1 + str2)
{
i += 1;
break;
}
}
给en加密函数传入的参数就是id值,该值范围由this.id = ((int)(Math.random() * 1000000000 % 100000)); 这里决定。
我使用java写了一个简单的获取激活码的代码,获取到一下激活码:
9e3bcb38bc593906eb7614756ef11907
61beeb148d50f2cb0151f7d04cbe6aa8
4b7cf14cca1cb8a8d4b3f65654126b75
部分关键代码:
上传了word的格式的 凉生我怕怕 发表于 2017-12-21 14:18
你少写了这个东西也比较重要
int j = paramString.length();
if (j % 2 == 1) {
其实不用的,1 对应的注册码是80e1f490cc8e40d6adfbffa34555ec18,可以注册成功的
int j = paramString.length();
if (j % 2 == 1) {
return (byte[])null;
}
是对密文的长度判断的
String[] arrayOfString = paramAnonymousView.split("z");
虽然判断了明文中是否包含z,但是包含也可以使用,只是不会提示:软件无限期使用
String[] arrayOfString = paramAnonymousView.split("z");
try
{
Integer.parseInt(arrayOfString, 16);
Toast.makeText(FAppProtect.this, "启动成功,一起去浪吧,小伙子!", 2000).show();
paramAnonymousView = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我😡";
if (arrayOfString.length > 1) {
paramAnonymousView = "软件无限期使用" + FAppProtect.formatDuring(Long.parseLong(arrayOfString));
} 你少写了这个东西也比较重要
int j = paramString.length();
if (j % 2 == 1) {
return (byte[])null;
}
加密之前的明文长度必须是2 的 倍数
并且必须包含 字符串z
String[] arrayOfString = paramAnonymousView.split("z"); 哈哈 我还做了视频的 分析分析wlpkcheng老铁的吃鸡辅助注册码算法 凉生我怕怕 发表于 2017-12-21 14:16
哈哈 我还做了视频的 分析分析wlpkcheng老铁的吃鸡辅助注册码算法
厉害了 老铁,我新手,第一次写,写的不好,得向你好好学习 小灰灰~ 发表于 2017-12-21 14:20
厉害了 老铁,我新手,第一次写,写的不好,得向你好好学习
我也是新手 哈哈 凉生我怕怕 发表于 2017-12-21 14:38
我也是新手 哈哈
多多交流哈,向大佬学习,看你发帖子就能感觉出来你的功底很不错,比我厉害{:1_893:} 我是过来跟着学习一下的, 小灰灰~ 发表于 2017-12-21 14:41
其实不用的,1 对应的注册码是80e1f490cc8e40d6adfbffa34555ec18,可以注册成功的
int j = paramString. ...
split 是用z字符串来分割 返回数组= = 哈哈某种意义上面来说确实可以判断是否字符串包含z
= = 我说的就是判断密文的长度是否是2的倍数 有矛就有盾,有盾就有矛