小灰灰~ 发表于 2017-12-21 14:05

对帖子 [还能不能有点职业道德]菜鸟学破解---破解辅助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块&#128176;,要买加我抠抠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 = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我&#128545;";
          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 = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我&#128545;";
          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:41

凉生我怕怕 发表于 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 = "激活成功,不止心殇告诫大家:请尊重作者也尊重自己,不要非法转载别人,否则和谐没用了不要找我&#128545;";
          if (arrayOfString.length > 1) {
            paramAnonymousView = "软件无限期使用" + FAppProtect.formatDuring(Long.parseLong(arrayOfString));
          }

连晋 发表于 2017-12-21 14:18

你少写了这个东西也比较重要
int j = paramString.length();
    if (j % 2 == 1) {
      return (byte[])null;
    }

加密之前的明文长度必须是2 的   倍数   

并且必须包含 字符串z

String[] arrayOfString = paramAnonymousView.split("z");

连晋 发表于 2017-12-21 14:16

哈哈 我还做了视频的 分析分析wlpkcheng老铁的吃鸡辅助注册码算法

小灰灰~ 发表于 2017-12-21 14:20

凉生我怕怕 发表于 2017-12-21 14:16
哈哈 我还做了视频的 分析分析wlpkcheng老铁的吃鸡辅助注册码算法

厉害了 老铁,我新手,第一次写,写的不好,得向你好好学习

连晋 发表于 2017-12-21 14:38

小灰灰~ 发表于 2017-12-21 14:20
厉害了 老铁,我新手,第一次写,写的不好,得向你好好学习

我也是新手 哈哈

小灰灰~ 发表于 2017-12-21 14:46

凉生我怕怕 发表于 2017-12-21 14:38
我也是新手 哈哈

多多交流哈,向大佬学习,看你发帖子就能感觉出来你的功底很不错,比我厉害{:1_893:}

kk1212 发表于 2017-12-21 15:11

我是过来跟着学习一下的,

连晋 发表于 2017-12-21 15:24

小灰灰~ 发表于 2017-12-21 14:41
其实不用的,1 对应的注册码是80e1f490cc8e40d6adfbffa34555ec18,可以注册成功的
int j = paramString. ...

split 是用z字符串来分割 返回数组= = 哈哈某种意义上面来说确实可以判断是否字符串包含z

= = 我说的就是判断密文的长度是否是2的倍数

hongxw 发表于 2017-12-21 15:43

有矛就有盾,有盾就有矛
页: [1] 2 3
查看完整版本: 对帖子 [还能不能有点职业道德]菜鸟学破解---破解辅助ID注册码 破解的完善