tcpfwd — TCP 端口转发 (nostdlib ARM)2026/7/2基本信息项目值源文件tcpfwd.c(248 行)编译产物tcpfwd(1864 bytes)目标平台ZTE UZ901 热点 (ARMv7, Linux 3.4.110)工具链arm-linux-gnueabihf-gcc -static -nostdlib -Os -s工作目录/tmp/(RAM) 或/mnt/userdata/(JFFS2)架构┌──────────┐ TCP ┌──────────┐ TCP ┌──────────┐ │ Client │ ←────────→ │ tcpfwd │ ←────────→ │ Target │ │ (browser)│ listen │ (hotspot)│ connect │ (server) │ └──────────┘ port └──────────┘ ip:port └──────────┘单线程、同步阻塞 poll 模型。每个连接串行处理接受 → 转发 → 关闭 → 下一个。源码结构1. 数据类型 (17-28行)structsockaddr_in{shortsin_family;ushort sin_port;uint sin_addr;charpad[8];};structpollfd{intfd;shortevents;shortrevents;};2. Syscall 包装层 (30-56行)ARM EABI 纯内联汇编r7传 syscall 号r0-r4传参数。staticlongs5(longn,longa0,longa1,longa2,longa3,longa4){registerlongr0asm(r0)a0;registerlongr1asm(r1)a1;registerlongr2asm(r2)a2;registerlongr3asm(r3)a3;registerlongr4asm(r4)a4;registerlongr7asm(r7)n;asmvolatile(svc #0:r(r0):r(r1),r(r2),r(r3),r(r4),r(r7):memory,cc);returnr0;}简化宏s3(n,a0,a1,a2)→ 3 参数 syscalls2(n,a0,a1)→ 2 参数 syscalls1(n,a0)→ 1 参数 syscallSyscall 映射宏编号说明exit(n)1退出read(f,b,l)3读文件/套接字write(f,b,l)4写文件/套接字open(p,f,m)5打开文件close(fd)6关闭 fdpoll(f,n,t)168IO 多路复用socket(d,t,p)281创建套接字bind(f,a,l)282绑定端口connect(f,a,l)283连接目标listen(f,b)284监听accept(f,a,l)285接受连接setsockopt(f,l,o,v,s)294设置选项3. 工具函数 (58-93行)writestr(fd, s)— 写字符串到 fd自动计算长度htons(x)— 主机序转网络序大小端交换parse_ip(out, pp)— 解析 IP 字符串 “1.2.3.4” →u32parse_port(pp)— 解析端口字符串 “8080” →intskip_spaces(pp)— 跳过空白字符4. 转发核心 proxy_loop (95-142行)staticvoidproxy_loop(intcfd,inttfd){charbuf[4096];while(1){structpollfdpfds[2];// 监视两个 fdpfds[0]{cfd,POLLIN};pfds[1]{tfd,POLLIN};intrpoll(pfds,2,-1);// 阻塞等待if(r0)break;// 检查错误事件if(pfds[0].revents(POLLERR|POLLHUP|POLLNVAL))break;if(pfds[1].revents(POLLERR|POLLHUP|POLLNVAL))break;// client → targetif(pfds[0].reventsPOLLIN){rread(cfd,buf,sizeof(buf));if(r0)break;// 全部写出处理短写char*bpbuf;intleftr;while(left0){intwwrite(tfd,bp,left);if(w0)break;bpw;left-w;}if(left0)break;}// target → client (同上)if(pfds[1].reventsPOLLIN){...}// 一方关闭后 break 并 close 两个 fd}close(cfd);close(tfd);}关键设计单 4KB 缓冲区两个方向共用串行 poll不会同时读写while (left 0)循环处理短写write 可能未写完所有字节任何方向 read ≤ 0 或 write ≤ 0 都断开整个连接poll 超时 -1无限等待5. 入口 _start (144-247行)_start ├── open(/tmp/tcpfwd.conf, O_RDONLY) ├── read 配置 (最大 512 字节) ├── parse: 跳过 # 注释和空行 │ └── listen_port target_ip target_port ├── socket → bind → listen ├── writestr(tcpfwd: running\n) └── loop: ├── accept → 新 client fd ├── socket → connect target ├── proxy_loop(cfd, tfd) └── 断开后回到 accept配置格式/tmp/tcpfwd.conf:8080 192.168.1.100 3000 # 或带注释: # 转发热点 8080 → 内网服务器 3000只解析第一行有效规则支持#行注释支持空行编译arm-linux-gnueabihf-gcc-static-nostdlib-Os-s\-otcpfwd tcpfwd.c或使用 WSL:/opt/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-gcc\-static-nostdlib-Os-s-mfloat-abisoft\-otcpfwd tcpfwd.c部署adb push tcpfwd /tmp/ adb shellchmodx /tmp/tcpfwdecho8080 192.168.1.100 3000|adb shelltee/tmp/tcpfwd.conf adb shell/tmp/tcpfwd 限制限制说明单连接串行一次只能转发一个连接完成后才 accept 下一个无 fork/线程无法并发处理多个连接无超时poll(-1) 永久阻塞僵尸连接不回收4KB 缓冲区超过 4KB 报文需多次 poll 转发单规则config 只读第一条规则config 512B文件最大 512 字节硬编码路径config 只读/tmp/tcpfwd.conf已知 Bugconfig 路径混淆: 头注释写/mnt/userdata/tcpfwd.conf代码写/tmp/tcpfwd.conf无 SO_REUSEADDR 效果: 设置了 reuse 但连接断开后有 TIME_WAIT 问题accept 空地址: 第二个参数传 0无法知道客户端 IPgcc register asm:s5使用register asm(rX)在-Os下跨函数调用可能被优化出错GCC 11 已知问题文档日期: 2026-07-02相关文件: 已删除