提前说明:
这个做法我个人认为不算私服,它更倾向于是绕过验证,因为游戏联机本身是不靠和服务器的通信的(心跳检测不算)
新人第一次发帖,难免错漏,如有错误,麻烦管理了,这里先立好准备挨打!
Evolve(国内叫进化,不是那个方舟生存进化),是之前一个4V1不对称对抗的游戏,相信不少人看到过游戏风云的视频,或者看到过B站里的介绍。
由于2K运营不善,在几次折腾之后,游戏关服,Steam下架。我从朋友口中听说了这个游戏,顺着痕迹搜了一下,搜到了一篇文章(来源3DM):
如图,就是这篇文章
于是再度研究,发现这个游戏“不使用第三方服务器”(这里埋个伏笔,2K太损了),而是调用Steam的P2P功能来联机,这一下就激发了我的兴趣:
“
取到一份游戏本体,然后使用Goldberg,Smartsteamemu等软件进行模拟,不就可以进行局域网联机了吗?
”
说干就干,在哥们(真哥们啊,国外discord的朋友)的帮助下我成功拿到一个账号得到了本体,之后开始了旷日持久的“破解战”!
阶段一:游戏和Steam的欺骗战
首先查看steam_api.dll的版本号:
版本较老!我使用了Smartsteamemu进行游戏模拟,游戏成功打开,单机游戏正常,但是当点选“MultiPlayer"时,游戏弹出提示:不能链接到服务器,请检查网络连接。
由于服务器已经关闭,我首先想到的是模拟有缺陷,故换上了Goldberg补丁(替换关键文件steam_api64.dll)结果,Goldberg双击游戏,直接链接到了Steam!
这使我心生警惕:游戏似乎并未使用steam_api64.dll进行游戏和Steam之间的通信?
在打好Goldberg补丁的前提下,我登陆哥们的账号,果然游戏顺利进入菜单,信息全部来源于正版。(后来测试发现,游戏会校验这个文件的正确与否,否则游戏无法打开,这个我们回头再说)
于是我改用Goldberg的steamclient版本,这个版本是虚拟自己为steam(个人理解),然后让游戏去链接它,于是避开了steam_api64.dll的验证,游戏成功进入 ->结果如旧,仍然是提示:不能链接到服务器,但是,Goldberg补丁使用,会无限卡住教程,看来单纯Steam部分已经被模拟的差不多了,一定有什么地方有了问题。
阶段二:网络验证的欺骗战
到这里我其实已经黔驴技穷,于是我首先考虑使用SmartSteamEmu。接下来考虑是不是真的有个服务器:使用mitmproxy,安装根证书后进行抓包,抓到了几个Post的包,返回的都是类似下面的json:
我点击多人游戏,等待到游戏结束,也没有什么类似验证的包。
于是我换上正版游戏监视,发现了一个包访问了dsx_1_b.4v1game.net/auth.php,它的请求是一个json文件,返回也是一个json文件:
(下图是返回的,由于游戏在打包,所以不好意思没办法上图,只能多口述,以后再补)
这个json文件显而易见是加密的,我多次测试发现它还会变化,所以我暂时放到了一边。
经过多次正版测试后我发现,该游戏会访问”两个“(为什么加引号呢?我们后文再说)网站,一个是上面说的dsx_1_b.4v1game.net,另一个是89oyxxxxx(具体没记),第二个多数时间是使用Post方法,收到的请求也都一致,有的时候会请求一个MicroPatch.pak,其他的请求大部分都是失败的,显然服务器关闭了他们也消失了。从这点判断,不难推出,第一个网站是验证游戏是否可以联机的,第二个网站应该之前用于传输更新文件。
但是为什么使用SmartSteamEmu的时候没有这个请求呢?
我翻看了auth.php的访问,发现它的请求部分有一串乱码(抱歉没法截图,以后有空补上),这串乱码经过比对(因为我监视了Steam和游戏,所以从Steam里发现了),是一个叫做SteamAuthTicket的东东。
这使我有所想法:
由于使用Goldberg的时候会无限卡住教程,但是相对来说,Goldberg补丁模拟较为完美,可能这个Ticket SmartSteamEmu没有模拟起来,但是Goldberg可以模拟?
但是Goldberg补丁使用,会无限卡住教程(游戏进入就会显示教程) 于是我想到一个办法:首先先用正版的云存档,然后将Goldberg的SteamID(唯一识别码)设置为正版的,之后将存档替换进去,于是这样”跳过“了教程,成功进入游戏。
我尝试点击多人游戏,等待一会儿后,游戏果然发送了请求,而且果不其然服务器返回了Error,403 Forbidden(大概就是识别不通过什么的)。
此时的我的考虑:它的发送一定会保存到内存里,所以我用CE(该游戏反调试,CE不知道为啥能用)修改那块内存改成正版的,然后返回一个正版用的回复Token,这样的话就能骗过去……
游戏一定有什么加盐啊,时间计算Token啊,多重加密啊……
事实上,我多想了
在一次误操作的时候,我将Error的请求改成了正版的请求,结果:游戏就进!到!了!联!机!界!面!
(当时我人都傻了)
我当时还考虑是不是Token没过期,于是我日期往后调了一堆,结果照旧,说明这货估计的验证就是把那个返回的解密之后,判断了个Success啥的……(这段我很侥幸,如果有大佬还请不吝指教)
于是我使用mitmproxy的脚本功能,写下了如下代码:
(文件是直接用mitmproxy下载下来的)
[Python] 纯文本查看 复制代码 multiheader = {"Content-Type": "application/json,charset=utf-8",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Connection": "keep-alive",
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))),
"Expires": "0",
"Server": "Pinenut-Crack/1.1",
"Pragma": "no-cache",
"X-Powered-By": "PHP/5.3.29"
}
def evolvecrack(jsonfile, logstr, optheader, flow):
print("[松子日志]:" + logstr)
flow.response = http.HTTPResponse.make(
200, # (optional) status code
jsonfile, # (optional) content
optheader) # (optional)
def readjson(jsonname):
with open(os.path.abspath('.') + "\\EvolveCrack\\" + jsonname, encoding="utf-8") as f:
return f.read()
def request(self, flow) -> None:
elif "auth.php" in flow.request.pretty_url:
# This is for dsx-1-b.4v1game.net.
authmulti = str(readjson("SteamLogin.json"))
evolvecrack(authmulti, "Game are Trying to Go MultiPlayer.", multiheader, flow)
elif "requestserver.php" in flow.request.pretty_url:
# This is for dsx-1-b.4v1game.net.
requestlb = str(readjson("requestserver.json"))
evolvecrack(requestlb, "Game are Trying to Go MultiPlayer.", multiheader, flow)
之后我使用透明模式全局代{过}{滤}理,果然成功进入了游戏的联机界面。
之后我找了一个朋友测试,然后……我俩就无限卡加载了……
阶段三:本地验证的Trust战
确认不是网络问题后,我直接想到了”教程打不开“这件事。自己开了一句单人,果然,也是无限加载。
于是怀疑还有特殊的验证。(这里其实是请了国外的Nemirtingas大佬研究,他是Goldberg Emulator的参与开发者之一,非常感谢他,这边为了叙述方便,我将以我的视角来说明)使用OpenArk查看Evolve.exe仔细瞧瞧……
嗯?这是?:
网上搜索了一下这个,找到了API文档和CSDN论坛的:
”如何用WinVerifyTrust验证文件的数字签名?-CSDN论坛“
于是豁然开朗。看来是它会验证什么东西的数字签名。于是我下载了Nemirtingas Steam Emu(这里提这个是因为后面会讲到一个坑,这个是Nemirtingas大佬自己做的Steam Emulator,功能基本上就是Goldberg翻版)
确认了有数字签名……进入游戏……还是不行……
找大佬要了签名的根证书……安装信任……还是不行……
奇哉怪哉,明明有证书为什么不行?莫非有其他验证?
于是我细细比对了一下两个的证书:(这里为了复现当时的场景)
发现Nemirtingas给的注册的是sha256证书。会不会是因为这个呢?
我下载了一个Vsign签名工具,它自带一个测试的代码签名,一路安装,进行双签(SHA1 + SHA256,就是上图左边不打码那样) + 安装证书到信任, 一番操作之后,果然成功,游戏可以正常联机了!
但是这样的话每个人都得安装一个根证书,这对萌新很不友好,那么应该怎么办呢?当然是:干它!
Goldberg有一个功能是禁止程序联网,于是我下载了它的代码,根据它的Hook程序联网的代码,照着抄了一个自己的,然后开始准备编译:
一番操作之后代码和上面差不多,之后根据微软原文:
Zero | The trust provider can use the interactive desktop to display its user interface. |
让他返回0:
[C++] 纯文本查看 复制代码 //Add Wintrust support:
static decltype(WinVerifyTrust)* pfnWinVerifyTrust;
LONG Mine_WinVerifyTrust(
HWND hwnd,
GUID* pgActionID,
LPVOID pWVTData
) {
PRINT_DEBUG("Mine_WinVerifyTrust Hook Successfully.\n");
return (LONG)0;
};
于是果然不再需要检查证书。大获全胜……了吗?
阶段四:2K的DLC验证战
游戏很成功的可以使用局域网联机了。但是我们都发现一个问题:新版本的游戏,它……不能解锁DLC。
一般游戏的DLC都是靠Steam的接口请求解锁的,但是Goldberg配置解锁失败。
此时我突然想起来之前看到的一些东西:
在全局的mitmproxy里,有几个提示:
xxxxx.2k.com 可能不信任你的证书(注:这里偷懒了,我没放原文,意思就是这个意思)
所以根据它的尿性,我大胆猜测:这货使用了证书固定技术,防了一手中间人攻击?!
……然后好吧,确实,因为人家把证书都放外面了……
里面全是吊销的证书。
网上搜了一下名字,感觉和这个有关:
“
/etc/pki/tls/certs/ca-bundle.crt 文件存储了各大证书颁发证的根证书交叉文件。curl 访问https网站时,会比对这个文件里的根证书。如果这个文件过老,那就是有新的根证书未加入到这个文件里。导致curl无法正常访问https网站“
也就是说里面放了一堆证书都是验证用的。这怎么办呢?
我想到了mitmproxy下载的时候有个”其他平台”可以下载一个证书,要不,换了试试?说干就干,顺手换掉,再折腾一下(挂梯子什么的),果然抓到了信息。
在一堆信息里筛选之后发现了这个东东:
(这里是改了的,之前都是一堆false)
我将返回值改成True,然后将ownersteamid改成破解的ID,果然解锁了全DLC!
(后来测试,这货好像有个steamid就能解锁……是多不走心……)
于是局域网化联机,胜利结束……了吗?
阶段五:易于使用的优化战
现在要想联机,我首先得管理员权限打开mitmproxy的透明模式,这样很不方便,有些软件是会不工作的。
所以考虑了一下,决定:搞成一键使用的!
在Goldberg里找到如下代码(橙色的框是我加的):
这样加了一段之后,就可以把请求重定向到任何我想要的IP和端口了!
但是它还是https呀,那怎么办呢?好说,摘掉它的HTTPS头:
之后跑了一下,发现:在这个模式下2K 服务器一点消息都没了……按理说你就算是不奏效,也不能一点消息都没吧?
查看了一下发现了这些代码:
这是ws2_32.dll的函数。我跟着日志搜了一下,发现2K的请求混在里面被拦截了(因为不是局域网IP会被拦截)
真是奇怪,它居然是靠ws2_32.dll来进行HTTPS工作的?(有没有大佬科普一下?我本人技术不高,这是啥操作?)
因为不懂这个,我摘不掉它的HTTPS头。但是我可以修改LAN_IP的判断,让他也重定向到我想要的IP和端口嘛!
一番操作之后……(各路大佬轻喷,我写的烂我知道,上面那段是Goldberg原文,下面有中文注释的那一行之后就是我干的了)
[C++] 纯文本查看 复制代码 // I use this check to redirect the 2K check.
static bool is_lan_ip(const sockaddr *addr, int namelen)
{
if (!namelen) return false;
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
unsigned char ip[4];
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
if (is_adapter_ip(ip)) return true;
if (ip[0] == 127) return true;
if (ip[0] == 10) return true;
if (ip[0] == 192 && ip[1] == 168) return true;
if (ip[0] == 169 && ip[1] == 254 && ip[2] != 0) return true;
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return true;
if ((ip[0] == 100) && ((ip[1] & 0xC0) == 0x40)) return true;
if (ip[0] == 239) return true; //multicast
if (ip[0] == 0) return true; //Current network
if (ip[0] == 192 && (ip[1] == 18 || ip[1] == 19)) return true; //Used for benchmark testing of inter-network communications between two separate subnets.
if (ip[0] >= 224) return true; //ip multicast (224 - 239) future use (240.0.0.0–255.255.255.254) broadcast (255.255.255.255)
} else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
unsigned char ip[16];
unsigned char zeroes[16] = {};
memcpy(ip, &addr_in6->sin6_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP6 %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu...%hhu\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[15]);
if (((ip[0] == 0xFF) && (ip[1] < 3) && (ip[15] == 1)) ||
((ip[0] == 0xFE) && ((ip[1] & 0xC0) == 0x80))) return true;
if (memcmp(zeroes, ip, sizeof(ip)) == 0) return true;
if (memcmp(zeroes, ip, sizeof(ip) - 1) == 0 && ip[15] == 1) return true;
if (ip[0] == 0xff) return true; //multicast
if (ip[0] == 0xfc) return true; //unique local
if (ip[0] == 0xfd) return true; //unique local
//TODO: ipv4 mapped?
}
//If not LAN IP, there will redirect.
//不是LAN IP的,在这里会被拦截到
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
unsigned char ip[4];
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
PRINT_DEBUG("CHECK NON LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
//check if there have serverip.txt/serverport.txt, if have,redirect to it; Or ignore it.
//修改这一部分,使它强行定位到某个IP和某个Port,否则正常访问。
if (file_exists(get_full_program_path() + "serverip.txt"))
{
std::ifstream readFile(get_full_program_path() + "serverip.txt");
//Read and put it in.
std::string temp;
readFile >> temp;
const std::string& tempip = temp;
//Put it in.
//这里得到了String的临时IP,接下来将这个IP想办法放进去
addr_in->sin_addr.s_addr = inet_addr(temp.c_str());
PRINT_DEBUG("Crack 2K DLC Check - IP\n");
}
if (file_exists(get_full_program_path() + "serverport.txt"))
{
std::ifstream readFile(get_full_program_path() + "serverport.txt");
std::string temp;
readFile >> temp;
int port = atoi(temp.c_str());
addr_in->sin_port = htons(port);
PRINT_DEBUG("Crack 2K DLC Check - PORT\n");
}
//Send it back
//修改后,将修改的转回sockaddr,通过重新赋值给该指针来修改指针指向的值。
addr = (struct sockaddr*)addr_in;
//display the new IP for debug.
//记得重新读取IP的值……
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
PRINT_DEBUG("NOW LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
return true;
}
PRINT_DEBUG("NOT LAN IP\n");
return false;
}
可能有人问了:你重定向到哪儿去呢?别急,我们知道mitmproxy有个功能叫“反向代{过}{滤}理”,设置一个反向代{过}{滤}理就好啦!
最后在整合了网上知乎陈泽安大佬的代码之后(来源:https://www.zhihu.com/question/422464846/answer/1619189974)
最后成品大概就是这样的:(各位大佬正好求教,这么一堆if elif有没有办法优化呀,好长好麻烦啊)
[Python] 纯文本查看 复制代码 # Mitmproxy读取部分:
# 作者:陈泽安
# 链接:[url=https://www.zhihu.com/question/422464846/answer/1521657468]https://www.zhihu.com/question/422464846/answer/1521657468[/url]
# 来源:知乎
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
from mitmproxy.options import Options
from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.proxy.server import ProxyServer
from mitmproxy.tools.dump import DumpMaster
from mitmproxy import http
import threading
import asyncio
import json
import time
# 判断文件是否存在
import os
# 彩蛋
import datetime
# Sorry I don't use english to make this file.
# 搬运他们到这里……
"""
HTTPS HEADER.I DON'T KNOW WHETHER IS THIS FOR USE.
"""
twokheader = {"Server": "mitmcrack",
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))),
"Content-Type": "application/json",
"Connection": "close",
"P3P": "CP=\"See [url=http://www.take2games.com/privacy]http://www.take2games.com/privacy[/url]\""
}
multiheader = {"Content-Type": "application/json,charset=utf-8",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Connection": "keep-alive",
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))),
"Expires": "0",
"Server": "Pinenut-Crack/1.1",
"Pragma": "no-cache",
"X-Powered-By": "PHP/5.3.29"
}
# For Update Server, there aren't the same, so I write it below, not define here.
def evolvecrack(jsonfile, logstr, optheader, flow):
print("[松子日志]:" + logstr)
flow.response = http.HTTPResponse.make(
200, # (optional) status code
jsonfile, # (optional) content
optheader) # (optional)
def readjson(jsonname):
with open(os.path.abspath('.') + "\\EvolveCrack\\" + jsonname, encoding="utf-8") as f:
return f.read()
class Addon(object):
def __init__(self):
print("EvolveEmulator插件成功挂载!\nEvolveEmulator addon is successfully running!")
def response(self):
print("\n")
def request(self, flow) -> None:
#print("我觉得这里怎么不得执行一下啊嗯?")
if "doorman/1" in flow.request.pretty_url:
evolvecrack(readjson("doorman.json"), "Game are Trying to request doorman in 2K", twokheader, flow)
elif "telemetry/1" in flow.request.pretty_url:
if "configs.generate" in flow.request.text:
telemetryconfiggenerate = str(readjson("telemetryconfiggenerate.json"))
# Set Time to Local Time.
teleconfig = telemetryconfiggenerate.replace("\"serverTime\": 0",
"\"serverTime\": " + str(int(time.time())))
evolvecrack(teleconfig, "Game are Trying to request teleconfig in 2K", twokheader, flow)
elif "sessions/1" in flow.request.pretty_url:
session = str(readjson("sessions.json"))
# Here,it changed not only serverTime but also appContext.
sessionto = json.loads(str(flow.request.get_text()))
appConText = sessionto["header"]["appContext"]
sessionre = session.replace("\"serverTime\": 0", "\"serverTime\": " + str(int(time.time())))
sessionre = sessionre.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
evolvecrack(sessionre, "Game are Trying to request sessions-heartbeat in 2K", twokheader, flow)
elif "news/1" in flow.request.pretty_url:
news = str(readjson("news.json"))
evolvecrack(news, "Game are Trying to request news in 2K", twokheader, flow)
elif "storage/1" in flow.request.pretty_url:
storage = str(readjson("iteminsert.json"))
evolvecrack(storage, "Game are Trying to request storages - iteminsert in 2K", twokheader, flow)
elif "sso/1" in flow.request.pretty_url:
# It change the appContext too,shit!
ssoto = json.loads(str(flow.request.get_text()))
sso = str(readjson("applogon.json"))
appConText = ssoto["header"]["appContext"]
sso = sso.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
evolvecrack(sso, "Game are Trying to request sso-authlogon in 2K", twokheader, flow)
elif "stats/1" in flow.request.pretty_url:
# It change the appContext too.
statsto = json.loads(str(flow.request.get_text()))
stats = str(readjson("stats.json"))
appConText = statsto["header"]["appContext"]
stats = stats.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
evolvecrack(stats, "Game are Trying to request stats in 2K", twokheader, flow)
elif "entitlements/1" in flow.request.pretty_url:
if "steamApi.user.checkAppOwnership" in flow.request.text:
# Set All DLC Unlock(No Steam ID will lock all DLC):
checkapp = str(readjson("checkAppOwnership.json"))
checkappto = json.loads(str(flow.request.get_text()))
appConText = checkappto["header"]["appContext"]
checkapp1 = checkapp.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
checkapps = checkapp1.replace("\"ownersteamid\": \"0\"", "\"ownersteamid\": " + "\"76561198587806892\"")
evolvecrack(checkapps, "Game are Trying to request checkAppOwnership in 2K", twokheader, flow)
elif "entitlementDefs.getFirstPartyMapping" in flow.request.text:
# Unknown request.Just send back as its format.
mapto = json.loads(str(flow.request.get_text()))
map = str(readjson("getFirstPartyMapping.json"))
appConText = mapto["header"]["appContext"]
map = map.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
evolvecrack(map, "Game are Trying to request getFirstPartyMapping in 2K", twokheader, flow)
elif "grants.find" in flow.request.text:
# Unknown request 2.Just send back as its format.Need 2 replace.
grants = str(readjson("grantsfind.json"))
grantsto = json.loads(str(flow.request.get_text()))
appConText = grantsto["header"]["appContext"]
grants1 = grants.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
grantss = grants1.replace("\"createdOn\": 0", "\"createdOn\": " + str(int(time.time())))
evolvecrack(grantss, "Game are Trying to request grantsfind in 2K", twokheader, flow)
elif "content/1" in flow.request.pretty_url:
if "strings.get" in flow.request.text:
strsto = json.loads(str(flow.request.get_text()))
strs = str(readjson("stringsget.json"))
appConText = strsto["header"]["appContext"]
strs = strs.replace("\"appContext\": 0", "\"appContext\": " + str(appConText))
evolvecrack(strs, "Game are Trying to request stringsget in 2K", twokheader, flow)
elif "auth.php" in flow.request.pretty_url:
# This is for dsx-1-b.4v1game.net.
authmulti = str(readjson("SteamLogin.json"))
evolvecrack(authmulti, "Game are Trying to Go MultiPlayer.", multiheader, flow)
elif "requestserver.php" in flow.request.pretty_url:
# This is for dsx-1-b.4v1game.net.
requestlb = str(readjson("requestserver.json"))
evolvecrack(requestlb, "Game are Trying to Go MultiPlayer.", multiheader, flow)
elif "Production/252019.252019.0/build_config_signed.json" in flow.request.pretty_url:
# mb_exclusions KrakenElder, Don't know what it is.
# Although it is transparent with "octet-stream", I think it will works using "readjson".
# There are two build_config_signed.json here.
mb_exclusions = readjson("build_config_signed0.json")
mb_header = {
"Content-Type": "application/octet-stream",
"Connection": "keep-alive",
"Last-Modified": "Tue, 19 Jan 2016 17:29:48 GMT", # I am not sure whether it will check for that.
"Accept-Ranges": "bytes",
"Server": "BmazonS3", # It's Okay I think,lol.
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))), # now time.
"ETag": "\"302fd31b05d6893d009f81f5babe6de2\"", # Seems game doesn't use this.
"X-Cache": "Never from cloudfront",
"Via": "1.1 646b6f21a2659c68f7a3822d035b97d3.cloudfront.net (CloudFront)", # Well, no speed up I think.
"X-Amz-Cf-Pop": "NRT57-C2",
"X-Amz-Cf-Id": "Bg5xfYkwkZXBisBgsbBLjoR_u3V_zJfPK9W7T1Jy57bmsktcUqTE5A=="
}
evolvecrack(mb_exclusions, "Game are Trying to request mbclusions from updateserver.", mb_header, flow)
elif "Production/252019.252019/micro_patch_version_signed.json" in flow.request.pretty_url:
# Micro patch json. If game are not have it in GameDLC08, it will send request to server to get.
microjson = readjson("micro_patch_version_signed.json")
micro_header = {"Content-Type": "application/octet-stream",
"Connection": "keep-alive",
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))),
# now time.
"Last-Modified": "Thu, 28 Apr 2016 19:21:44 GMT",
"ETag": "\"0b8122e356ca818bf97c80e71379bef4\"",
"Cache-Control": "max-age=259200",
"Accept-Ranges": "bytes",
"Server": "BmazonS3",
"X-Cache": "Never from cloudfront",
"Via": "1.1 646b6f21a2659c68f7a3822d035b97d3.cloudfront.net (CloudFront)",
"X-Amz-Cf-Pop": "NRT57-C2",
"X-Amz-Cf-Id": "pKEfUlyGx4Sn1J-uWqjQ6U3OskCD-9iXUYffyW-U99MhYGQOdJIE2Q==",
"Age": "172185"
}
evolvecrack(microjson, "Game are Trying to request micro version from updateserver.", micro_header, flow)
elif "Production/252019.252019/micro_patch_252019.252019_31.pak" in flow.request.pretty_url:
# This will redirect it to Gitee, which are the same like github, Or you can return the file on your server.
# flow.request.url = "https://gitee.com/firehomework/evolve-legacy-files" \
# "/raw/master/micro_patch_252019.252019_31.pak"
print("Here")
mb_header = {
"Content-Type": "text/plain",
"Connection": "keep-alive",
"Transfer - Encoding": " chunked",
"Last-Modified": "Tue, 19 Jan 2016 17:29:48 GMT", # I am not sure whether it will check for that.
"Accept-Ranges": "bytes",
"Server": "BmazonS3", # It's Okay I think,lol.
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))), # now time.
"ETag": "\"302fd31b05d6893d009f81f5babe6de2\"", # Seems game doesn't use this.
"X-Cache": "Never from cloudfront",
"Via": "1.1 646b6f21a2659c68f7a3822d035b97d3.cloudfront.net (CloudFront)", # Well, no speed up I think.
"X-Amz-Cf-Pop": "NRT57-C2",
"X-Amz-Cf-Id": "Bg5xfYkwkZXBisBgsbBLjoR_u3V_zJfPK9W7T1Jy57bmsktcUqTE5A=="
}
with open(os.path.abspath('.') + "\\EvolveCrack\\" + "micro_patch_252019.252019_31.pak", "rb") as f:
evolvecrack(f.read(), "Game are Trying to request micro pak from updateserver.", mb_header, flow)
# Actually I don't surely know whether it will work.
# And the game won't request it when using fake server. Why?!
elif "Production/252019.252019.31/build_config_signed.json" in flow.request.pretty_url:
# This is another build_config_signed.json.
anocof = readjson("build_config_signed.json")
mb_header = {
"Content-Type": "application/octet-stream",
"Connection": "keep-alive",
"Last-Modified": "Tue, 19 Jan 2016 17:29:48 GMT", # I am not sure whether it will check for that.
"Accept-Ranges": "bytes",
"Server": "BmazonS3", # It's Okay I think,lol.
"Date": str(time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(time.time()))), # now time.
"ETag": "\"302fd31b05d6893d009f81f5babe6de2\"", # Seems game doesn't use this.
"X-Cache": "Never from cloudfront",
"Via": "1.1 646b6f21a2659c68f7a3822d035b97d3.cloudfront.net (CloudFront)", # Well, no speed up I think.
"X-Amz-Cf-Pop": "NRT57-C2",
"X-Amz-Cf-Id": "Bg5xfYkwkZXBisBgsbBLjoR_u3V_zJfPK9W7T1Jy57bmsktcUqTE5A=="
}
evolvecrack(anocof, "Game are Trying to request another build_config from updateserver.", mb_header, flow)
else:
print("[松子日志]:" + "Maybe there are something I forgot to emulate.")
print("[松子日志]:It's okay as seems there are lots of them just need a response...")
print(flow.request.pretty_url)
requestlb = str(readjson("requestserver.json"))
evolvecrack(requestlb, "Something I forgot to emulate but seems okay", multiheader, flow)
# see source mitmproxy/master.py for details
def loop_in_thread(loop, m):
asyncio.set_event_loop(loop) # This is the key.
m.run_loop(loop.run_forever)
# 用于文件读取的函数
def read_file(filepath):
with open(filepath) as fp:
content = fp.read()
return content
if __name__ == "__main__":
print("Welcome to use EvolveReborn Software By Pinenut. 严禁倒卖!")
print("Lots of thanks:Nemirtingas,schmogmog,nemerod,kiagam")
# This is an egg,and it doesn't have any useful function, Like a joke, so just ignore it,please?
d1 = datetime.datetime.today()
d2 = datetime.datetime(2019, 10, 16)
if(d1 - d2).days % 365 == 0:
print("小(夹)彩(私)蛋(活)!今天是对于作者来讲很重要的周年纪念日哦!")
print("Hey!Today is a very important day for author! Anniversary!")
else:
print("A number:" + str((d1 - d2).days % 365))
print("今天是:" + str(d1))
print("Today is " + str(d1))
# This is an egg,and it doesn't have any useful function, Like a joke, so just ignore it,please?
serverport = 2000
print("================================================================")
# 添加读取文件部分
if not os.path.exists("EvolveCrack"):
print("严重错误……您的EvolveCrack文件夹呢……考虑重新拷贝一下文件吧……")
print("Fatal Error...Cannot found EvolveCrack Folder...Copy the crack and try again...")
os.system("pause")
exit(1)
if os.path.exists("motd.txt"):
print("下面的是motd的公告/包含的信息不由我做主。")
print("The thing below will read from motd.txt, I don't know what in it.")
print("===========================MOTD START===================================")
with open("motd.txt", "r",encoding="utf-8") as fr:
for line in fr:
print(line)
print("===========================MOTD OVER===================================")
if os.path.exists("serverport.txt"):
with open("serverport.txt", "r") as fr:
# 以r形式打开文件
for line in fr: # 一行一行读取
# 读取到了端口号信息
print("读取到修改后的端口信息,信息为\n" + str(line))
print("Read successfully!The port now is :\n" + str(line))
print("正在修改端口号。\nChanging the server port...")
serverport = int(line)
# 添加昵称修改部分
if os.path.exists("local_save.txt"):
localsave = read_file("local_save.txt")
if os.path.exists(localsave + "\\" + "settings" + "\\" + "account_name.txt"):
account_name = read_file(localsave + "\\" + "settings" + "\\" + "account_name.txt")
if account_name == "Goldberg":
print("您要考虑修改一个名字吗?这个名字是默认名字,辨识度很差!\n"
"Do you wanna to change the name? This name is the default name, "
"there will lots of people named that.\n")
choose = input("输入Y来修改名字,输入其他不修改名字并启动游戏\nInput Y then press Enter to change the name, Others to run "
"the game without changing the name\n")
if choose == "Y" or choose == "y":
newname = input("请输入新的名字!(不支持中文!)\nPlease Input your new name!")
with open(localsave + "\\" + "settings" + "\\" + "account_name.txt", "w+", encoding="utf-8") as f:
f.write(newname)
print("修改新名字成功,新名字为:" + newname + "\n Set new name successfully!New name is" + newname)
else:
print("看起来这是您第一次启动游戏。请按照向导设置您的游戏属性!\nSeems this is the first time you run the game,please follow the "
"guide to set your game!")
newname = input("请输入您游戏内的名字!(不支持中文!)\nPlease Input your new name!")
#先得创建文件夹
os.mkdir(localsave + "\\" + "settings")
with open(localsave + "\\" + "settings" + "\\" + "account_name.txt", "w+", encoding="utf-8") as f:
f.write(newname)
print("设置名字成功,您游戏内的名字为:" + newname + "\n Set the name successfully, your new name is" + newname)
options = Options(listen_host='0.0.0.0', listen_port=serverport, http2=True, confdir="./certs",
mode="reverse:[url=http://www.baidu.com]http://www.baidu.com[/url]")
m = DumpMaster(options, with_termlog=False, with_dumper=False)
config = ProxyConfig(options)
m.server = ProxyServer(config)
m.addons.add(Addon())
print("服务器已经运行!请配置您的serverip为127.0.0.1(默认即配置)")
print("Server is running, Port = " + str(serverport) + ", Please configure your serverip to 127.0.0.1(A default "
"config)")
print("================================================================")
print("正在启动游戏……如果游戏没有成功启动,请手动启动RebornEvolve.exe!\nRunning game...If the game failed to run,please run "
"RebornEvolve.exe by yourself")
if(os.path.exists("RebornEvolve.exe")):
os.startfile("RebornEvolve.exe")
else:
print("启动失败,可能这个文件被误杀了。")
print("Failed to run the game, maybe the RebornEvolve.exe was deleted by anti-virus software.")
# run mitmproxy in background, especially integrated with other server
loop = asyncio.get_event_loop()
t = threading.Thread(target=loop_in_thread, args=(loop, m))
t.start()
# Other servers might be started too.
# time.sleep(20)
# print('going to shutdown mitmproxy')
# m.shutdown()
在这样配合之下,终于!完成了整个游戏的局域网化!实现了脱离Steam!
小插曲:歪门邪道解决游戏的输入法崩溃
游戏当出现中文输入法的时候会导致游戏崩溃,一按就崩溃。我不知道原因,也不知道咋解决,但是我想到一个好办法(大嘘)
我使用AheadLib做了一个imm32.dll的中间dll,之后用UltraEdit修改了Evolve.exe,将imm32.dll改成cmm32.dll。
之后在中间dll内写了这么个代码:
哎,我不知道你为啥崩溃,但是我让你的输入法不能用了不就完了.jpg
(如果有大佬愿意指点为什么崩溃,感激不尽哈哈)
阶段??:一些解决不了的问题
1. 游侠Hook问题。
我刚开始是打算给游侠用的,但是游侠的补丁是会Hook ws2_32.dll的,这样就会导致2K验证失效,请问有无好办法规避?
2.2K验证的https问题。
我网上搜索几乎没有看到相关资料(ws2_32和https),大概是我搜索的方向不对?而且很神奇的不在于此,改了端口后抓包明显是http(显示都改成了HTTP,没假),但是仍然需要证书验证,个人猜测是不是这部分在游戏的exe代码里?该软件反调试,我不擅长于此,所以我的重心都是围绕在“骗过游戏“进行的,对此并不了解。
最后:
新人一枚,求大佬轻喷 |