diff --git a/patch_v2/meowpkg/nyai b/patch_v2/meowpkg/nyai index 3d1b5ae..59b3e17 100755 Binary files a/patch_v2/meowpkg/nyai and b/patch_v2/meowpkg/nyai differ diff --git a/patch_v2/meowpkg/nyai.c b/patch_v2/meowpkg/nyai.c index 3757894..760af21 100644 --- a/patch_v2/meowpkg/nyai.c +++ b/patch_v2/meowpkg/nyai.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,10 +33,11 @@ #include #include #include +#include #include #include -#define NYAI_VERSION "0.3.0" +#define NYAI_VERSION "0.3.1" #define BUF 1024 #define MAX_SRC 16 #define MAX_PKG 256 @@ -46,6 +48,11 @@ * 每次安装一个子目录 ---,无论成败都删。 */ #define STG_SUBDIR "MeowOS/tmp/pkgs" +/* 单包解压后体积上限(防解压炸弹:解包先于验签,恶意包可以在验签失败前把卡写满)。 + * 现网最大 App ≈ 6MB(ncm),512MB 留足余量;另加 statvfs 余量检查。 */ +#define MAX_UNPACK_BYTES (512LL * 1024 * 1024) +#define STG_SPACE_MARGIN (16LL * 1024 * 1024) + /* .mpk 签名 footer 布局(与 patch_v2/meow_verify/meow_sig.h 同步;此处只读 package 用于 * 「footer.package vs app.json.package」一致性检查——真正的验签仍由 meow_verify 执行)。 */ #define SIG_MAGIC "MEOWSIG1" @@ -111,13 +118,20 @@ static void strip_comment(char *s) if (p) *p = 0; } -/* 包名合法:仅 [A-Za-z0-9._-],且拒绝 "." / ".."(否则 MeowAPPs/ 会越出安装目录)。 */ +/* 包名合法:仅 [a-z0-9._-],且拒绝 "." / ".." / 前导或尾随 '.'。 + * 只收小写是安全要求:SD 卡是 exFAT,名字比较不区分大小写, + * 允许大写就能用 "OS.MEOW.READER" 落到已装的 MeowAPPs/os.meow.reader 上 + * 并绕过 is_system_pkg() 的签名门(现网包名全小写,收紧无兼容代价)。 */ static int valid_pkg(const char *s) { if (!s || !*s) return 0; - if (!strcmp(s, ".") || !strcmp(s, "..")) return 0; - for (; *s; s++) - if (!(isalnum((unsigned char)*s) || *s=='.' || *s=='-' || *s=='_')) return 0; + if (s[0] == '.') return 0; /* "." / ".." / 隐藏名 */ + size_t n = strlen(s); + if (s[n-1] == '.') return 0; /* exFAT/Windows 尾点映射怪癖 */ + for (; *s; s++) { + unsigned char c = (unsigned char)*s; + if (!(islower(c) || isdigit(c) || c=='.' || c=='-' || c=='_')) return 0; + } return 1; } static int unsafe_path(const char *s) @@ -127,6 +141,8 @@ static int unsafe_path(const char *s) /* 前向声明:错误前缀(install/upgrade),定义见安装期安全设施一节。 */ static void op_err(const char *fmt, ...); +/* 前向声明:独立 '..' 路径组件判定,定义见导出过滤一节(安装期成员名检查也用)。 */ +static int has_dotdot_comp(const char *p); static int mkdir_p(const char *path) { @@ -214,10 +230,10 @@ static void fmt_utc(long long sec, char *out, int n) unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); unsigned mp = (5*doy + 2)/153; unsigned d = doy - (153*mp+2)/5 + 1; - unsigned m = mp + (mp < 10 ? 3 : 9); /* 1..12 */ + int m = (int)mp + (mp < 10 ? 3 : -9); /* March-based → 1..12 */ if (m <= 2) y++; long long rem = sec % 86400; - snprintf(out, n, "%04lld%02u%02u-%02u%02u%02u", y, m, d, + snprintf(out, n, "%04lld%02d%02u-%02u%02u%02u", y, m, d, (unsigned)(rem/3600), (unsigned)((rem%3600)/60), (unsigned)(rem%60)); } @@ -366,7 +382,7 @@ typedef struct { long long version_code; /* app.json "version_code"(正整数,安装/升级比较唯一依据;缺时 has_=0) */ int has_version_code; /* app.json 是否显式声明 version_code */ char exec[512]; - char package[64]; /* app.json "package"(权威包名,== 文件名茎;II 期绑定校验用) */ + char package[64]; /* app.json "package"(唯一权威包名;M35 后与文件名无关) */ char author[128]; char routes[512]; char services[512]; @@ -860,10 +876,14 @@ static int cmd_list_upgradable(pkgrec *recs, int nrec) } /* ---------------- II 期签名校验(MPK_SIGNATURE_DESIGN §5.1,shell-out meow_verify) ---------------- */ -/* 系统包前缀:os.meow.*(防伪造系统包名;独立于 valid_pkg,不改变现有合法包名序列)。 */ +/* 系统包前缀:os.meow.*(防伪造系统包名)。 + * **必须大小写不敏感**:安装落盘目录在 exFAT 上(名字比较不区分大小写), + * 若这里区分大小写,"OS.MEOW.READER" 会被当成第三方放行并落到已装的 + * MeowAPPs/os.meow.reader 上,直接顶掉系统 App(valid_pkg 也已收紧为只收小写, + * 这里是第二道防线:任一改动失效都不会打开这个口子)。 */ static int is_system_pkg(const char *pkg) { - return pkg && !strncmp(pkg, "os.meow.", 8); + return pkg && !strncasecmp(pkg, "os.meow.", 8); } /* meow_verify 二进制路径:环境变量 MEOW_VERIFY_BIN 覆盖(宿主测试用),缺省 /usr/bin/meow_verify。 */ @@ -1063,36 +1083,106 @@ static void scrub(char *s) } } -/* 解包前逐条检查归档成员类型(tar -tv 首列):只允许普通文件 '-' 与目录 'd'。 - * 符号链接/硬链接/设备/FIFO 一律拒绝(防「先建链、后续成员写穿到目录外」)。 - * 名字穿越交由 tar 自身剥离(busybox 1.31 真机实测:'../x'→'x'、'/x'→'x'); - * 解包后再用 stg_tree_ok() 复核暂存树。 - * 返回 0 通过 / -2 成员类型非法 / -3 空归档 / -1 读失败。 */ -static int mpk_check_members(const char *mpk) +/* 解析一行 `tar -tv`:`