吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17581|回复: 28
收起左侧

[原创] x64dbg 获取 UE4 虚幻引擎游戏的资源包密钥

[复制链接]
xyx0826 发表于 2019-3-22 16:48

最近发现一款很好玩的 UE4 游戏,玩着玩着就想提取游戏里的一些资源出来折腾。下了个提取器打开一看,资源包竟然是加密的!

好嘛,反正 UE4 引擎本身是开源的,算法逻辑什么的都看得到。想必不会太难。良心游戏开发商还留下了 PDB 数据库。之前我用 x64dbg 只玩过改跳转过验证,这次试试结合符号的调试。

首先,虚幻引擎的资源包格式为 .pak。使用 16 进制编辑器把资源包打开以后可以看到,有些资源内容似乎没有加密:

72 03 19 d9 03 19 01 b8 03 19 41 53 53 45 52 54 49 4f 4e 4d 4f 44 45 4c 53 54 52 55 45 46 4f 52 43 45 53 01 57 65 02 31 68 02 84 62 65 02 31 62 68 02 84  .r..ù...¸..ASSERTIONMODELSTRUEFORCES.We.1h..be.1bh..  
1e 01 95 50 52 4f 56 45 01 95 54 52 55 45 03 1e 01 95 46 4f 52 43 45 07 0f 62 65 02 31 62 68 02 84 02 34 06 1b 04 08 02 3a 06 1b 04 08 02 ab 03 10 4f 46  ....PROVE..TRUE....FORCE..be.1bh...4.....:.....«..OF  

所以我猜想,只有资源索引是加密的。搜索找到 UE4 源码里加载 PAK 文件的函数:

void FPakFile::LoadIndex(FArchive* Reader)
{
    // ...
    // Decrypt if necessary
    if (Info.bEncryptedIndex)
    {
        DecryptData(IndexData.GetData(), Info.IndexSize, Info.EncryptionKeyGuid);
    }
    // ...
}

证明猜想。继续查看 DecryptData 函数:

void DecryptData(uint8* InData, uint32 InDataSize, FGuid InEncryptionKeyGuid)
{
    SCOPE_SECONDS_ACCUMULATOR(STAT_PakCache_DecryptTime);
    FAES::FAESKey Key;
    FPakPlatformFile::GetPakEncryptionKey(Key, InEncryptionKeyGuid);
    check(Key.IsValid());
    FAES::DecryptData(InData, InDataSize, Key);
}

第一直觉肯定是去追 FPakPlatformFile::GetPakEncryptionKey 来看密钥是如何获取的了。然而这个函数用了委托,在反汇编下比较难分析。所以我转而查看 FAES::DecryptData 函数:

// 本行防止 Markdown 鬼畜高亮
void FAES::DecryptData(uint8* Contents, uint32 NumBytes, const FAESKey& Key)
{
    checkf(Key.IsValid(), TEXT("No valid decryption key specified"));
    DecryptData(Contents, NumBytes, Key.Key, sizeof(Key.Key));
}

