为什么不是直接把 main.gd 扔回 pck
因为原游戏原本加载的是res://main.gdc不是res://main.gd所以你不能只改源码文件名就塞回去。更稳的做法是先把 .gd 编译回 .gdc再用新的 .gdc 去替换包里的旧 .gdc这就是你这两条命令连起来的意义。两条命令连起来的完整语义连起来就是这条链修改 main.gd↓编译成 main.gdc↓把新的 main.gdc 替换进 GD1.pck↓得到可运行的 patched 版本也就是第一条命令负责“把源码变回游戏能执行的格式”第二条命令负责“把新逻辑塞回原游戏包”6. 用一句最短的话总结gd 是源码gdc 是编译后的 Godot 脚本pck 是 Godot 游戏资源包两条命令分别是gd - gdc新 gdc - patch 进 pckaes用的库封装逸最简单的一集int __fastcall main(int argc, const char **argv, const char **envp){int i; // [rspCh] [rbp-144h]AES_KEY aes_key; // [rsp20h] [rbp-130h] BYREFchar buf[16]; // [rsp120h] [rbp-30h] BYREFchar ciphertext[24]; // [rsp130h] [rbp-20h] BYREFunsigned __int64 v8; // [rsp148h] [rbp-8h]v8 readfsqword(0x28u);read(0, buf, 0x10u);if ( strlen(sdpc_key_123456) 16 ){AES_set_encrypt_key(sdpc_key_123456, 128, aes_key);AES_encrypt(buf, ciphertext, aes_key);printf(“Ciphertext: “);for ( i 0; i 15; i )printf(”%02x”, (unsigned __int8)ciphertext[i]);putchar(10);if ( !strcmp(ciphertext, “ae2f680621fb34d6a8ee7019069ded5e”) )puts(“fail”);elseputs(“success”);return 0;}else{fwrite(“Key must be 16 bytes\n”, 1u, 0x15u, stderr);return 1;}}已知密文密钥直接解密呗解出来是sdpc{rerepwnpwnpfrom Crypto.Cipher import AESkey bsdpc_key_123456_ct bytes.fromhex(“ae2f680621fb34d6a8ee7019069ded5e”)pt AES.new(key, AES.MODE_ECB).decrypt(ct)print(pt)ryo ? soyo mioida打开int __fastcall main(int argc, const char **argv, const char **envp){Stream *Stream; // raxunsigned int v4; // eaxchar Buffer[1024]; // [rsp20h] [rbp-60h] BYREFchar *Str1; // [rsp420h] [rbp3A0h]char *Str2; // [rsp428h] [rbp3A8h]_main();Str2 “U0RQQ1NFQ3t3ZWxjb21lX05ld1N0YXJzfQ”;puts(“please input your flag:”);Stream __iob_func();fgets(Buffer, 1024, Stream);Buffer[strcspn(Buffer, “\n”)] 0;v4 strlen(Buffer);Str1 (char *)base64_encode(Buffer, v4);if ( !Str1 )return 1;if ( !strcmp(Str1, Str2) )puts(“Correct”);elseputs(“False”);free(Str1);return 0;}直接base64解码SDPCSEC{welcome_NewStars}逸别卷了喝一杯茶休息一下int _fastcall main(int argc, const char **argv, const char **envp){size_t n; // raxsize_t n_1; // raxuint32_t dest[2]; // [rsp18h] [rbp-28h] BYREFuint32_t key[4]; // [rsp20h] [rbp-20h] BYREFchar buf[8]; // [rsp30h] [rbp-10h] BYREFunsigned __int64 v9; // [rsp38h] [rbp-8h]v9 readfsqword(0x28u);read(0, buf, 8u);memset(key, 0, sizeof(key));n strlen(“sdpc”);memcpy(key, “sdpc”, n);*(QWORD *)dest 0;n_1 strlen(buf);memcpy(dest, buf, n_1);tea_encrypt(dest, key);printf(“Encrypted: %08x%08x\n”, dest_[0], dest_[1]);if ( !strcmp((const char *)dest_, 9dcfccd3088d1064) )puts(“fail”);elseputs(“success”);return 0;}read(0, buf, 8u);从标准输入键盘读最多 8 个字节存到 buf 数组里。read 是一个系统提供的基础函数。第一个参数 0 标准输入键盘。第二个参数 buf 读到的内容存到哪里。第三个参数 8u 最多读 8 个字节。所以你在键盘上打东西程序会取前 8 个字符存到 buf。memset(key, 0, sizeof(key));把 key 数组的每一个字节都设为 0。这叫清零。memset memory set设置内存。为什么这么做因为 key 声明时没有指定初始值里面的内容是垃圾值内存里之前残留的数据必须先清零才能用。sizeof(int) // → 4 (int 占 4 字节)sizeof(char) // → 1 (char 占 1 字节)sizeof(“abc”) // → 4 (字符串 “abc” 实际是 ‘a’,‘b’,‘c’,‘\0’共 4 字节)int arr[10];sizeof(arr) // → 40 (10 × 4 40 字节)用来返回目标在内存中占多少字节memcpy(key, “sdpc”, n);memcpy memory copy复制内存。把字符串 “sdpc” 的 4 个字节复制到 key 数组的开头。做完这一步key 数组的内容是key[0] “sdpc” 的前 4 个字节‘s’,‘d’,‘p’,‘c’key[1] 0key[2] 0key[3] 0(QWORD *)dest 0;把 dest_ 数组的前 8 个字节全部设为 0。QWORD 是 8 字节整数 是解引用把 dest当成那 8 字节看效果相当于 dest_[0] 0 且 dest_[1] 0。就是清零。memcpy(dest_, buf, n_1);把你输入的 n_1 个字符复制到 dest_ 数组里。dest_ 的前 4 字节被当作一个叫 dest_[0] 的整数后 4 字节被当作 dest_[1]。为什么要这样搞因为 dest_ 声明的是 uint32_t 数组每个元素 4 字节但你输入的是字符串一个一个字符。它用 memcpy 把这 8 个字符直接覆盖到 dest_ 的 8 字节上。这样你输入的 8 个字符就变成了两个 32 位整数——这是把字符串当数字去加密的前提。