小白一个,以火柴人联盟破解为例理顺下自己的思路,如果疏漏,请大牛斧正……
1、明确目标
拿到一个apk后,不要着急反编译,先在手机上试玩一下,看哪里是需要内购的以及内购的方式,手机短信验证、支付宝、微信支付等等,心中大概有个谱。以在*豆荚上下载的火柴人联盟为例,查看付费方式(苦逼的很,手机不能截屏,只能用其他手机拍照了。)如下图所示
可以看到支持多种付费方式,有话费支付和三方支付(支付宝、微信、银联,手机充值卡)。
2、验证软件是否有签名并进行破解
把apk扔到android killer中,反编译,不做任何操作,直接编译打包,安装到手机。此时运行程序,有如下提示
可以看到,二次打包后不能运行,说明它有进行签名验证。
在android killer中搜索关键字signatures,结果如下
wandoujia,wow,weibo是三方的sdk,不关心,直接看上述两个文件,发现关键字都是存在一个叫做getAPPSecretString的函数中,直接上java代码,逻辑是获取软件的签名后进行散列运算,然后返回一个字符串,我们此时几乎可以肯定这个东西是用于签名验证的。
我们大概知道了获取签名的位置是其中一个,由于软件一打开就提示是盗版,所以接下来看下主流程。入口是com.DBGame.DiabloLOL.SplashActivity;查看它的oncreate函数,发现它是调用了DiabloLOL的activity,我们主要看DiabloLOL.smali文件,oncreate函数如下
[Java] 纯文本查看 复制代码 protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
init();
Bluetooth.getInstance().init(this);
this.context = this;
BLHelper.init(this.aHandler, this);
MobClickCppHelper.init(this);
this.wandouGamesApi = CmgameApplication.getWandouGamesApi();
this.wandouGamesApi.init(this);
try
{
ReYun.initWithKeyAndChannelId(this, "a4d2f6135f92a14d5d86da48f095e823", BLHelper.getEmChannel());
[color=#ff0000] new AddBlackName(this);[/color]
[color=#ff0000] AddBlackName.Check();[/color]
instance = this;
paramBundle = ((TelephonyManager)getSystemService("phone")).getSimOperator();
if (((paramBundle != null) && (paramBundle.trim().equals("46001"))) || (paramBundle.trim().equals("50501")))
{
i = 2;
this.iFromPay = i;
if (i != 2) {
EMPayManager.initMerge(this, 1, 2);
}
MatchVSHelper.init(this.aHandler, this);
return;
}
}
catch (Exception paramBundle)
{
for (;;)
{
int i;
paramBundle.printStackTrace();
continue;
if ((paramBundle != null) && (paramBundle.trim().equals("46003"))) {
i = 1;
} else {
i = 0;
}
}
}
}
看到一个AddBlackName.Check(); 而且addblackName正好是我们上面找到那个两个包括getAPPSecretString类中的一个,感觉棒棒哒,赶紧跟进去,代码很简单,只上关键代码
[Java] 纯文本查看 复制代码 str3 = ((WifiManager)AddBlackName.mContext.getSystemService("wifi")).getConnectionInfo().getMacAddress();
str2 = String.format("X-Em-Apk-Certifiction%s", new Object[] { utils.getAPPSecretString(AddBlackName.mContext) });
AddBlackName.loadData(String.format("http://119.29.20.199/stickman/api/v1/sign/confirmSign?did=%s&appkey=%s&ver=%s&ch=%s&lang=%s&pkgname=%s&mac=%s&sdkver=%s&sign=%s", new Object[] { str1, AddBlackName.sKey, Integer.valueOf(i), AddBlackName.sChannel, AddBlackName.sLanguage, AddBlackName.mContext.getPackageName(), str3, "1.0.0", str2 }));
获取软件签名后调用loadData函数,传进去的还有个网址,大概可以判断是进行网络验证,跟进去看看,流程也很命令,不再贴代码,大概就是构造一个http包,发到服务器,经过服务器验证,返回状态。当返回状态为1103时,认为是校验失败
[Java] 纯文本查看 复制代码 if (updateItem.getStatus() == 1103)
{
updateItem.setTitle(paramString.getString("authTitle"));
updateItem.setDescription(paramString.getString("authDesc"));
}
PreferUtil.saveIntValue(mContext, "BLACK_ITATUS", updateItem.getStatus());
PreferUtil.saveStrValue(mContext, "BLACK_STITLE", updateItem.getTitle());
PreferUtil.saveStrValue(mContext, "BLACK_SMESSAGE", updateItem.getDescription());
PreferUtil.saveStrValue(mContext, "BLACK_SURL", updateItem.getLink());
if (updateItem.getStatus() == 1103) {
mHandler.sendEmptyMessage(1000);
OK,知道大概流程就好办了只需要搜索1103关键字,看哪里进行修改了,修改下if跳转即可。修改方法不再赘述,感兴趣的同学可下载样本比对。修改完成后可正常运行。
3、内购破解
3.1 话费支付破解
软件签名破解完成后,接下来进行内购破解,在不少文章中可到内购破解方式是搜索payCancel、payFailed、onBilling等关键字。试着搜索了,这些关键字都有,但是修改后并没有起作用。后来想了下,这些关键字都是三方sdk中计费成功和失败的标记,而在这个软件中,包含的
三方计费sdk太多了,有移动、联通、三方支付,如下所示
大概看了下其中的逻辑,在common中的EMPayManager类initMerge方法中进行运营商判断,然后根据运营商不同实例化下面的实例,
[Java] 纯文本查看 复制代码 public static void initMerge(Context paramContext, int paramInt1, int paramInt2)
{
String str = ((TelephonyManager)paramContext.getSystemService("phone")).getSimOperator();
if (((str != null) && (str.trim().equals("46001"))) || (str.trim().equals("50501"))) {
EMUnicomManager.init(paramContext);
}
for (;;)
{
return;
if ((str != null) && (str.trim().equals("46003"))) {
EMEGamePayManager410.init(paramContext);
} else if (paramInt2 == 0) {
EMMMBillingManager.getInstance().init(paramContext);
} else if (paramInt2 == 1) {
EMMMIntenetManager.getInstance(paramContext).init(paramContext, new OnLoginFinishListener()
{
public void onLoginFinish(int paramAnonymousInt) {}
});
} else {
EMSohuPayManager.init((Activity)paramContext);
}
}
}
如果修改每个计费sdk的接口会比较麻烦,我们回到主逻辑DiaboLOL,在文件开头定义了一个handler,主要代码有
[Java] 纯文本查看 复制代码 EMPayManager.payMerge(DiabloLOL.this, DiabloLOL.this.PRO_ID[DiabloLOL.this.mPayIndex], DiabloLOL.this.PAY_RMB[DiabloLOL.this.mPayIndex], DiabloLOL.this.PAY_CODE_UNICOM[DiabloLOL.this.mPayIndex], DiabloLOL.this.PAY_CODE_SMS[DiabloLOL.this.mPayIndex], DiabloLOL.this.PAY_NAME[DiabloLOL.this.mPayIndex], DiabloLOL.this.PAY_CODE_MM[DiabloLOL.this.mPayIndex], 2, 0, null, false, "shit", new OnPayFinishListener()
{
public void onPayFinish(int paramAnonymous2Int)
{
if (paramAnonymous2Int == 1)
{
Log.e("test", "rechager sucess");
DiabloLOL.this.setPayment();
BLHelper.purchaseComplete(DiabloLOL.this.PRO_ID_Str[DiabloLOL.this.mPayIndex], 1);
BLHelper.closeShieldLayer();
}
for (;;)
{
return;
Log.e("test", "rechager failure");
BLHelper.closeShieldLayer();
System.out.println("������������");
}
}
调用 EMPayManager的payMerge函数,新建监听器作为参数,跟进去payMerge,是根据运营商不同,调用不同计费sdk的pay接口,并把监听器传进去。如果跟进每个计费接口,会发现每个接口最终都会调用监听器的onPayFinish接口,那么
我们只需要修改onPayFinish接口,使参数恒为1即可。
3.2 三方支付破解
上一步骤搞定了话费内购,接下来在主逻辑中发现一个可疑方法 PayThird ,实现了onfailed和onSuccess方法,把onfailed中的内容全部删除,然后把onSuccess中的内容拷贝进去。测试,发现三方支付的内购也成功破解了,如下
[Java] 纯文本查看 复制代码 public void PayThird(String paramString)
{
Log.e("lihytest", "orderNum " + paramString);
long l = this.PAY_RMB[this.mPayIndex];
this.wandouGamesApi.pay(this, this.PAY_NAME[this.mPayIndex], l, paramString, new OnPayFinishedListener()
{
public void onPayFail(PayResult paramAnonymousPayResult)
{
Log.e("asd", "onPayFail");
BLHelper.closeShieldLayer();
}
public void onPaySuccess(PayResult paramAnonymousPayResult)
{
Log.e("asd", "onPaySuccess");
ReYun.setPayment("unkown", "wdj151", "RMB", DiabloLOL.this.PAY_RMB[DiabloLOL.this.mPayIndex] / 100.0F, 0.0F, DiabloLOL.this.PAY_NAME[DiabloLOL.this.mPayIndex], 0L, 0);
BLHelper.purchaseComplete(DiabloLOL.this.PRO_ID_Str[DiabloLOL.this.mPayIndex], 2);
BLHelper.closeShieldLayer();
}
});
}
打开smali代码,把onPaySucess中的部分拷贝到onPayFailed中即可。
其实。。。这个破解的有点侥幸,具体逻辑现在也没有搞明白,所以有时候运气来了挡都挡不住
看下成果吧
4 总结
1、对于签名验证,有本地验证和网络验证,可以根据不联网时能不能正常运行来判断
2、对于单一模式的付费方式,最好看一下是使用的是那种计费sdk,然后取开发者网站看下该sdk的重要接口
3、对于多种模式的付费方式,查找调用计费接口的统一入口。
PS:仅供技术交流,请勿传播破解软件
|