吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 31391|回复: 136
收起左侧

[原创] Charles 4.5.1逆向工程(2)

    [复制链接]
scz 发表于 2019-10-24 17:49
本帖最后由 scz 于 2019-10-25 17:53 编辑

标题: Charles 4.5.1逆向工程(2)

创建: 2019-10-24 17:14
更新: 2019-10-25 17:50
链接: http://scz.617.cn:8/misc/201910241714.txt
      https://www.52pojie.cn/thread-1042815-1-1.html

续前文:

《Charles 4.5.1逆向工程》
http://scz.617.cn:8/misc/201910170937.txt
https://www.52pojie.cn/thread-1039171-1-1.html

上次通过定位Register按钮处理代码定位与License强相关的YQUd.class,后来看到
另一种定位方案,参看:

《如何逆向破解抓包工具Charles》
https://blog.csdn.net/Kaitiren/article/details/83182108

他的思路是在JD-GUI中搜"This is a 30 day trial version",定位:

com.xk72.charles.gui.SplashWindow.class

--------------------------------------------------------------------------
import com.xk72.charles.YQUd;

public class SplashWindow extends JWindow
{
    public void showSharewareStatus ()
    {
        this.showStatus( "This is a 30 day trial version. If you continue using Charles you must\npurchase a license. Please see the Help menu for details." );
    }

    public void showRegistrationStatus ()
    {
        /*
         * 为true时表示已注册
         */
        if ( YQUd.tEdg() )
        {
            /*
             * 形如"Anyting - Site License"
             */
            this.showStatus( "Registered to: " + YQUd.NCuT() );
        }
        else
        {
            this.showSharewareStatus();
        }
    }
--------------------------------------------------------------------------

他这个思路比我的好,更简捷,不需要动态调试,不需要定位PeRA.class,直接就找
到YQUd.class。不过你可能会碰上一个问题,某些版本JD-GUI不能正确反编译
SplashWindow.class,搜索字符串失败。

暴破比较省事,高阶选手在可能的情况下提供keygen。我20岁的时候对netguy师兄佩
服得五体投地,他就是那种高阶选手。

TEAM MESMERiZE提供了通杀3.x、4.x的keygen,未做混淆,可以清楚地看到如何从注
册名变换到序列号,参看:

https://www.upload.ee/files/9009 ... -MESMERiZE.rar.html

keygen.jar
MD5     47d911ba0cecfd31c3312cbb3a344981
SHA256  973d364b71140dcf0aa2b181f1920f688a63cfe1bcdf776c0a4d463095fb1541

下面是keygen的主要逻辑,从注册名到序列号:

kg_charles_web_proxy_analyzer_v4_2.KG_Charles_Web_Proxy_Analyzer_v4_2.class

--------------------------------------------------------------------------
public String calculateSerial ( String name )
{
    /*
     * 这个神密常量并没有直接出现在Charles代码中
     */
    int serialCheckSum  = 1418211210;
    /*
     * 这个函数内置了RC5加密操作
     *
     * RC5_SETUP( 1763497072, 2049034577 )
     */
    int nameCheckSum    = calcNameChecksum( name );
    serialCheckSum     ^= nameCheckSum;
    long serial         = serialCheckSum;
    serial    <<= 32;
    serial   >>>= 32;
    serial    <<= 32;
    /*
     * 这个常量包含License类型,用网友的话说,是控制授权级别的。后面讲了如
     * 何获取这两个常量。
     */
    serial     |= 0x1CAD6BC;

    int     serialLow   = ( int )( serial & 0xFFFFFFFFFFFFFFFF );
    int     serialHigh  = ( int )( serial >>> 32 & 0xFFFFFFFFFFFFFFFF );
    int[]   serialEnc   = new int[2];

    serialEnc[0]    = serialLow;
    serialEnc[1]    = serialHigh;

    int[]   serialDec   = new int[2];

    RC5 serialDecrypter = new RC5();

    serialDecrypter.RC5_SETUP( -334581843, -1259282228 );
    /*
     * 注意,这里是解密操作,不是加密操作
     */
    serialDecrypter.RC5_DECRYPT( serialEnc, serialDec );

    long    serialDecrypted = ( serialDec[1] & 0xFFFFFFFF ) << 32;

    serialDecrypted    |= serialDec[0] & 0xFFFFFFFF;

    int xorCheckSum = calcXorChecksum( serial );

    String  strSerial   = Integer.toHexString( xorCheckSum ) + Long.toHexString( serialDecrypted );

    /*
     * 前2个字符是校验和
     */
    strSerial   = String.format( "%02X", new Object[] { Integer.valueOf( xorCheckSum ) } ) + String.format( "%016X", new Object[] { Long.valueOf( serialDecrypted ) } );

    this.tfSerial.setText( strSerial );
    return strSerial;
}
--------------------------------------------------------------------------

下面是Charles中校验序列号的相关代码:

Y:\自己的技术文档\misc\破解Charles\4.5.1\YQUd_scz.java

com.xk72.charles.YQUd.class

--------------------------------------------------------------------------
public final class YQUd
{
    /*
     * 返回错误提示,用于throw new LicenseException()
     */
    private String tEdg ( int var1 )
    {
        /*
         * RC5_SETUP( 1763497072, 2049034577 );
         */
        this.Rarr( 8800536498351690864L );

        try
        {
            String  var5;
            /*
             * dbSi[]中存放经RC5处理过的错误提示,反静态分析
             */
            byte[]  var2    = new byte[(var5 = dbSi[var1]).length() / 2];

            /*
             * 将hexstr转成byte[]
             */
            for ( int var3 = 0; var3 < var2.length; ++var3 )
            {
                var2[var3]  = ( byte )Integer.parseInt( var5.substring( var3 << 1, ( var3 << 1 ) + 2 ), 16 );
            }

            byte[]  var6;

            /*
             * RC5_EncryptArray()
             *
             * 注意,这里是加密操作,不是解密操作
             */
            for( var1 = ( var6 = this.NCuT( var2 ) ).length; var6[var1-1] == 0; --var1 )
            {
                ;
            }
            return new String( var6, 0, var1, "UTF-8" );
        }
        catch ( UnsupportedEncodingException var4 )
        {
            return "";
        }
    }

