2023 新年红包答案揭晓
2023癸卯年初一,笔者发布了今年的新年红包。
在这个文件当中一共包含了3个拼手气红包。你都领取到了吗?现在笔者来揭晓这些红包的正确领取方式。
红包1
红包1是本次的签到红包,领取方式非常简单:在 64位 Windows 操作系统上执行从新年红包链接上下载的可执行文件,然后点击“获取”按钮,即可看到红包1的值,顺利领取红包1。
红包2
红包2的领取其实非常简单:在DOS环境下执行上述链接下载下来的可执行文件,等待数小时程序自动穷举计算正确红包口令(以DOSBOX模拟器所需要的时间为例),即可获得红包2。
大家总说微软的向前兼容做得很好,Windows 11可以执行从Windows 1.0以来的各种GUI可执行文件。
(网图,侵删)
但其实某种程度上,微软的向后兼容也做得很好,XD。每个Windows的PE可执行文件都包括MZ头部,即一个合法的DOS程序。不过大多数时候这个DOS程序只会输出一句话:“This program cannot be run in DOS mode.”
笔者给红包2留了下面的这些提示:
使用标准的8文件名+3拓展名的命名方式,企图唤起大家一些古老的回忆
故意在 64位 Windows 模式的窗口标题下增加了
(Win 64)
的后缀作为呼应在DOS代码区段未对任何字符串进行混淆加密,可以用Hex编辑器看到在程序的开头有若干以
$
结尾的字符串,并写有(DOS Mode)
等字样。进一步地,笔者在MZ头部被链接器以0填充的部分用Hex编辑器改了一个
This program CAN be run in DOS mode!!!
作为提示。
这些提示大家发现了几个呢?
除了干等程序自动解算红包,还可以通过逆向分析的方式快速获取这个红包。
通过DOS时代的逆向分析工具,可以分析出,这个程序是在求算同余方程 2^k % 1000000007 == 743327059
。 由于DOS的INT 21h
调用的常数太大,所以导致这个O(n)
的程序需要数小时才能完成。直接写一个小程序就能快速解算出这个同余方程的解。
这个红包领取起来并不困难,不过出这个红包可是费了番功夫。后续我会再写篇文章来详细介绍下这个红包在DOS下运行的故事。
红包3
在 64位 Windows 下执行可执行文件,点击“获取”按钮,在获取红包1的同时,也能同时发起对红包3的获取请求。但是红包3没有提示正确。
通过抓包分析红包3(当然逆向分析也行),可以发现红包3以Modbus TCP协议和服务端交互。
客户端没有什么特别的逻辑,就是向服务端发送了两个0x3读请求,并根据读请求渲染到界面上。(可惜没有找到又快又容易实现的Windows GUI软件栈,这里给大家逆向分析带来了很多干扰,抱歉)
客户端0x3命令可以读取0-2三个寄存器的值,通过抓包(或者逆向分析客户端),可以猜测0-1寄存器表示红包的值,2寄存器表示红包的正确与否。
随后按照Modbus TCP协议请求Server端,分析Server端的行为。
可以发现,Server端能响应unit id为1、3的请求,应该对应红包1、3。Server端能接受0x3(读)/0x6(写单个寄存器)/0x10(写多个寄存器)三个Modbus命令。
通过一些开源的modbus client进行适当的测试,可以发现,0x6/0x10可以写入0-1寄存器更新红包的值,同时,通过对红包1所对应的unit 1进行操作,可以验证,2寄存器的值会随着0-1寄存器的变化而变化。
基于红包2的提示,可以分析出,红包3应当是需要写一个Modbus Client,穷举所有可能的红包口令。
可是这个时候又会发现,如果按照最基础的实现方式来枚举红包口令,穷举速度会非常慢:网络延迟这个常数远大于红包2的INT 21h
,下发0x10设置红包的值,然后再0x3读取寄存器2的值,需要2*RTT,这个时间会有几十到几百毫秒,这个超大的常数会让红包口令的枚举完全无法实现。
有了这个超大的常数,还能枚举吗?答案是肯定的,因为有这样两个办法解决它:
TCP连接保序,可以在一个TCP连接上连续发一串Modbus报文,使得网络延迟被隐藏;
注意到Server端会为每个TCP Connection分配上下文,可以通过多连接的方式进一步增大并行度。
经过这两个优化,现在的瓶颈就到了Server端CPU消耗和带宽了。本次红包的Modbus Server是我专门写的高性能Modbus TCP Server(后续也会另写文章介绍下这个TCP Server的实现),CPU不是瓶颈(实际监控看CPU消耗不到10%),实际的瓶颈是带宽(国内云服务带宽太贵了,我只开了2Mbps),最终大概需要2h多可以枚举到红包口令(来自唯一领到红包3的 @NickCao同学实测)。
这里分享下@NickCao同学的代码实现,他通过rust函数式计算的方式,优雅地完成了红包口令的多线程枚举:
use byteorder::BigEndian as E;
use byteorder::WriteBytesExt;
use rayon::prelude::*;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Instant;
const UNIT_ID: u8 = 3;
fn main() {
(0u32..99999999)
.into_par_iter()
.chunks(((u16::MAX - 1) / 2).into())
.for_each(|chunk| {
let now = Instant::now();
let mut buf = vec![];
for (i, x) in chunk.iter().enumerate() {
// Transaction Identifier
buf.write_u16::<E>(i as u16).unwrap();
// Protocol Identifier
buf.write_u16::<E>(0).unwrap();
// Length
buf.write_u16::<E>(11).unwrap();
// Unit Identifier
buf.write_u8(UNIT_ID).unwrap();
// Function Code
// 0x10 = Write Multiple registers
buf.write_u8(0x10).unwrap();
// Starting Address
buf.write_u16::<E>(0).unwrap();
// Quantity of Registers
buf.write_u16::<E>(2).unwrap();
// Byte Count
buf.write_u8(4).unwrap();
// Registers Value
buf.write_u32::<E>(*x).unwrap();
// Transaction Identifier
buf.write_u16::<E>(u16::MAX - i as u16).unwrap();
// Protocol Identifier
buf.write_u16::<E>(0).unwrap();
// Length
buf.write_u16::<E>(6).unwrap();
// Unit Identifier
buf.write_u8(UNIT_ID).unwrap();
// Function Code
// 0x03 = Read Holding Registers
buf.write_u8(0x03).unwrap();
// Starting Address
buf.write_u16::<E>(2).unwrap();
// Quantity of Registers
buf.write_u16::<E>(1).unwrap();
}
let mut conn = TcpStream::connect("8.130.19.255:502").unwrap();
conn.write_all(&buf).unwrap();
let mut res = vec![0; 23 * chunk.len()];
conn.read_exact(&mut res).unwrap();
res.chunks_exact(23).for_each(|inner| {
match inner {
[tid0, tid1, 0, 0, 0, 6, UNIT_ID, 0x10, 0, 0, 0, 2, _, _, 0, 0, 0, 5, UNIT_ID, 0x03, 2, 0, chk] => {
match chk {
0 => (),
1 => {
let tid = u16::from_be_bytes([*tid0, *tid1]);
println!("answer is {:?}", tid as u32 + *chunk.first().unwrap());
std::process::exit(0);
},
_ => unreachable!(),
}
},
_ => unreachable!(),
}
});
println!(
"chunk: {} processed, took {} seconds",
chunk.first().unwrap(),
now.elapsed().as_secs()
);
});
}
Server端的binary可在这里下载。代码请期待后续的文章。
结语
今年这个红包不知道有没有给大家带来收获,反正给我倒是带来了许多收获2333。在文章的最后,笔者给大家拜个晚年,祝大家兔年吉祥~
- 原文作者:ShadowMov's Blog
- 原文链接:https://shadowmov.com/posts/2023-redpack-write-up/
- 版权声明:本作品采用CC BY-SA 4.0. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。