【放置江湖】弱联网手游,网络协议分析修改。每天签到可获得35元宝
接下来的工作我们就需要对lua脚本文件进行解密,lua手游脚本解密的方法有很多,网上也有非常多的教程。我们重点分析:YXHelper:doMd5ForHttpRequest 在lua解密的文件夹里面是搜不到这个函数的实现,这个方法在 native 里面实现;time: 1516158302.0023 localTime = localTime + 0.0001 一个基于时间和次数的设置。解密lua
我们随便打开一个lua脚本文件,发现游戏脚本已经被加密了。

接下来的工作我们就需要对lua脚本文件进行解密,lua手游脚本解密的方法有很多,网上也有非常多的教程。我在之前也发过一篇,关于本游戏的的解密教程,大家可以做参考。
解密lua脚本后,让游裸奔。我们通过对http协议头的一些参数进行搜索,定位到 HttpManager.lua 文件。
设置协议头,实现代码。和我们抓包的结果一致。
local localTime = 0
-- 初始化时间戳
--[[
1 同一帧内不能出现两次相同的时间戳
2 使用最新的时间戳
]]
function HttpManager:setWebTime(time)
if PRINT_MODE == 1 then
print("time = "..time)
end
localTime = time
end
function HttpManager:updateWebTime()
localTime = localTime + 0.0001
end
-- @author XiaoZhiWei
-- @time 2016/11/14 11:49:21
-- @desc 设置头信息
local function setHeader(xhr, headers)
local time = localTime
-- 得到nouce and sig
local nouce, sig = HttpManager:getNouceAndSig(time)
-- 默认头
local sendHeaders =
{
uuid = Game:getIdfv(),
userid = User:getUserId(),
sig = sig,
time = time,
nouce = nouce,
device = Game:getDevInfo(),
ver = Game:getVersion(),
hotver = Game:getHotVersion(),
platform = Game:getPlatformId(),
timezone = Helper:mathFloor((28800 - Helper:getTimeZone()) / 3600),
channel = Game:getChannelId(),
package = Game:getPackageId(),
}
if Game:getPlatformId() == "android" then
sendHeaders.pkname = SdkMethod:getPackageName()
sendHeaders.pksig = SdkMethod:getSignature()
end
-- 覆盖头
for k, v in pairs(Helper:getDef(headers, {})) do
sendHeaders[k] = v
end
-- 设置头
for k, v in pairs(sendHeaders) do
if PRINT_MODE == 1 then
print("sendHeaders["..k.."]= "..tostring(v))
end
xhr:setRequestHeader(k, v)
end
end
-- @author XiaoZhiWei
-- @time 2016/11/14 11:10:58
-- @desc 获取随机nouce 以及加密 sig
function HttpManager:getNouceAndSig(time)
local nouce = ""
for i = 1, 6 do
local index = math.random(65, 122)
while (index > 90 and index < 97) or index == 101 do
index = math.random(65, 122)
end
nouce = nouce..string.char(index)
end
local sig = YXHelper:doMd5ForHttpRequest(nouce, time)
if PRINT_MODE == 1 then
print("sig = "..tostring(sig))
print("nouce = "..tostring(nouce))
end
return nouce, sig
end
分析
-
time: 1516158302.0023 localTime = localTime + 0.0001 一个基于时间和次数的设置。
-
sig: 27347124073b9e2933fb1f7dd971f200 sig = YXHelper:doMd5ForHttpRequest(nouce, time) 一个基于时间和随机文本的请求签名。
我们重点分析:YXHelper:doMd5ForHttpRequest 在lua解密的文件夹里面是搜不到这个函数的实现,这个方法在 native 里面实现;
我们用 IDA 打开 libcocos2dlua.so 搜索 doMd5ForHttpRequest