    /*
     * 检查序列号的第二步。之所以搞得这么复杂,完全是为了对抗静态分析。
     */
    private boolean tEdg ( long var1 )
    {
        /*
         * calcXorChecksum
         */
        int var3    = TryJ( var1 );
        /*
         * RC5_SETUP(),密钥与明文是同一组
         */
        this.Rarr( var1 );

        long    var4    = var1;

        for ( int var6 = 0; var6 < var3 + 35; ++var6 )
        {
            /*
             * long RC5_ENCRYPT ( long )
             */
            var4    = this.NCuT( var4 );
        }
        /*
         * 0x520AAC2983719004
         * 0x520AAC29 0x83719004
         * 1376431145 2205257732
         */
        return var4 == 5911726755176091652L;
    }

    /*
     * 检查序列号的第一步
     */
    private long TryJ ( String name_var1, String serial_var2, int type_var3 )
    {
        /*
         * 序列号18个字符
         */
        if ( serial_var2.length() != 18 )
        {
            /*
             * The license key is not the correct length. Please check your license key and try again.
             */
            throw new LicenseException( this.tEdg( 0 ) );
        }
        /*
         * 检查序列号黑名单,这些应该是曾经在Internet上贴出来过的
         */
        else if ( !serial_var2.equalsIgnoreCase( "7055ce2f8cb4f9405f" ) ... )
        {
            /*
             * 将序列号切割成3部分,2+8+8
             */
            long    serialDec_var4      = Long.parseLong(serial_var2.substring(2, 10), 16) << 32 | Long.parseLong(serial_var2.substring(10, 18), 16);
            int     xorCheckSum_var12   = Integer.parseInt(serial_var2.substring(0, 2), 16);
            /*
             * 0xB4F0E0CCEC0EAFAD
             * -1259282228 -334581843
             *
             * RC5_SETUP( -334581843, -1259282228 );
             */
            this.Rarr( -5408575981733630035L );
            long    serialEnc_var7;
             /*
              * calcXorChecksum( serial );
              * RC5_ENCRYPT( serialDec, serialEnc );
              *
              * 注意,这里是加密操作,不是解密操作
              *
              * 检查序列号校验和
              */
            if ( TryJ( serialEnc_var7 = this.NCuT( serialDec_var4 ) ) != xorCheckSum_var12 )
            {
                /*
                 * 抛出异常,显示错误提示
                 */
                throw new LicenseException( this.tEdg( 1 ) );
            }
            else
            {
                /*
                 * 处理后的序列号的低32位包含License类型,常量0x1CAD6BC会
                 * 出现在这个位置。之前这个地方看错了,少看了一组逻辑右移
                 * (>>>),错写成0x1CAD6BC未被检查。
                 */
                this.RjRQ   = ( int )( serialEnc_var7 << 32 >>> 32 >>> 24 );
                if ( this.RjRQ == 1 )
                {
                    this.aqrV   = LicenseType.tEdg;
                }
                else
                {
                    if ( this.RjRQ != type_var3 )
                    {
                        if ( this.RjRQ < type_var3 )
                        {
                            /*
                             * 抛出异常,显示错误提示
                             */
                            throw new LicenseException( this.tEdg( 3 ) );
                        }
                        /*
                         * 抛出异常,显示错误提示
                         */
                        throw new LicenseException( this.tEdg( 1 ) );
                    }
                    /*
                     * 处理后的序列号的低32位包含License类型。
                     */
                    switch( ( int )( serialEnc_var7 << 32 >>> 32 >>> 16 & 255L ) )
                    {
                    case 1:
                        this.aqrV   = LicenseType.tEdg;
                        break;
                    case 2:
                        this.aqrV   = LicenseType.TryJ;
                        break;
                    case 3:
                        this.aqrV   = LicenseType.NCuT;
                        break;
                    default:
                        /*
                         * 抛出异常,显示错误提示
                         */
                        throw new LicenseException( this.tEdg( 1 ) );
                    }
                }
                /*
                 * RC5_SETUP( 1763497072, 2049034577 );
                 */
                this.Rarr( 8800536498351690864L );
                try
                {
                    byte[]  var10   = name_var1.getBytes( "UTF-8" );
                    if ( ( type_var3 = ( var12 = var10.length ) + 4 ) % 8 != 0 )
                    {
                        type_var3  += 8 - type_var3 % 8;
                    }
                    byte[]  var14   = new byte[type_var3];
                    System.arraycopy( var10, 0, var14, 4, var12 );
                    var14[0]    = (byte)(var12 >> 24);
                    var14[1]    = (byte)(var12 >> 16);
                    var14[2]    = (byte)(var12 >> 8);
                    var14[3]    = (byte)var12;
                    /*
                     * RC5_EncryptArray();
                     */
                    byte[]  var6    = this.NCuT( var14 );
                    int     var11   = 0;
                    byte[]  var13   = var6;
                    type_var3   = var6.length;
                    /*
                     * calcNameChecksum( name )
                     */
                    for( int var15 = 0; var15 < type_var3; ++var15 )
                    {
                        byte    var5 = var13[var15];
                        /*
                         * nameCheckSum
                         */
                        var11   = ( var11 ^= var5 ) << 3 | var11 >>> 29;
                    }
                    /*
                     * XOR后应该得到"1418211210(0x54882F8A)"
                     */
                    var11  ^= ( int )( serialEnc_var7 >> 32 );
                    /*
                     * 0xA58D19C600000000
                     * 0xA58D19C654882F8A
                     *
                     * 接下来会转到"检查序列号的第二步"
                     */
                    return -6517524747541020672L | ( long )var11 << 32 >>> 32;
                }
                catch ( UnsupportedEncodingException var9 )
                {
                    return -1L;
                }
            }
        }
        else
        {
            throw new LicenseException( this.tEdg( 1 ) );
        }
    }

