好友
阅读权限 30
听众
最后登录 1970-1-1
楼主
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,不是我写的
免费评分
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。