code:
int __fastcall YXHelper::doMd5ForHttpRequest(int a1, int a2, int a3)
{
int v3; // r9
int v4; // r6
int v5; // r8
unsigned int i; // r7
const void *v7; // r10
size_t v8; // r8
void *v9; // r9
int v10; // r7
char v12; // [sp+Ch] [bp-FCh]
const void *v13; // [sp+10h] [bp-F8h]
int v14; // [sp+14h] [bp-F4h]
int v15; // [sp+18h] [bp-F0h]
int v16; // [sp+1Ch] [bp-ECh]
char v17; // [sp+20h] [bp-E8h]
char v18; // [sp+B8h] [bp-50h]
char v19[20]; // [sp+C8h] [bp-40h]
v3 = a2;
v4 = a1;
v5 = a3;
sub_E67C0C(&v13, "ed24bd36b1a87f60bebaca65538a590c", &v14);
v14 = 0;
v15 = 0;
v16 = 0;
std::vector<std::string,std::allocator<std::string>>::push_back(&v14, v3);
std::vector<std::string,std::allocator<std::string>>::push_back(&v14, v5);
std::vector<std::string,std::allocator<std::string>>::push_back(&v14, &v13);
YXHelper::sortStringList(&v14);
sub_E66AE0(&v13, &unk_F20BD8);
for ( i = 0; i < (v15 - v14) >> 2; ++i )
sub_E65B98(&v13, v14 + 4 * i);
MD5_Init(&v17);
v7 = v13;
v8 = *((_DWORD *)v13 - 3);
v9 = malloc(*((_DWORD *)v13 - 3));
memcpy(v9, v7, v8);
MD5_Update(&v17, v9, v8);
memset(v19, 0, 0x11u);
MD5_Final(v19, &v17);
sub_E67C0C(v4, &unk_F20BD8, &v12);
v10 = 0;
do
{
sprintf(&v18, "%02x", (unsigned __int8)v19[v10]);
sub_E65AA0(v4, &v18);
++v10;
}
while ( v10 != 16 );
free(v9);
std::vector<std::string,std::allocator<std::string>>::~vector(&v14);
sub_E65714(&v13);
return v4;
}
翻译成C#代码
private string GetNouceAndSig(string nouce, string time)
{
List<string> list = new List<string>();
list.Add("ed24bd36b1a87f60bebaca65538a590c");
list.Add(nouce);
list.Add(time);
list.Sort();
string s = string.Join(string.Empty, list);
using ( var md5 = MD5.Create())
{
byte[] md = md5.ComputeHash(Encoding.UTF8.GetBytes(s));
return BitConverter.ToString(md).Replace("-", String.Empty).ToLower();
}
}
GetNouceAndSig("1516158302.0023", "pDKFkg")
> 27347124073b9e2933fb1f7dd971f200
OK,验证通过。
接下来 我们对 POST 提交数据 和 resp 返回数据进行分析。
-- @author TangJian
-- @desc
function HttpManager:post(url, sendData, headers, callback, isNeedWait, isNeedEncrypt)
self:updateWebTime() -- 刷新时间
isNeedWait = Helper:getDef(isNeedWait, false) -- 等待默认为false
-- 检查url是否齐全
if string.find(url, DOMAIN) == nil and string.find(url, "api/service_ios/") == nil then
url = DOMAIN..url
end
-- 需要等待的处理
local waitingLayer = nil
if isNeedWait then
waitingLayer = WaitingLayer:createInRunningScene()
end
-- 请求处理开始
if PRINT_MODE == 1 then
print("toUrl = "..tostring(toUrl))
end
-- 创建请求
local xhr = cc.XMLHttpRequest:new()
xhr.responseType = cc.XMLHTTPREQUEST_RESPONSE_STRING
xhr:open("post", url, true)
xhr:registerScriptHandler(
function()
-- 移除waitingLayer
if isNeedWait and waitingLayer then
waitingLayer:hideAndRemoveSelf()
end
-- 校验response
local response, text
if string.find(url, "upload_error_msg") ~= nil or string.find(url, "upload_user_file_5/fankui") ~= nil then
response, text = xhr.response, xhr.status
else
response, text = self:getDecyptResponseWithCheckCheat(url, xhr.response, xhr.status)
end
-- 校验response
-- local response, text = self:getDecyptResponseWithCheckCheat(url, xhr.response, xhr.status)
-- response 无法获取,直接弹出错误信息文本
-- if response == nil then
-- PopText(text)
-- else
-- end
response = Helper:getDef(response, "")
if PRINT_MODE == 1 then
print("response:"..tostring(xhr.response))
print("Decyptresponse:"..tostring(response))
print("status:"..tostring(xhr.status))
print( "url = "..url )
end
if callback then
callback(response, xhr.status)
end
end)
-- 设置超时
xhr.timeout = 999
-- 设置头
setHeader(xhr, headers)
-- 发送请求
local tmpSendData = sendData
if type(tmpSendData) == "table" then
tmpSendData = json.encode(tmpSendData)
else
tmpSendData = tostring(tmpSendData)
end
if PRINT_MODE == 1 then
print("加密前 = "..tmpSendData)
end
--加密处理
tmpSendData = self:encryptPostData(tmpSendData ,isNeedEncrypt)
if PRINT_MODE == 1 then
## 小结
有了这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“**打铁还需自身硬**”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的**Android学习PDF+架构视频+面试文档+源码笔记**,还有**高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料**这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“**打铁还需自身硬**”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的**Android学习PDF+架构视频+面试文档+源码笔记**,还有**高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料**这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
更多推荐



所有评论(0)