ykxmz 1.5.0锛氫慨澶嶆墜琛ㄧ TLS 璇佷功鏍¢獙 + MTU 淇缃戠粶绋冲畾鎬?

琚帶绔?- 淇 MTU 闂锛氬す MSS + 璺緞 MTU 鎺㈡祴锛堝叕缃戝彲鐢ㄧ巼 48% -> 100%锛?- 澶嶇敤 HTTP 杩炴帴锛堝厤鎺夋瘡娆℃柊寤?TLS 鐨?30% 鎻℃墜澶辫触锛?- 姣忓垎閽熷洖澶存壘灞€鍩熺綉锛岃嚜鍔ㄥ垏鍥炲揩 400 鍊嶇殑璺緞
- 鐢婚潰蹇冭烦甯э細闈欐鏃剁渷娴侀噺锛屽鎴风浠嶇煡閬撴祦鏄椿鐨?- 閿佸睆鏃剁粰鍑哄彲鎿嶄綔鐨勬彁绀?
鎵嬭〃绔?- 鍐呯疆 GTS Root R4 / GlobalSign Root CA锛屼慨 SSLHandshakeException
- 杩炴帴閿欒缈昏瘧鎴愪汉璇濓紙璇佷功/鏃堕棿/瑙f瀽/瓒呮椂鍒嗗埆鎻愮ず锛?- 缁忎腑杞繛涓婂悗鐢ㄦ湇鍔″櫒鎶ョ殑鍦板潃鍒锋柊宸查厤瀵硅褰?- 鏂板灞€鍩熺綉杩炴帴椤甸潰銆佹湇鍔″櫒寤惰繜妫€娴?
鏈嶅姟鍣ㄧ
- 鏀寔 chunked 璇锋眰浣擄紙淇瘡 16 绉掍竴娆$殑杩炴帴閿欎贡涓庡ぇ闈㈢Н 502锛?- 缃戠粶璋冧紭鑴氭湰 net-tune.sh锛堝紑鏈鸿嚜鍔ㄥ簲鐢級
- 绠$悊椤碉細鎵归噺鍒犻櫎銆佺綉缁滀綋妫€銆佷笅杞藉尯
- 闅ч亾鏀?HTTP/2 + 鍙岃繛鎺?
鏂囨。
- 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
This commit is contained in:
xmz7891
2026-10-06 19:01:05 +08:00
commit b705216e4a
3195 changed files with 203292 additions and 0 deletions
+292
View File
@@ -0,0 +1,292 @@
# -*- coding: utf-8 -*-
"""修 Android 端审计出来的问题(第二版:修掉第一版脚本自身的 bug)。
严重
1. 4 个按钮永久失效(动作名与服务端对不上)
2. RemoteClient 读取循环 NPE(未捕获会杀掉整个进程)
3. Json 解析无深度上限(局域网任意主机可远程触发栈溢出)
4. 对话框没判 isFinishing(退出后弹窗崩)
5. readAll 无体积上限(大文件 OOM,而 OOM 是 Error 抓不住)
6. moveTabIndicator 无限自我重投(主线程空转、发烫)
中等
7. connect 握手失败不 close(socket 泄漏 + 误报已连接)
8. pids 越界
9. 断线不清功能缓存
"""
from __future__ import annotations
import sys
from pathlib import Path
SRC = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\android\src"
r"\com\lanremote\watch")
MAIN = SRC / "MainActivity.java"
CLIENT = SRC / "RemoteClient.java"
JSON = SRC / "Json.java"
FIXES = [
# 1. 动作名
(MAIN, 'media("play_pause")', 'media("playpause")', "播放/暂停 动作名"),
(MAIN, 'media("vol_up")', 'media("volup")', "音量+ 动作名"),
(MAIN, 'media("vol_down")', 'media("voldown")', "音量- 动作名"),
(MAIN, 'confirmPower("reboot", "重启")', 'confirmPower("restart", "重启")',
"重启 动作名"),
# 2. 读取循环 NPE + 单行上限
(CLIENT,
''' try {
String line;
while (running && (line = reader.readLine()) != null) {''',
''' try {
// close() 会把 reader 置 null,而 running 判断和 readLine 之间有竞态。
// 先取到局部变量并判空 —— 读取线程里的未捕获 NPE 会杀掉整个进程。
BufferedReader localReader = this.reader;
if (localReader == null) {
return;
}
String line;
while (running && (line = localReader.readLine()) != null) {
if (line.length() > MAX_LINE_CHARS) {
continue; // 对端发超长单行,丢掉防止占满内存
}''',
"读取循环 NPE + 单行上限"),
# 3. connect 失败要 close
(CLIENT,
''' Map<String, Object> reply = call(handshake, timeoutMs);
if (Json.bool(reply, "ok", false)) {''',
''' Map<String, Object> reply;
try {
reply = call(handshake, timeoutMs);
} catch (IOException failed) {
// 握手失败必须关掉,否则 socket 和读取线程泄漏,
// 而且 isConnected() 一直是 true,界面以为已经连上了。
close();
throw failed;
}
if (Json.bool(reply, "ok", false)) {''',
"connect 失败 close"),
# 4. Json 深度上限(字段)
(JSON,
''' private static final class Parser {
private final String src;
private int pos;
''',
''' private static final class Parser {
/** 最大嵌套深度。不加限制的话,一行 "[[[[…" 就能把栈撑爆,
* 而 UDP 发现的入口是局域网里任何主机都能打的,等于远程 DoS。 */
private static final int MAX_DEPTH = 64;
private final String src;
private int pos;
private int depth;
''',
"Json 深度字段"),
# 5. Json 深度检查
(JSON,
''' Object readValue() {
if (atEnd()) {
throw new IllegalArgumentException("JSON 意外结束");
}
char c = src.charAt(pos);''',
''' Object readValue() {
if (atEnd()) {
throw new IllegalArgumentException("JSON 意外结束");
}
if (depth > MAX_DEPTH) {
throw new IllegalArgumentException("JSON 嵌套太深");
}
char c = src.charAt(pos);''',
"Json 深度检查"),
# 6. Json 深度计数:进 readObject / readArray 各加一次
(JSON,
''' private Map<String, Object> readObject() {
Map<String, Object> map = new LinkedHashMap<String, Object>();
pos++; // '{'
''',
''' private Map<String, Object> readObject() {
Map<String, Object> map = new LinkedHashMap<String, Object>();
pos++; // '{'
depth++;
''',
"Json readObject 深度+"),
# 7. 对话框判 isFinishing
(MAIN,
''' private void showChooser(String title, List<String> items, final Chooser chooser) {
final String[] array = items.toArray(new String[0]);''',
''' private void showChooser(String title, List<String> items, final Chooser chooser) {
if (isFinishing() || isDestroyed()) {
return; // 界面已经关了就别弹,否则 BadTokenException
}
final String[] array = items.toArray(new String[0]);''',
"showChooser 判 isFinishing"),
# 8. readAll 体积上限
(MAIN,
''' private byte[] readAll(Uri uri) {
InputStream input = null;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
input = getContentResolver().openInputStream(uri);
if (input == null) {
return null;
}
byte[] chunk = new byte[64 * 1024];
int read;
while ((read = input.read(chunk)) > 0) {
buffer.write(chunk, 0, read);
}''',
''' /** 单个文件最大 32MB。整文件读进内存再 Base64 会放大好几倍,
* 选个大视频就直接 OOM —— 而 OOM 是 Error,catch(IOException) 抓不住。 */
private static final int MAX_UPLOAD_BYTES = 32 * 1024 * 1024;
private byte[] readAll(Uri uri) {
InputStream input = null;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
input = getContentResolver().openInputStream(uri);
if (input == null) {
return null;
}
byte[] chunk = new byte[64 * 1024];
int read;
while ((read = input.read(chunk)) > 0) {
if (buffer.size() + read > MAX_UPLOAD_BYTES) {
return null; // 超限,上层会提示文件太大
}
buffer.write(chunk, 0, read);
}''',
"readAll 体积上限"),
# 9. moveTabIndicator 有限重试
(MAIN,
''' if (target.getWidth() == 0) {
// 布局还没完成,等一帧再试
tabIndicator.post(new Runnable() {
@Override
public void run() {
moveTabIndicator(target);
}
});
return;
}''',
''' if (target.getWidth() == 0) {
// 布局还没完成,等一帧再试 —— 但必须有限次。
// 面板被 GONE 掉时父容器不再 layout,宽度永远是 0,
// 无条件重投会让主线程空转,100% CPU 发烫耗电。
if (indicatorRetries >= 5) {
indicatorRetries = 0;
return;
}
indicatorRetries++;
tabIndicator.post(new Runnable() {
@Override
public void run() {
moveTabIndicator(target);
}
});
return;
}
indicatorRetries = 0;''',
"moveTabIndicator 有限重试"),
(MAIN,
" private View tabIndicator;",
" private View tabIndicator;\n private int indicatorRetries = 0;",
"indicatorRetries 字段"),
# 10. pids 越界
(MAIN,
''' public void onPick(int index) {
confirmKill(names.get(index), pids.get(index));
}''',
''' public void onPick(int index) {
// pids 可能比 names 短(服务端漏发或被截断),
// 不校验直接 get 会崩在 UI 线程
if (index < 0 || index >= names.size()) {
return;
}
String pid = index < pids.size()
? pids.get(index) : "";
confirmKill(names.get(index), pid);
}''',
"pids 越界保护"),
]
def apply(path: Path, old: str, new: str, tag: str) -> bool:
if not path.is_file():
print(f" [跳过] {tag} —— 文件不存在")
return False
text = path.read_text(encoding="utf-8")
if old not in text:
print(f" [跳过] {tag} —— 锚点没找到")
return False
if text.count(old) > 1:
print(f" [注意] {tag} 匹配 {text.count(old)} 处,只改第一处")
path.write_text(text.replace(old, new, 1), encoding="utf-8")
print(f" {tag}: OK")
return True
def main() -> int:
print("应用修复:")
applied = 0
for path, old, new, tag in FIXES:
if apply(path, old, new, tag):
applied += 1
# ---- RemoteClient:加单行长度常量 ----
ctext = CLIENT.read_text(encoding="utf-8")
if "MAX_LINE_CHARS" in ctext and "private static final int MAX_LINE_CHARS" not in ctext:
ctext = ctext.replace(
" private boolean running",
" /** 单行上限,和服务端的 8MB 对齐 */\n"
" private static final int MAX_LINE_CHARS = 8 * 1024 * 1024;\n\n"
" private boolean running", 1)
CLIENT.write_text(ctext, encoding="utf-8")
print(" MAX_LINE_CHARS 常量: OK")
# ---- Json:readArray 的深度计数 + 出口减一 ----
jtext = JSON.read_text(encoding="utf-8")
if "depth++;" in jtext and jtext.count("depth++") < 2:
jtext = jtext.replace(
" private List<Object> readArray() {",
" private List<Object> readArray() {", 1)
# 找 readArray 的开头插入 depth++
marker = "pos++; // '['"
if marker in jtext:
jtext = jtext.replace(marker, marker + "\n depth++;", 1)
JSON.write_text(jtext, encoding="utf-8")
print(" Json readArray 深度+: OK")
# ---- 断线清功能缓存 ----
text = MAIN.read_text(encoding="utf-8")
hooked = 0
for anchor in (" private void doDisconnect() {",
" public void onDisconnected(final String reason) {"):
if anchor not in text:
continue
head, _, tail = text.partition(anchor)
if "featureCache = null;" in tail[:500]:
continue
newline = tail.find("\n")
insertion = ("\n // 换连另一台电脑后,「功能」页不能还显示上一台的分组\n"
" featureCache = null;\n"
" featureGroups = null;")
text = head + anchor + tail[:newline + 1] + insertion + tail[newline + 1:]
hooked += 1
if hooked:
MAIN.write_text(text, encoding="utf-8")
print(f" 断线清功能缓存: OK({hooked} 处)")
print(f"\n 应用了 {applied} 处")
return 0
if __name__ == "__main__":
sys.exit(main())