专栏名称: 吾爱破解论坛
吾爱破解论坛致力于软件安全与病毒分析的前沿,丰富的技术版块交相辉映,由无数热衷于软件加密解密及反病毒爱好者共同维护,留给世界一抹值得百年回眸的惊艳,沉淀百年来计算机应用之精华与优雅,任岁月流转,低调而奢华的技术交流与探索却
目录
相关文章推荐
软件小妹  ·  经典收藏版,一定不要错过! ·  3 天前  
锌财经  ·  还清8.24亿巨债,罗永浩还要再还6个亿 ·  4 天前  
锌财经  ·  还清8.24亿巨债,罗永浩还要再还6个亿 ·  4 天前  
科技日报  ·  整治!中央网信办出手 ·  4 天前  
科技日报  ·  整治!中央网信办出手 ·  4 天前  
36氪出海  ·  出海周刊114期 | ... ·  1 周前  
36氪出海  ·  出海周刊114期 | ... ·  1 周前  
51好读  ›  专栏  ›  吾爱破解论坛

【Android 原创】用PHP帮你刷关---记一次分析游戏并写出挂机软件的过程

吾爱破解论坛  · 公众号  · 互联网安全  · 2017-02-12 13:48

正文

前两天下了个游戏,名字叫做“咚嗒嗒部落”。
感觉还挺好玩,然后就忍不住尝试去破解一下。


解压游戏的安装包,用Android killer分析了一下,发现这是个Unity写的游戏。


Unity的游戏肯定要找找Assmbly-Csharp.dll啊,于是把它丢进refelector。
 
开发者不用心啊,代码都不混淆,于是试着找点有用的东西。
这期间试着把关卡冷却时间调成0,本地上成功了,但是服务器端还有另一层冷却验证,冷却时间只能不了了之。
又更改了英雄的攻击,结果一进入战斗页面就闪退= =
可能是我改得不好,反正通过改dll我得不到什么好玩的东西。

想了想,干脆做个挂机的软件吧。
于是用fiddler抓了包,进行了相关的分析。
抓包的过程不详细写了,就把分析的结果贴上来吧。
这个游戏在登录时会向121.199.54.144:101发包,结构如下:


121.199.54.144:101        /default.ashx?

MsgId=2

Sid=

Uid=0

St=

actionId=1004

Handler=Login

MobileType=1

PassportId=puluter

Password=密码屏蔽下qwq

DeviceID=android-0fa7fe268b41e6082c4489537992e698

RetailID=0000

RetailUser=

sign=251b5ed2598d668a876ee812c83c0935


然后服务器会返回一个token和一个UserID,结构如下。


{

"StateCode":0,

"StateDescription":"",

"Vesion":"1.0",

"Handler":"Login",

"Data":        

{

"Token":"04cfe4f03****7b4a42f38475cda862f",

"PassportId":"puluter",

"UserId":114364,

"UserType":0,

"IsGuest":false

}

}


这个部分主要是获取UserID。
然后会向主服务器(121.199.54.144:81)索取其他服务器的名称和IP地址。


121.199.54.144:81/Service.aspx?d=MsgId%3d4%26Sid%3d%26Uid%3d114364%26St%3d%26actionId%3d1001%26GameID%3d1%26Pid%3dpuluter%26sign%3d80f97b67d3a4c0d15456baf3f03092e3


分析↑这个包,我们会发现,其实这个包信息部分是“d=”后面的那一块,一眼看出这是Urlencode,我们解码来看一下。
解码后的结构如下:



MsgId=4

&Sid=

&Uid=114364

&St=

&actionId=1001

&GameID=1

&Pid=puluter

&sign=80f97b67d3a4c0d15456baf3f03092e3


MsgId我在一开始还以为要严格保证递增= =实际上MsgId是多少无所谓。
Uid是上面获取到的UserId。
然后比较麻烦的就是这个sign了。
可以发现这个sign是一个MD5,经过一些尝试发现这个MD5和前面的请求有关,那它到底是怎么加密的呢?
这个时候我突然想起来还有个dll没有分析呢,发包加密方式肯定在那个dll里。
先搜索一下MD5,然后再analyze一下这个函数被怎么调用,发现如下代码:
 
关键部分是:

s_strPostData = s_strUserData + "&sign=" + getMd5String(s_strUserData + s_md5Key);