这个函数首先检查密钥是否有效,如无效则会断言报错。将游戏文件载入 x64dbg,搜索报错字符串,定位到如下逻辑:

        00007FF78CF60750 | 48:895C24 08      | mov qword ptr ss:[rsp + 8], rbx
        00007FF78CF60755 | 48:897424 10      | mov qword ptr ss:[rsp + 10], rsi
        00007FF78CF6075A | 57                | push rdi
        00007FF78CF6075B | 48:83EC 20        | sub rsp, 20
        00007FF78CF6075F | 49:8BD8           | mov rbx, r8
        00007FF78CF60762 | 8BFA              | mov edi, edx
        00007FF78CF60764 | 48:8BF1           | mov rsi, rcx
        00007FF78CF60767 | 45:33C9           | xor r9d, r9d
        00007FF78CF6076A | 49:8BC0           | mov rax, r8
        00007FF78CF6076D | 0F1F00            | nop dword ptr ds:[rax], eax
        00007FF78CF60770 | 8338 00           | cmp dword ptr ds:[rax], 0
  |<=== 00007FF78CF60773 | 75 4D             | jne game.7FF78CF607C2
  |     00007FF78CF60775 | 41:FFC1           | inc r9d
  |     00007FF78CF60778 | 48:83C0 04        | add rax, 4
  |     00007FF78CF6077C | 41:83F9 08        | cmp r9d, 8
  |     00007FF78CF60780 | 72 EE             | jb game.7FF78CF60770
  |     00007FF78CF60782 | 4C:8D0D D79F7E02  | lea r9, qword ptr ds:[7FF78F74A760]
  |                                         |     L"No valid decryption key specified"
  |     00007FF78CF60789 | 41:B8 9E040000    | mov r8d, 49E
  |     00007FF78CF6078F | 48:8D15 4AA07E02  | lea rdx, qword ptr ds:[7FF78F74A7E0]
  |     00007FF78CF60796 | 48:8D0D 7B9F7E02  | lea rcx, qword ptr ds:[7FF78F74A718]
  |     00007FF78CF6079D | E8 AEE70000       | call game.7FF78CF6EF50
  |     00007FF78CF607A2 | 4C:8D0D 97A07E02  | lea r9, qword ptr ds:[7FF78F74A840]
  |                                         |     L"No valid decryption key specified"
  |     00007FF78CF607A9 | 41:B8 9E040000    | mov r8d, 49E
  |     00007FF78CF607AF | 48:8D15 FAA07E02  | lea rdx, qword ptr ds:[7FF78F74A8B0]
  |     00007FF78CF607B6 | 48:8D0D 53A17E02  | lea rcx, qword ptr ds:[7FF78F74A910]
  |     00007FF78CF607BD | E8 9EBAFFFF       | call game.7FF78CF5C260
  |===> 00007FF78CF607C2 | 41:B9 20000000    | mov r9d, 20
        00007FF78CF607C8 | 4C:8BC3           | mov r8, rbx
        00007FF78CF607CB | 8BD7              | mov edx, edi
        00007FF78CF607CD | 48:8BCE           | mov rcx, rsi
        00007FF78CF607D0 | 48:8B5C24 30      | mov rbx, qword ptr ss:[rsp + 30]
        00007FF78CF607D5 | 48:8B7424 38      | mov rsi, qword ptr ss:[rsp + 38]
        00007FF78CF607DA | 48:83C4 20        | add rsp, 20
        00007FF78CF607DE | 5F pop rdi
        00007FF78CF607DF | E9 0C000000       | jmp <game.FAES::DecryptD

如果 00007FF78CF60770 处的比较中,rax 指向的内存地址值为零,jne 跳转才会执行。

这个跳转便是用来判断密钥是否有效的部分。如果密钥起始处不为空,逻辑便会跳转后运行到 00007FF78CF607DF,跳转进入 AES 解密逻辑。

如果密钥起始处为空,接下来,程序将循环 7 次,每次将起始处向后挪 4 个字节。(这样可以确保找到长度更小的密钥:如果密钥长度比较小,前面的空间便都是零。)如果实在找不到(读到的都是零)就会断言报错了。

所以用来在 00007FF78CF6076Arax 赋值的 r8 寄存器就指向内存里密钥的起始处。在 00007FF78CF6076A 下断点,获取 r8 寄存器里的值。内存窗口里这个值指向的地方便是密钥的起始处了。

AES-256 的密钥长度为 32 个字节,所以在内存窗口中复制 32 个字节的数据,转为 16 进制,塞进资源提取器,成功!

免费评分

参与人数 6威望 +1 吾爱币 +12 热心值 +5 收起 理由
40m41h42t + 1 + 1 我很赞同!
决心是1231 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 谢谢@Thanks!
鲁大师6806 + 1 谢谢@Thanks!
加油 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lm180180 发表于 2020-10-22 23:29
sajuuk1129 发表于 2019-3-22 20:58
这种加密方式太水了。。汗颜

像那种严密加密的,加密后机器都跑死,吃电脑资源,还极其费电,这样的游戏和程序,从来不玩不用!
 楼主| xyx0826 发表于 2019-3-22 21:04
jimo 发表于 2019-3-22 18:23
这岂不意味着PUBG的PAK能随意解密了?

PUBG 的加密逻辑是一样的。我没买游戏,不清楚,不过主程序估计加了壳。
头像被屏蔽
SGmod 发表于 2019-3-22 17:37
jimo 发表于 2019-3-22 18:23
这岂不意味着PUBG的PAK能随意解密了?
明明是我先 发表于 2019-3-22 19:54
很不错的分享啊,感谢感谢
sajuuk1129 发表于 2019-3-22 20:58
这种加密方式太水了。。汗颜
xmzdts 发表于 2019-3-22 21:02
楼主解答了我的疑惑,感谢  谢谢分享技术
稻海香 发表于 2019-3-23 00:20
高手,这是高手
爱啦花花牛 发表于 2019-3-23 08:36
认真学习争取早日做大佬
haoxiangzuo 发表于 2019-3-23 12:09
谢谢楼主分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 02:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表