吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12562|回复: 24
收起左侧

[Android 原创] 小议JEB3逆向工程

  [复制链接]
scz 发表于 2020-6-5 14:02
标题: 小议JEB3逆向工程

创建: 2020-06-05 12:32
更新:
链接: http://scz.617.cn:8/misc/202006051232.txt

2020.6.1有个JEB Pro 3.19.1.202005071620被放出来了,小钻风说可能是信用卡欺
诈弄来的。当时给他弄了个传统keygen对付着用,第二天他说有人放出另一种keygen,
可以离线算注册码,就是许可证密钥(License key)。这事我以前没干过,有点意思。

以调试方式启动JEB3,用Eclipse Attach。

点击"Manual Key Generation",看到"许可证数据(License data)"。在Eclipse中
Suspend JVM,主线程调用栈回溯是:

org.eclipse.swt.internal.win32.OS.WaitMessage() line: not available [native method]
org.eclipse.swt.widgets.Display.sleep() line: 4528
com.pnfsoftware.jeb.rcpclient.util.SwtUtil.sleep(org.eclipse.swt.widgets.Display) line: 52
com.pnfsoftware.jeb.rcpclient.dialogs.LicenseKeyDialog(com.pnfsoftware.jeb.rcpclient.dialogs.JebDialog).open() line: 395
com.pnfsoftware.jeb.rcpclient.dialogs.LicenseKeyDialog.open() line: 50
com.pnfsoftware.jeb.rcpclient.dialogs.LicenseKeyAutoDialog$3.widgetSelected(org.eclipse.swt.events.SelectionEvent) line: 190
org.eclipse.swt.widgets.TypedListener.handleEvent(org.eclipse.swt.widgets.Event) line: 252
org.eclipse.swt.widgets.EventTable.sendEvent(org.eclipse.swt.widgets.Event) line: 89
org.eclipse.swt.widgets.Display.sendEvent(org.eclipse.swt.widgets.EventTable, org.eclipse.swt.widgets.Event) line: 4105
org.eclipse.swt.widgets.Button(org.eclipse.swt.widgets.Widget).sendEvent(org.eclipse.swt.widgets.Event) line: 1037
org.eclipse.swt.widgets.Display.runDeferredEvents() line: 3922
org.eclipse.swt.widgets.Display.readAndDispatch() line: 3524
com.pnfsoftware.jeb.rcpclient.dialogs.LicenseKeyAutoDialog(com.pnfsoftware.jeb.rcpclient.dialogs.JebDialog).open() line: 394
com.pnfsoftware.jeb.rcpclient.dialogs.LicenseKeyAutoDialog.open() line: 83
com.pnfsoftware.jeb.rcpclient.RcpClientContext.retrieveLicenseKey(java.lang.String) line: 1699
com.pnfsoftware.jeb.rcpclient.RcpClientContext(com.pnfsoftware.jeb.client.AbstractClientContext).prepareCheckLicenseKey() line: 736
com.pnfsoftware.jeb.rcpclient.RcpClientContext(com.pnfsoftware.jeb.client.AbstractClientContext).start() line: 442
com.pnfsoftware.jeb.rcpclient.RcpClientContext.start() line: 1522
com.pnfsoftware.jeb.rcpclient.RcpClientContext.onApplicationReady(com.pnfsoftware.jeb.rcpclient.extensions.app.App) line: 1212
com.pnfsoftware.jeb.rcpclient.JebApp.onApplicationReady() line: 73
com.pnfsoftware.jeb.rcpclient.JebApp(com.pnfsoftware.jeb.rcpclient.extensions.app.App).run() line: 186
com.pnfsoftware.jeb.rcpclient.Launcher.main(java.lang.String[]) line: 35

--------------------------------------------------------------------------
/*
* com.pnfsoftware.jeb.rcpclient.RcpClientContext.retrieveLicenseKey(String) : String
*/
public String retrieveLicenseKey(String licdata)
--------------------------------------------------------------------------

这个函数负责弹框显示"许可证数据(License data)",形参licdata就是对话框里的
数字串。