sign的值是md5(前面的那些请求+s_md5Key).
这个s_md5key是什么东西?
再搜一搜,最后在netwriter类里找到了它。
 
所以我们现在得出,sign=md5(request+"123");
有了sign,接下来的事情就很简单了。

在得出sign之后,我们还需要Sid这个东西。它来自于与服务器的第一次交互:

/default.ashx?d=MsgId%3d27%26Sid%3d%26Uid%3d114364%26St%3d%26actionId%3d1007%26MobileType%3d1%26Token%3d461bd5efee****52914e5b73ebcfd504%26DeviceID%3dandroid-bd19f5ede3358bdff8ff0eadc425d524%26RetailID%3d0000%26sign%3d069d0b7c6af822fd903f5773713d867c

MsgId=27

&Sid=

&Uid=114364

&St=

&actionId=1007

&MobileType=1

&Token=461ed5ef*****552914p5b73ebcfd504

&DeviceID=android-bd19f5ede3358bdff8ff0eadc425d524

&RetailID=0000

&sign=1381204e9b30736fc3c45fcb7717a0dd


这个包的返回response里会含有Sid,我通过正则表达式获取它。

然后进入这篇文章的正题:刷关。
分析包得出,某个小关卡胜利前后,游戏会发送两个极为重要的包,分别是:

MsgId=36&Sid={$sid}&Uid=114364&St=&actionId=1068&BigLevel={$big}&SmallLevel={$small}&Difficulty={$dif}


MsgId=57&Sid={$sid}&Uid=114364&St=&actionId=1069


之前反编译的时候,其实还反编译出了一份action表:


