当前位置: 首页 > news >正文

为什么不是直接把 main.gd 扔回 pck

因为原游戏原本加载的是:

res://main.gdc
不是:

res://main.gd
所以你不能只改源码文件名就塞回去。更稳的做法是:

先把 .gd 编译回 .gdc
再用新的 .gdc 去替换包里的旧 .gdc
这就是你这两条命令连起来的意义。

  1. 两条命令连起来的完整语义

连起来就是这条链:

修改 main.gd

编译成 main.gdc

把新的 main.gdc 替换进 GD1.pck

得到可运行的 patched 版本
也就是:

第一条命令负责“把源码变回游戏能执行的格式”
第二条命令负责“把新逻辑塞回原游戏包”
6. 用一句最短的话总结

gd 是源码
gdc 是编译后的 Godot 脚本
pck 是 Godot 游戏资源包
两条命令分别是:

gd -> gdc
新 gdc -> patch 进 pck
aes,用的库封装,逸最简单的一集
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-144h]
AES_KEY aes_key; // [rsp+20h] [rbp-130h] BYREF
char buf[16]; // [rsp+120h] [rbp-30h] BYREF
char ciphertext[24]; // [rsp+130h] [rbp-20h] BYREF
unsigned __int64 v8; // [rsp+148h] [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”);
else
puts(“success”);
return 0;
}
else
{
fwrite(“Key must be 16 bytes\n”, 1u, 0x15u, stderr);
return 1;
}
}
已知密文密钥,直接解密呗

解出来是

sdpc{rerepwnpwnp
???

from Crypto.Cipher import AES
key = b"sdpc_key_123456_"
ct = bytes.fromhex(“ae2f680621fb34d6a8ee7019069ded5e”)
pt = AES.new(key, AES.MODE_ECB).decrypt(ct)
print(pt)
ryo ? soyo? mio?
ida打开

int __fastcall main(int argc, const char **argv, const char **envp)
{
Stream *Stream; // rax
unsigned int v4; // eax
char Buffer[1024]; // [rsp+20h] [rbp-60h] BYREF
char *Str1; // [rsp+420h] [rbp+3A0h]
char *Str2; // [rsp+428h] [rbp+3A8h]

_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”);
else
puts(“False”);
free(Str1);
return 0;
}
直接base64解码

SDPCSEC{welcome_NewStars}
逸别卷了,喝一杯茶休息一下
int _fastcall main(int argc, const char **argv, const char **envp)
{
size_t n; // rax
size_t n_1; // rax
uint32_t dest
[2]; // [rsp+18h] [rbp-28h] BYREF
uint32_t key[4]; // [rsp+20h] [rbp-20h] BYREF
char buf[8]; // [rsp+30h] [rbp-10h] BYREF
unsigned __int64 v9; // [rsp+38h] [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”);
else
puts(“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] = 0
key[2] = 0
key[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 位整数——这是把字符串当数字去加密的前提。

http://www.jsqmd.com/news/1229015/

相关文章:

  • 2026年图片购买网站选型攻略,一篇文章讲明白如何选、选哪个 - 极欧测评
  • 鸿蒙原生开发手记:徒步迹 - 弹出菜单与对话框封装
  • 鸿蒙原生开发手记:徒步迹 - 显式动画与转场动画
  • 【小程序课程设计/毕业设计】图书馆时段预约与座位分配管理平台 智能图书馆座位调度服务小程序的设计与实现 校园自习室座位资源管理小程序设计与实现【附源码、数据库、万字文档】
  • 2026甄选安康名包名表奢侈品回收劳力士卡地亚浪琴朗格萧邦爱马仕迪奥标杆门店实力盘点 - 谊识预商务
  • 金融AI的“监管大坝”正式合龙:从野蛮生长到有规可依
  • Fun-ASR性能对比与基准测试:与Whisper、Paraformer等模型的全面评测
  • 基于springboot摊位管理系统
  • Vibe Coding:3分钟AI对话构建Spring Boot完整服务实践
  • LeetCode 494:目标和问题解法精讲
  • Windows平板选购指南:性价比区间全解析
  • 北京华恒智信破解学校职责不清定岗定编管理案例
  • Steam DLC解锁终极指南:3分钟快速上手完整教程
  • Meta AI投资震荡:开源与闭源的技术路线之争
  • 2026甄选安庆名包名表奢侈品回收劳力士欧米茄萧邦朗格浪琴路易威登LV普拉达门店实力排行公布 - 谊识预商务
  • CVE-2026-8924实战排查教程:curl超级Cookie注入漏洞检测、绕过原理与完整修复方案
  • Unity性能优化全攻略:从核心概念到移动端实战调优
  • 金融行业签合同,快不是第一位的——安全和合规才是
  • 2.8万亿参数的“破壁者”:Kimi K3如何重新定义开源大模型的能力边界
  • 小蜜陪护机器人 - Agent扩展开发指南
  • 天气/气象数据批量采集——从多源API到智能可视化分析平台
  • 学历普通也能拿 offer,普通院校学生的春招突围策略
  • SpinKit-ObjC完全指南:UIKit中15种加载动画的终极实现方案
  • 【小程序课程设计/毕业设计】基于 SpringBoot 的身心健康生活服务小程序 健康数据统计与生活习惯养成小程序 大众健康资讯推送与自我管理小程序【附源码、数据库、万字文档】
  • 如何快速配置KK_Plugins:3个关键步骤详解
  • TI C2000 DCSM安全模块配置实战:从原理到代码保护
  • 82M参数Kokoro语音合成:如何在Python和JavaScript中部署50+种多语言语音
  • 2026甄选安阳名包名表奢侈品回收江诗丹顿卡地亚积家朗格伯爵普拉达罗意威核心门店实力盘点 - 谊识预商务
  • C语言的分支循环语句
  • ARM公版架构市场现状与技术挑战分析