hare_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_clearsess(data->cookies);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
else if(strcasecompare(argptr, "FLUSH")) {
/* flush cookies to file, takes care of the locking */
Curl_flush_cookies(data, 0);
}
else if(strcasecompare(argptr, "RELOAD")) {
/* reload cookies from file */
Curl_cookie_loadfiles(data);
break;
}
可能性是无止境。我们有很多内联常量和许多字符串引用可供我们使用,以在内存中找到函数。该函数从以下程序集开始:
mov qword ptr ss:[rsp+8],rbx
mov qword ptr ss:[rsp+10],rbp
mov qword ptr ss:[rsp+18],rsi
push rdi
sub rsp,30
xor ebp,ebp
mov rsi,r8
mov rbx,rcx
cmp edx,D2
如我们所见,寄存器edx
将包含常量。根据设置的值,它将分支到不同的代码以应用传递给函数的选项。我们主要是在寻找常数0x40
和0x51
。从原始源代码中我们知道,第三个参数包含该值,在这种情况下,它将为true
或false
。第三个寄存器是,r8
并包含指向持有该标志的存储器的指针。我们可以使用x64dbg的条件断点在触发时自动修补内存:
在下图中,您可以看到edx
设置为,0x40
并且该寄存器r8
指向内存中的修补值。
您可能想知道:“但是我们正在触及这个过程吗?”。没错,就是这样,这就是为什么我要在这里停止:)我目前正在为EasyAntiCheat(在上面的屏幕快照中一直在使用)的用户模式模拟器进行开发,我们将在其中介绍更多调试功能。文章。
回顾过去,我希望我曾经尝试过这样做。从Daylight的记录看Dead,很明显,即使在最新版本中,它们也一定弄乱了一些琐碎的东西。游戏使用虚幻引擎4。所有游戏资产都存储在所谓的“ pak”文件中。检查这些文件的字符串已经揭示了很多其他信息。如果我告诉您“ Dead by Daylight”不对“ pak”文件进行任何完整性检查,您会相信我吗?对。有两种方法可以解决此问题,要么给QuickBMS打个镜头,要么使用十六进制编辑器。在本文中,我们将使用十六进制编辑器方法,将QuickBMS的操作留给读者练习。好处是您可以修补所有游戏中的配置文件和资产,使您可以真正接近完整的wallhack。我们将要编辑的文件称为,pakchunk0-WindowsNoEditor.pak
并托管与作弊目的相关的主要配置文件。查找SSL
并将内容擦除为空字节。
这是所有正在使用的固定密钥的转储:
+PinnedPublicKeys=".dev.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;PiEjPYP2N0QUoKvrwZZjmvSLIl0bBGJgKUevOeNowEM="
+PinnedPublicKeys=".qa.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;tBLmAw0lCqG3/5sn6ooVk9JNdIcptJb0iXoi4qkAqUo="
+PinnedPublicKeys=".stage.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;OBU5+MqEy/LV95MgQf23LGpAaaYElBvALjPW7AgmMNo="
+PinnedPublicKeys=".ptb.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;ICaQHYr/VHjpTY6UKcm8FUtWnUMe9q6WNrzr+WDuUls="
+PinnedPublicKeys=".cert.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;pvf7WXymw2xK8n6YTqblRrt3vwe2mSuGmAk8buiF2C4="
+PinnedPublicKeys=".management.live.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;EXrEe/XXp1o4/nVmcqCaG/BSgVR3OzhVUG8/X4kRCCU="
+PinnedPublicKeys="steam.live.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;EXrEe/XXp1o4/nVmcqCaG/BSgVR3OzhVUG8/X4kRCCU="
无论如何,保存文件,打开代理(在我的情况下为mitmweb)并运行游戏!
在此阶段,您应该能够看到请求。如您所见,还有一个对终结点的新请求clientVersion/check
。还记得我之前提到的按键吗?这些密钥被发送到该端点,从而验证我们bhvrSession
是否被允许向游戏服务器发送进一步的请求。但是,我们不必担心它们,因为游戏可以毫无问题地为我们做到这一点。
最终,我们将FullProfile/binary
在此阶段以一个GET请求结束该请求,该请求将获取当前玩家的个人资料数据。
GET https://latest.live.dbd.bhvronline.com/api/v1/players/me/states/FullProfile/binary HTTP/1.1
...
DbdDAgAC0Yh1kjiaxVnUM1aTDgjahR1BTCe3iiAWGiwQk+4OCPnSOU6mzbfid9Ag6883sQKbf6G9jRiYUD9DQUmA4TmT6yPBznJEcxhzvp+W/QhcgXhPLsD6o8CWt1iMcV8uStjBH3W6r+Bk0COJ5SSSOdKNU8
此数据包含库存,这对于欺骗项目特别有趣。POST请求使用了非常相似的端点,该请求上传了清单。可以对清单进行解密,编辑,加密,然后将其发送到服务器。这使我们能够获得所需的物品和所需的额外津贴。为了使事情变得清晰,以下是它们解密配置文件的方式:
cipher = AES.new(b"5BCC2D6A95D4DF04A005504E59A9B36E", AES.MODE_ECB)
profile = flow.response.content.decode()[8:]
profile = base64.b64decode(profile)
profile = cipher.decrypt(profile)
profile = "".join([chr(c + 1) for c in profile])
profile = base64.b64decode(profile[8:])
profile = profile[4:len(profile)]
profile = zlib.decompress(profile).decode("utf16")
profile = json.loads(profile)
加密的工作方式相同,只是相反。这也留给读者练习。
还有另一个请求发送到并命名/getUrl
为终结点,它为您提供了一个Websocket终结点的URL。有很多信息在传递,其中一些处于游戏状态:)不幸的是,一次只能连接一个客户端,但是这不会阻止您代理Websocket连接。这超出了范围,仅作为信息提及,我们将以更有趣的方式进行此操作。尽管如此,此处仍提供示例代码。
最近出现了一些帖子,人们在白天通过Deadlight日志文件轮询“死者”,以找出在大厅的凶手。这很有趣,但是根本不应该编写这样的代码。特别是如果作者声称它是出于“学习目的”,则不会:
yourId2 = (str(x2[len(x2) - 2].split('\n[')[0]))[46:]
yourId3 = (str(x2[len(x2) - 3].split('\n[')[0]))[46:]
yourId4 = (str(x2[len(x2) - 4].split('\n[')[0]))[46:]
yourId5 = (str(x2[len(x2) - 5].split('\n[')[0]))[46:]
verifyKiller2 = (((((str(x2[len(x2) - 2].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller3 = (((((str(x2[len(x2) - 3].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller4 = (((((str(x2[len(x2) - 4].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller5 = (((((str(x2[len(x2) - 5].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
让我们以正确的方式做。在我们前面的调试会话中,您可能已经注意到游戏将字符串打印到x64dbg的“ Log”选项卡中。这是因为Windows将DbgPrint
附加的所有调试字符串重定向到调试器。但是,实际上可以在没有调试器的情况下访问这些字符串,而我最近刚从DebugView中撤消了这样做的方法。您可以在我的个人GitHub上找到反向项目。基本思想是访问Windows的消息缓冲区(DBWIN_BUFFER
)并等待某些事件(DBWIN_BUFFER_READY
和DBWIN_DATA_READY
)。这使我们能够与游戏日志进行实时交互,而不必轮询任何文本文件并意外地滤除不相关的旧信息。利用非常基本的正则表达式,我们可以确定杀手(即使他在大厅里改变了角色,而原始脚本无法做到!)以及他的Steam个人资料。选项无穷!这是实际情况的细分版本:
const auto process_id = find_process("DeadByDaylight-Win64-Shipping.exe");
const std::regex character_id_pattern("Spawn new pawn characterId (\\d+)\\.");
const std::regex steam_id_pattern("Session:GameSession PlayerId:([0-9\\-a-z]+)\\|(\\d+)");
const std::regex killer_pattern("MatchMembersA=\\[\\\"([a-z0-9\\-]+)\\\"\\]");
auto buffer_ready = open_event(
EVENT_ALL_ACCESS,
L"DBWIN_BUFFER_READY");
auto data_ready = open_event(
SYNCHRONIZE,
L"DBWIN_DATA_READY");
auto file = open_mapping(
L"DBWIN_BUFFER");
auto buffer = reinterpret_cast<message*>(
wrapper::map_view_of_file(
file,
SECTION_MAP_READ,
0, 0, 0));
std::string killer_id;
while (wrapper::wait_for_single_object(
data_ready,
INFINITE) == WAIT_OBJECT_0)
{
if (buffer->process_id == process_id)
{
auto message = std::string(buffer->data);
std::smatch matches;
if (std::regex_search(message, matches, character_id_pattern))
{
auto character_id = std::stoi(matches[1].str());
auto killer = KILLERS.find(character_id);
if (killer != KILLERS.end())
{
std::cout << "Killer: " << killer->second << std::endl;
}
}
else if (std::regex_search(message, matches, steam_id_pattern))
{
auto player_id = matches[1].str();
auto steam_id = matches[2].str();
if (player_id == killer_id)
{
std::cout << "Killer Steam Profile: https://steamcommunity.com/profiles/" << steam_id << std::endl;
}
}
else if (std::regex_search(message, matches, killer_pattern))
{
killer_id = matches[1].str();
std::cout << "Found Killer PlayerID: " << killer_id << std::endl;
}
}
wrapper::set_event(buffer_ready);
}
wrapper::unmap_view_of_file(buffer);
wrapper::close_handle(file);
wrapper::close_handle(buffer_ready);
wrapper::close_handle(data_ready);
该代码仅用于演示目的,您可以在此处找到该项目。
在研究过程中,我创建了一个脚本(authenticator.py),该脚本可自动进行信息转储(例如bhvrSession
)并解密清单。数据正在单独的JSON文件中写入。创建文件后,您可以自由运行脚本levelup.py和rankup.py。该脚本发送基本POST请求到端点/api/v1/ranks/pips
和/api/v1/extensions/playerLevels/earnPlayerXp
分别。所需的JSON主体如下(与上述顺序相同):
{
"data": {
"consecutiveMatch": 1,
"emblemQualities": [
"Iridescent",
"Iridescent",
"Iridescent",
"Iridescent"
],
"isFirstMatch": true,
"levelVersion": 1337,
"matchTime": 1000,
"platformVersion": "steam",
"playerType": "survivor"
}
}
{
"forceReset": true,
"killerPips": 2,
"survivorPips": 2
}
总结一下,我想说的是,还有更多的方法可以探索,并且发布的代码可以通过许多方式进行改进。我认为最重要的收获是,如果使用过多的调试事件/字符串可能会非常危险。我相信“行为”的一般方法和工作都很好。但是,某些细节仍然有改进的余地,例如修复其反热检查所有游戏文件。我没有提到的另一个重要说明是pak文件未使用加密密钥。虚幻引擎4具有使用特定密钥加密所有游戏文件的功能。当然,可以对它进行反向工程,但是尽管如此,它仍使作弊变得更加困难。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。