public const int AddCharacteristic = 0x44b;
public const int AddFish = 0x7d3;
public const int AddFriend = 0x3fe;
public const int AddGoldAndDiamond = 0x7d1;
public const int AddHero = 0x3f1;
public const int AddItem = 0x7d5;
public const int AddOrChangeHeroTotem = 0x3f4;
public const int AddOrChnageOrClearGod = 0x3f8;
public const int AddResources = 0x7d4;
public const int AddTotem = 0x7d2;
public const int BindPhone = 0x271d;
public const int BindVerificationCode = 0x271b;
public const int BuyComfirmedShop = 0x40c;
public const int BuyDiamond = 0x40b;
public const int BuySundryItem = 0x40e;
public const int BuyVip = 0x4b0;
public const int ChangeHeroFightQueue = 0x3f0;
public const int ChangeHeroName = 0x3f5;
public const int ChangePwd = 0x3ee;
public const int ChangePwdByCode = 0x271c;
public const int ChangePWDVerificationCode = 0x271f;
public const int ChangeRoleHeadImg = 0x3f9;
public const int CheckBaiDuPayment = 0x459;
public const int CheckPhoneBind = 0x271e;
public const int CheckUCOrder = 0x45a;
public const int CheckXiaoMiPayment = 0x452;
public const int ClickFriendSeed = 0x441;
public const int ComposeTotem = 0x3fc;
public const int CreateRote = 0x3ed;
public const int DeleteFriend = 0x400;
public const int DeleteMail = 0x41c;
public const int Diamond10Fishing = 0x418;
public const int DiamondFishing = 0x417;
public const int DiamondRefreashPVPTargets = 0x430;
public const int DirServer = 0x3e9;
public const int DismisseHero = 0x427;
public const int EatFish = 0x420;
public const int FreashSundryShop = 0x43c;
public const int FreeRefreashPVPTargets = 0x42f;
public const int GetAiBeIOrder = 0x456;
public const int GetAllInfos = 0x468;
public const int GetAnnouncements = 0x457;
public const int GetBossDoubleReward = 0x453;
public const int GetDailyTaskInfo = 0x437;
public const int GetDiamondShop = 0x40a;
public const int GetEventSysInfoDta = 0x432;
public const int GetFaith = 0x43f;
public const int GetFightEventSysInfoDta = 0x435;
public const int GetFishBag = 0x410;
public const int GetFishingInfoData = 0x416;
public const int GetForeverTaskInfo = 0x439;
public const int GetFriendRequests = 0x445;
public const int GetFriends = 0x3ff;
public const int GetGameInfo = 0x271a;
public const int GetGodChangeFaithData = 0x41a;
public const int GetHeapReward = 0x461;
public const int GetItemBag = 0x411;
public const int GetLands = 0x405;
public const int GetLoginIn7DaysInfo = 0x45b;
public const int GetMailBag = 0x41b;
public const int GetNotice = 0x450;
public const int GetOffLineReward = 0x428;
public const int GetPVPInfoData = 0x424;
public const int GetPVPTargets = 0x423;
public const int GetRecommendFriends = 0x442;
public const int GetResourcesBag = 0x40f;
public const int GetRewardInfos = 0x460;
public const int GetRiverGodTask = 0x412;
public const int GetRiverGodTaskExtReward = 0x414;
public const int GetRiverGodTaskReward = 0x413;
public const int GetRoleLandsInfos = 0x434;
public const int GetRolePVPQueue = 0x431;
public const int GetSignInfo = 0x409;
public const int GetSingleRechargeReward = 0x462;
public const int GetStrikeInfo = 0x42b;
public const int GetSundryShop = 0x40d;
public const int GetTotemDatas = 0x3fa;
public const int GetVIPInfo = 0x45d;
public const int GetVivoPayInfo = 0x455;
public const int GetWinpointRanking = 0x421;
public const int GodAddFaith = 0x27a7;
public const int GoldChangeFaith = 0x419;
public const int GoldFishing = 0x415;
public const int Harvest = 0x404;
public const int LifeCardUserDailyReward = 0x45f;
public const int LoginAccountServer = 0x3ec;
public const int LoginGameServer = 0x3ef;
public const int MiLoginServer = 0x2720;
public const int MonthCardUserDailyReward = 0x45e;
public const int OpenLand = 0x402;
public const int Passport = 0x3ea;
public const int PVPReward = 0x422;
public const int QQOrder = 0x458;
public const int ReceiveFriendRequest = 0x443;
public const int ReceiveMailReward = 0x41d;
public const int RefuseFriendRequest = 0x444;
public const int Regist = 0x3eb;
public const int RequestActiveCode = 0x463;
public const int RequestADDiamond = 0x465;
public const int RequestADGetFish = 0x467;
public const int RequestAdReward = 0x464;
public const int RequestADSeedOK = 0x466;
public const int RequestBossFight = 0x42c;
public const int RequestBossFightReward = 0x42d;
public const int RequestFight = 0x41e;
public const int RequestFightReward = 0x41f;
public const int RequestPVPFight = 0x425;
public const int ResourcesExchangeFaith = 0x429;
public const int RewardDailyTask = 0x438;
public const int RewardEventSysInfo = 0x433;
public const int RewardFightEventSysInfo = 0x436;
public const int RewardForeverlTask = 0x43b;
public const int RewardLevelTask = 0x43a;
public const int RewardLoginIn7Days = 0x45c;
public const int SaleResource = 0x3f6;
public const int SaleTotem = 0x3fb;
public const int SearchFriend = 0x3fd;
public const int SearchFriendPVE = 0x401;
public const int Seeding = 0x403;
public const int SeedSpeedUp = 0x440;
public const int SellFish = 0x451;
public const int SendTutoralOk = 0x42e;
public const int SignIn = 0x408;
public const int StrikeReward = 0x42a;
public const int SubmitPics = 0x454;
public const int TransferHero = 0x3f3;
public const int TribeLevelUpdate = 0x407;
public const int TribeUpdate = 0x406;
public const int UpgradeGod = 0x3f7;
public const int UpgradeHero = 0x3f2;
public const int UpgradeheroSkill = 0x426;
public const int UseChangeCharacteristic = 0x44a;
public const int UseDoubleRewardItem = 0x449;
public const int UseExpItem = 0x446;
public const int UseFishEatCount = 0x44d;
public const int UseFreashCD = 0x44c;
public const int UseGoldItem = 0x447;
public const int UseHeroTransform = 0x44e;
public const int UseSkillPointItem = 0x448;
public const int VerifyAppStoreOrderInfo = 0x44f;


进行对照得出,第一个包是告诉服务器:‘我要开始打这一关了!’,第二个包是“这一关我打完了,快告诉我有什么奖励!”

结合这两个包就能达到成功刷关的目的了。

值得注意的是,刷关最好是对零cd的关卡使用。

最后附上刷关部分的代码:
密码各位自己抓包找吧,找完对这段代码进行简单的修改就能用了~


/**

* Created by PhpStorm.

* User: puluter

* Date: 2017/2/9

* Time: 8:34

*/

 