--------------------------------------------------------------------------
/*
* com.pnfsoftware.jeb.client.AbstractClientContext.prepareCheckLicenseKey() : void
*/
private void prepareCheckLicenseKey()
{
  ip.RF();

  String str1 = this.pm.getString(".LicenseKey");
  int[] arrayOfInt = new int[1];

  int i = 0;
  if (!Strings.isBlank(str1))
  {
    long[] arrayOfLong1 = ek.Ql();
    for (long l2 : arrayOfLong1)
    {
      Fp localFp2 = new Fp(l2);
      if (localFp2.RF(str1, arrayOfInt))
      {
        i = 1;
        break;
      }
    }
  }
  if (i == 0)
  {
    ip.Ql();
/*
* 733行,返回MachineId
*/
    long l1 = ek.RF();
/*
* 734行
*/
    Fp localFp1 = new Fp(l1);
/*
* 735行,返回"许可证数据(License data)"
*/
    String str2 = localFp1.RF();
/*
* 736行,str2即"许可证数据(License data)",str1即"许可证密钥(License key)"
*/
    str1 = retrieveLicenseKey(str2);
    ip.RF();
    if (!localFp1.RF(str1, arrayOfInt))
    {
      logger.info(S.s(437), new Object[0]);
      terminate();
    }
    this.pm.setString(".LicenseKey", str1.trim());
  }
  Licensing.setLicenseTimestamp(arrayOfInt[0]);

  int j = Licensing.getExpirationTimestamp();
  if (j != 0) {
    if ((j < 0) || (getStartTimestamp() >= j))
    {
      this.pm.setBoolean(".SupportExpired", Boolean.valueOf(true));
      notifySupportExpired();
    }
    else if (this.pm.getBoolean(".SupportExpired"))
    {
      this.pm.setBoolean(".SupportExpired", Boolean.valueOf(false));
    }
  }
}
--------------------------------------------------------------------------
/*
* com.pnfsoftware.jebglobal.Fp
*/
public class Fp
{
/*
* MachineId
*/
  private long RF;

/*
* 设置MachineId
*/
  public Fp(long paramLong)
  {
    this.RF = paramLong;
  }

/*
* 返回"许可证数据(License data)"
*/
  public String RF()
  {
    try
    {
      ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();

      LEDataOutputStream localLEDataOutputStream = new LEDataOutputStream(localByteArrayOutputStream);
      localLEDataOutputStream.writeLong(this.RF);
      localLEDataOutputStream.close();

/*
* 41行,localByteArrayOutputStream.toByteArray()等于8字节little-endian序
* 的MachineId
*
* this.RF也是MachineId
*
* 返回"许可证数据(License data)"
*/
      byte[] arrayOfByte = wR.RF(this.RF, localByteArrayOutputStream.toByteArray(), null);
/*
* 42行,对RC4结果进一步处理,就是格式化输出,没有数学变换
*/
      return wR.RF(arrayOfByte);
    }
    catch (Exception localException) {}
    return "";
  }
--------------------------------------------------------------------------

com.pnfsoftware.jebglobal.wR.RF(long, byte[], int[]) line: 104
com.pnfsoftware.jebglobal.Fp.RF() line: 41 [local variables unavailable]
com.pnfsoftware.jeb.rcpclient.RcpClientContext(com.pnfsoftware.jeb.client.AbstractClientContext).prepareCheckLicenseKey() line: 735 [local variables unavailable]
com.pnfsoftware.jeb.rcpclient.RcpClientContext(com.pnfsoftware.jeb.client.AbstractClientContext).start() line: 442
com.pnfsoftware.jeb.rcpclient.RcpClientContext.start() line: 1522
com.pnfsoftware.jeb.rcpclient.RcpClientContext.onApplicationReady(com.pnfsoftware.jeb.rcpclient.extensions.app.App) line: 1212
com.pnfsoftware.jeb.rcpclient.JebApp.onApplicationReady() line: 73
com.pnfsoftware.jeb.rcpclient.JebApp(com.pnfsoftware.jeb.rcpclient.extensions.app.App).run() line: 186
com.pnfsoftware.jeb.rcpclient.Launcher.main(java.lang.String[]) line: 35

--------------------------------------------------------------------------
/*
* com.pnfsoftware.jebglobal.wR.RF(long, byte[], int[]) : byte[]
*/
public static byte[] RF(long paramLong, byte[] paramArrayOfByte, int[] paramArrayOfInt)
{
  try
  {
    if (paramArrayOfByte == null) {
      return null;
    }
    ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();

    LEDataOutputStream localLEDataOutputStream = new LEDataOutputStream(localByteArrayOutputStream);

    localLEDataOutputStream.writeInt(64 + paramArrayOfByte.length);
/*
* 113行,随机数
*/
    localLEDataOutputStream.writeInt(UL.nextInt());

    localLEDataOutputStream.writeInt(5);
/*
* 116行,占坑,后面会回填CRC32
*/
    localLEDataOutputStream.writeInt(0);

    localLEDataOutputStream.writeInt(Licensing.user_id);
    localLEDataOutputStream.writeLong(Licensing.license_id);
/*
* 120行,MachineId
*/
    localLEDataOutputStream.writeLong(paramLong);
    localLEDataOutputStream.writeInt(Licensing.build_type);

    localLEDataOutputStream.writeInt(AbstractContext.app_ver.getMajor());
    localLEDataOutputStream.writeInt(AbstractContext.app_ver.getMinor());
    localLEDataOutputStream.writeInt(AbstractContext.app_ver.getBuildid());
    localLEDataOutputStream.writeLong(AbstractContext.app_ver.getTimestamp());

    int i = (int)((System.currentTimeMillis() - Tk) / 1000L);
    localLEDataOutputStream.writeInt(i);
/*
* 131行
*
* com.pnfsoftware.jebglobal.wR.Az() : int
*/
    int j = Az();

    localLEDataOutputStream.writeInt(j);
/*
* 135行,随机数
*/
    int k = UL.nextInt();
    localLEDataOutputStream.writeInt(k);
/*
* 138行,MachineId
*/
    localLEDataOutputStream.write(paramArrayOfByte);
    localLEDataOutputStream.close();

    byte[] arrayOfByte1 = localByteArrayOutputStream.toByteArray();
    ByteBuffer localByteBuffer = ByteBuffer.wrap(arrayOfByte1);
    localByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
    localByteBuffer.putInt(0, arrayOfByte1.length - 8);
/*
* 145行,计算[0x10:]的CRC32,回填相应字段
*/
    localByteBuffer.putInt(12, Hash.calculateCRC32(Arrays.copyOfRange(arrayOfByte1, 16, arrayOfByte1.length)));

    byte[] arrayOfByte2 = new byte[16];
    for (int m = 0; m < 8; m++) {
      arrayOfByte2[m] = arrayOfByte1[m];
    }
    for (m = 8; m < 16; m++) {
      arrayOfByte2[m] = RF[(m - 8)];
    }
/*
* 154行,RC4()
*/
    xS.RF(arrayOfByte2, arrayOfByte1, 8, arrayOfByte1.length);
    if ((paramArrayOfInt != null) && (paramArrayOfInt.length >= 1)) {
      paramArrayOfInt[0] = k;
    }
    return arrayOfByte1;
  }
  catch (Exception localException) {}
  return null;
}
--------------------------------------------------------------------------
/*
* RC4()
*/
com.pnfsoftware.jebglobal.xS.RF(byte[], byte[], int, int) : void
--------------------------------------------------------------------------

对前述xS.RF()的第2形参尝试手工解码:

--------------------------------------------------------------------------
48 00 00 00             // +0x0 64 + 8
05 05 6e 1d             // +0x4 随机数
05 00 00 00             // +0x8 固定值5
4a 56 83 5f             // +0xc 后面所有数据的CRC32
06 58 76 3e             // +0x10
                        // User ID: 1047943174
9b b9 d0 17 50 fe da 00 // +0x14
                        // License ID: 61641164873316763
xx xx xx xx xx xx xx xx // +0x1c MachineId
...                     // +0x24 (后略)
--------------------------------------------------------------------------

RC4是流式对称加密算法,从out[]解密出in[],即可得到LicenseId、MachineId。

简化版调用关系:

--------------------------------------------------------------------------
RcpClientContext.start                              // 3.19.1.202005071620
  AbstractClientContext.start                       // RcpClientContext:1522
    AbstractClientContext.prepareCheckLicenseKey    // AbstractClientContext:442
      ek.RF                                         // AbstractClientContext:733
                                                    // 返回MachineId
      Fp.RF                                         // AbstractClientContext:735
                                                    // 返回"许可证数据(License data)"的String形式
                                                    // 对话框中显示之
        wR.RF                                       // Fp:41
                                                    // 返回"许可证数据(License data)"的byte[]形式
          xS.RF                                     // wR:154
                                                    // RC4()/PrivateTransform()
      RcpClientContext.retrieveLicenseKey           // AbstractClientContext:736
                                                    // 形参是"许可证数据(License data)"的String形式
--------------------------------------------------------------------------

这种离线keygen确实更通用,参与运算的LicenseId、MachineId都从LicenseData解
密获取,同时适用于Windows、Linux、Mac,不需要关心MachineId的数据源在不同OS
上的获取方式。

说一下过期时间的事。JEB3的这个过期时间我不确认是否真地影响过期后的继续使用,
曾经有过不同的情况,比如只影响升级,不影响继续使用。如果真影响过期后的继续
使用,可能得动态Patch一下这个类:

com.pnfsoftware.jeb.client.Licensing

具体是这个函数:

com.pnfsoftware.jeb.client.Licensing.getExpirationTimestamp() : int

该函数中用到常量86400,将之改成864000,则过期时间变成"2030-05-30"。不要贪
心地改成8640000,否则过期时间回绕到1984。

$ fc /b old new
00000034: 01 0D
00000035: 51 2F
00000036: 80 00

可以用你喜欢的任一动态Patch技术去完成内存中的常量修改,不建议静态Patch。

感慨一句,JEB2真是我的伤心之地,以至于每看到一个新版JEB出场,都要长叹一声,
欲语还休。

免费评分

参与人数 34威望 +2 吾爱币 +135 热心值 +34 收起 理由
timnech + 1 + 1 谢谢@Thanks!
Jsong_ + 1 + 1 谢谢@Thanks!
直至最终 + 1 + 1 用心讨论,共获提升!
SomnusXZY + 1 + 1 热心回复!
woyucheng + 1 + 1 谢谢@Thanks!
lmjg520 + 1 + 1 我很赞同!
丶咖啡猫丶 + 1 + 1 谢谢@Thanks!
sym945 + 1 + 1 热心回复!
初心wo + 1 + 1 谢谢@Thanks!
Hslim + 1 + 1 我很赞同!
fengbolee + 1 + 1 谢谢@Thanks!
51m0n + 1 热心回复!
yixi + 1 + 1 谢谢@Thanks!
gaosld + 1 + 1 热心回复!
月六点年一倍 + 1 + 1 我很赞同!
wjsjwr + 1 谢谢@Thanks!
evea + 1 + 1 谢谢@Thanks!
魂殇 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
exluku + 1 + 1 热心回复!
5omggx + 1 + 1 用心讨论,共获提升!
温柔的一哥 + 1 + 1 热心回复!
笙若 + 1 + 1 谢谢@Thanks!
610100 + 3 + 1 谢谢@Thanks!
起航的宇航员 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
nevinhappy + 1 + 1 谢谢@Thanks!
xdnice + 1 + 1 分析的这么详细,受教了。
石香 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
howsk + 2 + 1 用心讨论,共获提升!
老和尚 + 1 + 1 谢谢四个发威@Thanks!
苏紫方璇 + 1 + 1 tql
jgs + 1 + 1 谢谢@Thanks!
yuhan694 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
砂砾酱 + 1 + 1 谢谢@Thanks!
qtfreet00 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

田田爱崽崽 发表于 2020-6-5 14:15
逆向工程软件被人给逆向工程了

免费评分

参与人数 2吾爱币 +2 热心值 +1 收起 理由
fengbolee + 1 + 1 我很赞同!
Avenshy + 1 我很赞同!

查看全部评分

nevinhappy 发表于 2020-6-5 22:34
scz 发表于 2020-6-5 18:53
就是-agentlib:jdwp=transport=dt_socket这种啊,不要想高深了

学到了,之前调试JAVA工程,总是把Jar文件拷到个新工程,用Eclipse启动带Main的包来进行调试,这样attach更方便。
Lixinist 发表于 2020-6-5 15:28
nevinhappy 发表于 2020-6-5 17:37
@scz 四哥,你上面提到的以调试模式启动JEB是个什么模式 ?嘿嘿,一直不知道JAVA还有这个模式。
xdnice 发表于 2020-6-5 17:59
分析的很详细,受教了。
头像被屏蔽
399713194 发表于 2020-6-5 18:05
提示: 作者被禁止或删除 内容自动屏蔽
 楼主| scz 发表于 2020-6-5 18:53
nevinhappy 发表于 2020-6-5 17:37
@scz 四哥,你上面提到的以调试模式启动JEB是个什么模式 ?嘿嘿,一直不知道JAVA还有这个模式。

就是-agentlib:jdwp=transport=dt_socket这种啊,不要想高深了
zhaooptimus 发表于 2020-6-6 22:11

"逆向工程软件被人给逆向工程了"  nice
tan567421 发表于 2020-6-7 08:48
学习了。确实很牛。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 12:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表