    /*
     * int calcXorChecksum ( long l )
     */
    private static final int TryJ ( long var0 )

    /*
     * RC5_EncryptArray(),进行RC5加密
     */
    private byte[] NCuT ( byte[] var1 )

    /*
     * RC5_ENCRYPT(),进行RC5加密
     *
     * 通过这个函数可以看出r=12
     */
    private long NCuT ( long var1 )

    /*
     * RC5_SETUP(),初始化对称密钥(8字节)
     *
     * Charles用的是RC5-32/12/8
     */
    private void Rarr ( long var1 )

    static
    {
        /*
         * 经RC5处理过的错误提示
         */
        dbSi    = new String[]{ "b241993e8a1...", ... };
        /*
         * RC5-32/r/b的特征常量P(0xB7E15163),出现在RC5_SETUP()中
         *
         * https://en.wikipedia.org/wiki/RC5
         */
        xgCJ    = -1209970333;
    }
--------------------------------------------------------------------------

TEAM MESMERiZE这伙人很厉害。calculateSerial()中出现的"1418211210"并未直接
出现在Charles代码中,单靠静态分析不可能得到这个神密常量。有两种可能,一种
是他们进行了动态调试,确实可以看到"1418211210";另一种是早期版本中出现过
"1418211210"。另一个常量0x1CAD6BC,我之前看错了YQUd.class,在某处少看了一
组逻辑右移(>>>),犯了低级错误,在注释中错写成校验序列号时0x1CAD6BC未被检查,
后来微博上有网友指正了该处错误。

为了得到"1418211210",简单地动态调试就可以,因为它与另一固定常量凑一起后出
现在"检查序列号的第二步",检查相应tEdg()的形参,其低32位就是。为了得到
0x1CAD6BC,可以拦截对应calcXorChecksum()的那个TryJ(),形参的低32位就是。这
个调试过程的前提是你有一对现成的"name:serial"。

keygen中的RC5.class值得保存,没有混淆,实际就是Java源码。

Charles用的是RC5-32/12/8,有个现成的Java实现可供参考:

https://hewgill.com/rc5/rc5java.zip

最常见的RC5配置是:

w=32
r=12
b=16
P(32)=0xb7e15163 (-0x481eae9d/-1209970333)
Q(32)=0x9e3779b9 (-0x61c88647)

在RC5的具体实现中,有两种实现方式,一种是使用P、Q动态生成一张表,另一种是
在代码中直接嵌入这张表。对于RC5-32/r/b来说,搜索P(0xB7E15163),不要搜
Q(0x9E3779B9)。一是Q有可能以负值形式出现,二是从实现上讲有两种实现方式,
stab[t]可能是预生成的,此时有P无Q,搜P可以保证命中。

这是我第二次碰上RC5,第一次碰上还是某次帮组织反APT时,一个极其强大的对手被
我方一个极其聪明的小伙子抓住了一丝尾巴,甚至都谈不上对手犯错,总归是极具传
奇色彩地还原了经RC5加密过的数据,待我临死前再来吹这个牛,如果那时我没得阿
尔茨海默症的话。

有了这个keygen,估计很长一段时间都不再需要暴破。早知道有这么个玩意儿,我剁
个毛线啊。在所谓的1024节,我们必须讨论一下程序,对吧。

keygen.jar.7z

4.74 KB, 下载次数: 870, 下载积分: 吾爱币 -1 CB

通杀3.x、4.x,不是我写的

免费评分

参与人数 29吾爱币 +27 热心值 +28 收起 理由
5omggx + 1 + 1 用心讨论,共获提升!
Trisscute + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
筱恒666 + 1 + 1 谢谢@Thanks!
wuwutian + 1 + 1 用心讨论,共获提升!
linrunqing521 + 1 热心回复!
quleilei889 + 1 谢谢@Thanks!
鱼七大大丶 + 1 + 1 用心讨论,共获提升!
oxsho + 1 热心回复!
scott.jan + 1 + 1 我很赞同!
laohucai + 1 + 1 谢谢@Thanks!
stat + 1 + 1 我很赞同!
xiong_online + 1 + 1 用心讨论,共获提升!
alien11 + 1 谢谢@Thanks!
夜尘i + 1 + 1 我很赞同!
zerglurker + 1 + 1 谢谢@Thanks!
china_wuying + 1 + 1 我很赞同!
daniel7785 + 1 用心讨论,共获提升!
wmsuper + 3 + 1 谢谢@Thanks!
N0LL + 1 + 1 谢谢@Thanks!
alsk + 1 + 1 我很赞同!
某些人 + 1 + 1 谢谢@Thanks!
LOLQAQ + 1 + 1 我很赞同!
cxqdly + 2 + 1 谢谢@Thanks!
cnshr00t + 1 + 1 我很赞同!
Liu0827 + 1 + 1 热心回复!
gaosld + 1 + 1 用心讨论,共获提升!
jnez112358 + 1 + 1 谢谢@Thanks!
xdnice + 1 + 1 用心讨论,共获提升!
monsterbaby521 + 1 热心回复!

查看全部评分

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

h080294 发表于 2019-10-24 19:24
注册的算法就是一个RC5,不过代码中只有加密的部分,需要自己补一个解密。

细节的部分就是你说的那个常量值,他在正向验证计算的时候右移了32位,类似这样。

var11 ^= (int)(var7 >> 32)



因此反推的时候,不能得到全部的信息。但是通过一些现成的key可以发现,那32位long值是固定的。另外那个30070460L的值同样,也可以得到。
 楼主| scz 发表于 2019-10-25 11:33
本帖最后由 scz 于 2019-10-25 17:54 编辑
h080294 发表于 2019-10-24 19:24
注册的算法就是一个RC5,不过代码中只有加密的部分,需要自己补一个解密。

细节的部分就是你说的那个常 ...

有现成的key,动态调试中是可以看到1418211210这些常量。从4.5.1的代码来看,纯静态反推1418211210是不可能的。
另一个常量0x1CAD6BC,我之前看错了YQUd.class,在某处少看了一组逻辑右移(>>>),犯了低级错误,在注释中错写成校验序列号时0x1CAD6BC未被检查。
Monitor 发表于 2019-10-24 17:52
yc19951005 发表于 2019-10-24 17:56
没看懂说的是啥   
lianxing11 发表于 2019-10-24 18:06
没看懂是什么东西- -
miaowing 发表于 2019-10-24 19:38
厉害了,大佬
头像被屏蔽
纳米科技 发表于 2019-10-24 22:19
支持  谢谢
孤独的老大哥 发表于 2019-10-24 22:45
谢谢分享
头像被屏蔽
yike911 发表于 2019-10-25 00:17
牛逼的,谢谢
tmsq 发表于 2019-10-25 02:03
目前看不懂,收藏
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-12-22 16:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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