require_once ("./include.php");

//phpinfo();

$cookie = dirname(__FILE__) . '/cookie.txt';

$a=login("121.199.54.144:101/default.ashx?MsgId=1&Sid=&Uid=114364&St=&actionId=1004&Handler=Login&MobileType=1&PassportId=puluter&Password=密码不能看qwq&DeviceID=android-bd19f5ede3358bdff8ff0eadc425d524&RetailID=0000&RetailUser=&sign=e6e2c90fdaf3f59d22bf87586c9f983d",$cookie);

var_dump($a);

ret();

$b=login("121.199.54.144:81/Service.aspx?d=MsgId%3d4%26Sid%3d%26Uid%3d114364%26St%3d%26actionId%3d1001%26GameID%3d1%26Pid%3dpuluter%26sign%3d80f97b67d3a4c0d15456baf3f03092e3",$cookie);

var_dump($b);

$ip="120.27.196.105:104";

ret();

$c=get($ip."/default.ashx?d=MsgId%3d27%26Sid%3d%26Uid%3d114364%26St%3d%26actionId%3d1007%26MobileType%3d1%26Token%3d461bd5efeee*****14e5b73ebcfd504%26DeviceID%3dandroid-bd19f5ede3358bdff8ff0eadc425d524%26RetailID%3d0000%26sign%3d069d0b7c6af822fd903f5773713d867c",$cookie);

ret();

$f=preg_match("/s_(.*?)\|1\|3/",$c,$g);

$g=$g[0];

ret();

var_dump($g);

$sid=$g;

$big=6;$small=1;$dif=1;$cnt=0;

while(true){$cnt++;

$packet="MsgId=36&Sid={$sid}&Uid=114364&St=&actionId=1068&BigLevel={$big}&SmallLevel={$small}&Difficulty={$dif}";

$md55=md5($packet."123");

$packet=$packet."&sign=".$md55;

$packet=urlencode($packet);

$app=get($ip."/default.ashx?d=".$packet,$cookie);

//var_dump($app);

//ret();

$ans=preg_match("/请求/",$app);

// if($ans!=0){

// $small=1;$big++;

// continue;

// }

$packet="MsgId=57&Sid={$sid}&Uid=114364&St=&actionId=1069";

$md55=md5($packet."123");

$packet=$packet."&sign=".$md55;

$packet=urlencode($packet);

$app=get($ip."/default.ashx?d=".$packet,$cookie);

var_dump($app);

//ret();

$ans=preg_match("/请求/",$app);

// if($ans==0){

// $ans=preg_match("/需要/",$app);

//// if($ans==0){

// $small++;

//// }

// }

// else {

// $small++;

// }

var_dump($big);var_dump($small);var_dump($cnt);//ret();

sleep(0.5);

}


/**

* Created by PhpStorm.

* User: yuzhu

* Date: 2017/2/9

* Time: 8:35

*/

function ret(){echo "
";}

 

function login($url,$cookie){

$ch=curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_ENCODING, "gzip");

curl_setopt($ch, CURLOPT_HEADER, "User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; MI 5 Build/NMF26V)");

curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie); //设置Cookie信息保存在指定的文件中

$output = curl_exec($ch);

curl_close($ch);

return $output;

 

}

 

function get($url,$cookie=""){

$ch=curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_ENCODING, "gzip");

curl_setopt($ch, CURLOPT_HEADER, "User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; MI 5 Build/NMF26V)");

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_HEADER, 0);

curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie); //读取cookie

$output = curl_exec($ch);

curl_close($ch);

return $output;

}

 

function post($url,$params,$cookie=""){

//$post_data = array ("username" => "bob","key" => "12345");

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_HEADER, "User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; MI 5 Build/NMF26V)");

curl_setopt($ch, CURLOPT_POST, 1);

curl_setopt($ch, CURLOPT_POSTFIELDS, $params);

curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie); //读取cookie

$output = curl_exec($ch);

curl_close($ch);

return $output;

}


对了,多说一句,这个php最好在cmd中运行,如果想在浏览器里用,那就得改不少代码了。

(其实这个游戏还有其他的一些小的逻辑bug,比如刷供奉值,今天先不写了)


--官方论坛

www.52pojie.cn

--推荐给朋友

公众微信号:吾爱破解论坛

或搜微信号:pojie_52