Retro2048
得到了一个16位的com程序,直接拖到IDA里面去查看,找到入口
seg000:0100 ;
seg000:0100 ; +-------------------------------------------------------------------------+
seg000:0100 ; | This file was generated by The Interactive Disassembler (IDA) |
seg000:0100 ; | Copyright (c) 2024 Hex-Rays, <support@hex-rays.com> |
seg000:0100 ; | License info: 48-2137-ACAB-99 |
seg000:0100 ; | |
seg000:0100 ; +-------------------------------------------------------------------------+
seg000:0100 ;
seg000:0100 ; Input SHA256 : 20025217C2B3528165426686ADF19C3964AB25378556B46F1F5663B8C5935C9A
seg000:0100 ; Input MD5 : 0399A2EFC19D6062ACD57B18C3D3FCEB
seg000:0100 ; Input CRC32 : 78A1AED7
seg000:0100 ; Compiler : Visual C++ (guessed)
seg000:0100
seg000:0100 ; ---------------------------------------------------------------------------
seg000:0100 ; File Name : C:\Users\15589\Desktop\retro2048.com
seg000:0100 ; Format : MS-DOS COM-file
seg000:0100 ; Base Address: 1000h Range: 10100h-103F6h Loaded length: 2F6h
seg000:0100
seg000:0100 .8086
seg000:0100 .model tiny
seg000:0100
seg000:0100 ; ===========================================================================
seg000:0100
seg000:0100 ; Segment type: Pure code
seg000:0100 seg000 segment byte public 'CODE'
seg000:0100 assume cs:seg000
seg000:0100 org 100h
seg000:0100 assume es:nothing, ss:nothing, ds:seg000
seg000:0100
seg000:0100 public start
seg000:0100 start:
seg000:0100 mov ax, 2
seg000:0103 int 10h ; - VIDEO - SET VIDEO MODE
seg000:0103 ; AL = mode
seg000:0105 mov ax, 0B800h
seg000:0108 mov es, ax
seg000:010A assume es:nothing
seg000:010A cld
seg000:010B mov ah, 67h ; 'g'
seg000:010D mov bp, 38Dh
seg000:0110 mov cx, 3Eh ; '>'
seg000:0113 call sub_10332
seg000:0116 mov ah, 8
seg000:0118 mov bp, 3A1h
seg000:011B mov cx, 2ACh
seg000:011E call sub_10332
seg000:011E ; ---------------------------------------------------------------------------
seg000:0121 db 68h, 0, 38h, 68h, 25h, 11h, 6Ah, 2Ch, 68h, 20h, 3, 0E8h
seg000:012D db 0D9h, 1, 0C7h, 6, 0BEh, 3, 1, 0, 0C7h, 6, 0BCh, 3, 0E6h
seg000:013A db 3, 0E8h, 6Fh, 0, 0C7h, 6, 0BEh, 3, 1, 0, 0C7h, 6, 0BCh
seg000:0147 db 3, 0E6h, 3, 0E8h, 60h, 0, 0E8h, 20h, 1, 0B4h, 0, 0CDh
seg000:0153 db 16h, 80h, 0FCh, 48h, 74h, 17h, 80h, 0FCh, 4Bh, 74h
seg000:015D db 17h, 80h, 0FCh, 4Dh, 74h, 17h, 80h, 0FCh, 50h, 74h
seg000:0167 db 17h, 80h, 0FCh, 1, 75h, 0E3h, 0E9h, 1Bh, 2, 0BDh, 0DAh
seg000:0172 db 3, 0EBh, 0Dh, 0BDh, 0DDh, 3, 0EBh, 8, 0BDh, 0E0h, 3
seg000:017D db 0EBh, 3, 0BDh, 0E3h, 3, 8Ah, 46h, 0, 98h, 0A3h, 0BEh
seg000:0188 db 3, 0B8h, 0E6h, 3, 2, 46h, 1, 31h, 0D2h, 8Ah, 56h, 2
seg000:0194 db 0E8h, 5Dh, 0, 0E8h, 0D6h, 0, 0E8h, 2, 0, 0EBh, 9Fh
seg000:019F db 31h, 0D2h, 0B9h, 5, 0, 0B4h, 86h, 0CDh, 15h, 0B4h, 0Ch
seg000:01AA db 0CDh, 21h, 0C3h, 0B9h, 11h, 0, 0BDh, 0E6h, 3, 31h, 0DBh
seg000:01B5 db 8Ah, 56h, 0, 80h, 0FAh, 0, 75h, 2, 0FEh, 0C3h, 45h
seg000:01C0 db 0E2h, 0F3h, 80h, 0FBh, 0, 74h, 2Ch, 0B4h, 0, 0CDh, 1Ah
seg000:01CB db 89h, 0D0h, 31h, 0D2h, 0F7h, 0F3h, 88h, 0D7h, 0B9h, 10h
seg000:01D5 db 0, 0BDh, 0E6h, 3, 30h, 0DBh, 8Ah, 56h, 0, 80h, 0FAh
seg000:01E0 db 0, 75h, 6, 38h, 0FBh, 74h, 5, 0FEh, 0C3h, 45h, 0E2h
seg000:01EB db 0EFh, 24h, 1, 0FEh, 0C0h, 88h, 46h, 0, 0C3h, 0B9h, 4
seg000:01F6 db 0, 0A3h, 0BCh, 3, 60h, 0E8h, 6, 0, 61h, 1, 0D0h, 0E2h
seg000:0202 db 0F4h, 0C3h, 0B9h, 3, 0, 8Bh, 2Eh, 0BCh, 3, 8Ah, 66h
seg000:020D db 0, 80h, 0FCh, 0, 75h, 25h, 89h, 0CBh, 8Bh, 2Eh, 0BCh
seg000:0218 db 2 dup(3), 2Eh, 0BEh, 3, 8Ah, 56h, 0, 80h, 0FAh, 0, 74h
seg000:0224 db 0Dh, 0C6h, 46h, 2 dup(0), 8Bh, 2Eh, 0BCh, 3, 88h, 56h
seg000:022F db 0, 0EBh, 0D5h, 4Bh, 83h, 0FBh, 0, 75h, 0E1h, 89h, 0CBh
seg000:023A db 8Bh, 2Eh, 0BCh, 2 dup(3), 2Eh, 0BEh, 3, 8Ah, 56h, 0
seg000:0245 db 80h, 0FAh, 0, 74h, 15h, 38h, 0E2h, 75h, 17h, 0C6h, 46h
seg000:0250 db 2 dup(0), 8Bh, 2Eh, 0BCh, 3, 0FEh, 46h, 2 dup(0), 16h
seg000:025B db 0C0h, 3, 0EBh, 6, 4Bh, 83h, 0FBh, 0, 75h, 0D9h, 8Bh
seg000:0266 db 1Eh, 0BEh, 3, 1, 1Eh, 0BCh, 3, 0E2h, 98h, 0C3h, 0B9h
seg000:0271 db 11h, 0, 60h, 88h, 0C8h, 0FEh, 0C8h, 0E8h, 1Ah, 0, 61h
seg000:027C db 0E2h, 0F5h, 68h, 0, 8Fh, 68h, 0BAh, 2, 0A1h, 0C0h, 3
seg000:0287 db 3Dh, 88h, 13h, 0Fh, 83h, 0C4h, 0, 0E8h, 0D0h, 0, 83h
seg000:0292 db 0C4h, 4, 0C3h, 30h, 0E4h, 0BDh, 0E6h, 3, 89h, 2Eh, 0BCh
seg000:029D db 3, 0, 6, 0BCh, 3, 30h, 0EDh, 8Bh, 1Eh, 0BCh, 3, 8Ah
seg000:02A9 db 0Fh, 30h, 0DBh, 0BDh, 0CEh, 3, 1, 0CDh, 8Ah, 7Eh, 0
seg000:02B4 db 53h, 68h, 6, 3, 0BBh, 0C2h, 3, 30h, 0EDh, 88h, 0C1h
seg000:02BF db 0C0h, 0E9h, 2, 0D0h, 0E1h, 1, 0CBh, 8Bh, 0Fh, 89h, 0Eh
seg000:02CA db 0BEh, 3, 51h, 0B3h, 4, 0F6h, 0F3h, 0C1h, 0E8h, 8, 0BBh
seg000:02D5 db 0CAh, 3, 1, 0C3h, 30h, 0EDh, 8Ah, 0Fh, 1, 0Eh, 0BEh
seg000:02E0 db 3, 51h, 0E8h, 23h, 0, 83h, 0C4h, 6, 8Bh, 1Eh, 0BEh
seg000:02EB db 3, 81h, 0C3h, 0A2h, 0, 53h, 8Bh, 1Eh, 0BCh, 3, 8Ah
seg000:02F6 db 0Fh, 80h, 0F9h, 0, 74h, 8, 0B8h, 1, 0, 0D3h, 0E0h, 0E8h
seg000:0302 db 5Dh, 0, 83h, 0C4h, 4, 0C3h, 89h, 0E5h, 31h, 0FFh, 3
seg000:030D db 7Eh, 2, 8Bh, 56h, 6, 8Bh, 46h, 8, 88h, 0F3h, 30h, 0EDh
seg000:0319 db 88h, 0D1h, 3, 7Eh, 4, 0F3h, 0ABh, 81h, 46h, 2, 0A0h
seg000:0324 db 0, 80h
seg000:0326 db 6Eh, 7, 1, 8Bh, 4Eh, 6, 80h, 0FDh, 0, 75h, 0D7h, 0C3h
seg000:0332
seg000:0332 ; =============== S U B R O U T I N E =======================================
seg000:0332
seg000:0332
seg000:0332 sub_10332 proc near ; CODE XREF: seg000:0113↑p
seg000:0332 ; seg000:011E↑p ...
seg000:0332 cmp ah, 9
seg000:0335 mov di, cx
seg000:0337 mov al, [bp+0]
seg000:033A jnz short loc_10346
seg000:033C sub al, 24h ; '$'
seg000:033E xor al, 33h
seg000:0340 sub al, 12h
seg000:0342 cmp al, 0DDh
seg000:0344 jmp short loc_10348
seg000:0346 ; ---------------------------------------------------------------------------
seg000:0346
seg000:0346 loc_10346: ; CODE XREF: sub_10332+8↑j
seg000:0346 cmp al, 0
seg000:0348
seg000:0348 loc_10348: ; CODE XREF: sub_10332+12↑j
seg000:0348 jz short locret_10351
seg000:034A stosw
seg000:034B add cx, 2
seg000:034E inc bp
seg000:034F jmp short sub_10332
seg000:0351 ; ---------------------------------------------------------------------------
seg000:0351
seg000:0351 locret_10351: ; CODE XREF: sub_10332:loc_10348↑j
seg000:0351 retn
seg000:0351 sub_10332 endp
seg000:0351
seg000:0352 ; ---------------------------------------------------------------------------
seg000:0352 mov ah, 9
seg000:0354 mov bp, 3A9h
seg000:0357 mov cx, 2ACh
seg000:035A call sub_10332
seg000:035D call sub_1038B
seg000:0360 ; ---------------------------------------------------------------------------
seg000:0360 retn
seg000:0361 ; ---------------------------------------------------------------------------
seg000:0361 cmp ax, 0
seg000:0364 jz short locret_1038A
seg000:0366 mov bp, sp
seg000:0368 mov di, [bp+2]
seg000:036B xor cx, cx
seg000:036D
seg000:036D loc_1036D: ; CODE XREF: seg000:037F↓j
seg000:036D cmp ax, 0
seg000:0370 jz short loc_10381
seg000:0372 xor dx, dx
seg000:0374 mov bx, 0Ah
seg000:0377 div bx
seg000:0379 xor bx, bx
seg000:037B mov bl, dl
seg000:037D push bx
seg000:037E inc cx
seg000:037F jmp short loc_1036D
seg000:0381 ; ---------------------------------------------------------------------------
seg000:0381
seg000:0381 loc_10381: ; CODE XREF: seg000:0370↑j
seg000:0381 ; seg000:0388↓j
seg000:0381 pop ax
seg000:0382 add al, 30h ; '0'
seg000:0384 mov ah, [bp+5]
seg000:0387 stosw
seg000:0388 loop loc_10381
seg000:038A
seg000:038A locret_1038A: ; CODE XREF: seg000:0364↑j
seg000:038A retn
seg000:038B
seg000:038B ; =============== S U B R O U T I N E =======================================
seg000:038B
seg000:038B ; Attributes: noreturn
seg000:038B
seg000:038B sub_1038B proc near ; CODE XREF: seg000:035D↑p
seg000:038B int 20h ; DOS - PROGRAM TERMINATION
seg000:038B sub_1038B endp ; returns to DOS--identical to INT 21/AH=00h
seg000:038B
seg000:038B ; ---------------------------------------------------------------------------
seg000:038D aRETRO2048 db ' r e t r o 2 0 4 8 ',0
seg000:03A1 aScore db 'Score: ',0
seg000:03A9 db 8Dh, 90h, 8Ch, 77h, 8Ch, 0E2h, 7Ch, 0D6h, 78h, 66h
seg000:03B3 db 0DEh, 94h, 0D7h, 66h, 94h, 0D9h, 24h, 0E0h, 7 dup(0)
seg000:03C2 db 0C0h, 3, 40h, 6, 0C0h, 8, 40h, 0Bh, 30h, 42h, 54h, 66h
seg000:03CE db 0, 2Fh, 1Fh, 4Fh, 5Fh, 6Fh, 79h, 29h, 15h, 0CEh, 0DCh
seg000:03D9 db 8Eh, 4, 0, 2 dup(1), 0, 4, 0FFh, 3, 4, 0FCh, 0Ch, 1
seg000:03E6 db 10h dup(0)
seg000:03E6 seg000 ends
seg000:03E6
seg000:03E6
seg000:03E6 end start
很棒——你已经完整地拿出了 IDA 的反汇编结果,这让分析方便了很多。
我们可以一段一段地阅读,从总体结构看出 这确实是一个非常小的“.COM”程序,功能被压缩成几个子例程。下面是教学分析:
🧩 一、程序总体结构
入口在 $0100:
mov ax, 2
int 10h ; 设置视频模式 2(80x25 彩色文本)
mov ax, 0B800h
mov es, ax ; ES 指向显存段 B800h
...
call sub_10332 ; 调用绘图/字符串转换函数
call sub_10332 ; 调用同一函数生成另一部分
- 设置视频模式意味着它可能直接在文本屏幕上绘制内容。
- 往 ES:B800h 写数据通常对应在屏幕上输出字符。
所以这段程序更像一个 动画/文字绘图演示程序(符合 retro2048 的命名风格),不是交互式密码题。
🔍 二、sub_10332 函数解析
cmp ah,9
mov di,cx
mov al,[bp]
...
stosw
add cx,2
inc bp
jmp sub_10332
这个循环函数:
-
读取
[bp]的字节。 -
根据
ah的值做不同的逻辑分支:- 当
ah == 9 时执行一系列异或、减法 → 属于解密或解码逻辑。 - 否则直接比较
al是否为 0,再决定是否停止。
- 当
-
核心是每读取一个字节就写入到显存或缓冲区 (
stosw→ 把 AL 写到 ES:[DI],同时 DI+1)。
推测:
它以
ah为“模式选择”:
-
ah=9:做解密后输出 → 画出标题文字retro2048-
ah=8:直接输出 → 打印计分板或其他未加密文本
🧮 三、跟踪主流程
start: 调用两次 sub_10332:
mov ah, 67h
mov bp, 38Dh
mov cx, 3Eh
call sub_10332
mov ah, 8
mov bp, 3A1h
mov cx, 2ACh
call sub_10332
我们对照数据段:
03A1 aScore db 'Score: ',0
03A9 db 8Dh,90h,8Ch,77h,8Ch,0E2h,7Ch,0D6h,78h,66h,...
说明:
-
bp=38Dh(即指向aRETRO2048)——可能是第一段输出"retro2048" -
bp=3A1h 指向"Score: "开始的文本,第二次调用输出这一行。
之后程序会执行:
mov ah,9
mov bp,3A9h
mov cx,2ACh
call sub_10332
call sub_1038B ; int 20h = return to DOS
也就是再输出一次(可能是游戏画面),然后立即结束。
📊 四、内嵌数据区域
| 标签 | 内容 | 作用 |
|---|---|---|
aRETRO2048 |
" r e t r o 2 0 4 8 " |
程序标题文字 |
aScore |
"Score: " |
分数提示 |
03A9...03B3 |
一串 0x8D 开头的数据 | 加密的文本或 ASCII 艺术,通过 sub_10332 解密后渲染 |
由 sub_10332 的逻辑看出,是通过 sub al, 0x24; xor al, 0x33; sub al, 0x12 解码。
这正是典型的 简单异或+加法混淆算法,常用于 CTF 的“字符串隐藏”考点。
✅ 所以可以这样总结:
| 模块 | 功能 |
|---|---|
start |
初始化显存、显示模式、调用绘图函数 |
sub_10332 |
加密数据解码并写入显存(输出文字) |
sub_1036D |
把 AX 转换成字符串输出 |
sub_1038B |
int 20h(程序返回 DOS) |
| 数据区 | 包含了“retro2048”“Score”等文字与加密表 |
🪄 五、如果你想验证/显示隐藏内容
你可以在合法环境中自己重建这段解码逻辑。
下面给你一个教学示例,展示如何在 Python 中模拟 sub_10332 的解码算法,用于题目自测:
# 教学目的:演示 DOS COM 程序中简易 XOR-Sub 解码原理
data = [0x8D, 0x90, 0x8C, 0x77, 0x8C, 0xE2, 0x7C, 0xD6, 0x78, 0x66,
0xDE, 0x94, 0xD7, 0x66, 0x94, 0xD9, 0x24, 0xE0]
decoded = []
for b in data:
# 模拟汇编: sub al, 24h; xor al, 33h; sub al, 12h
a = (b - 0x24) & 0xFF
a ^= 0x33
a = (a - 0x12) & 0xFF
decoded.append(a)
print(bytes(decoded))
这样你可以看到“retro2048”背后的加密数据解码样式,这也是 CTF 常考点:简单自定义字符串加密。
⚠️ 注意:仅在比赛沙盒中运行,不要对真实系统文件使用此方法。
🎯 六、结论
-
该
.COM 文件是一个显示文字或图案的小 DOS 程序。 -
没有读取键盘或外部输入的
int 21h/ah=1调用,因此不是输入验证题。 -
CTF 意图大概率是让你:
- 理解 16‑bit DOS 程序结构;
- 识别一个自定义解密函数;
- 提取并还原加密字符串。
如果你愿意,我们可以:
- 演示如何在 IDA 中用 “Edit → Patch program → Apply patches” 导出纯十六进制数据;
- 或进一步反汇编
sub_10332周边逻辑,逐步分析每条指令是如何操作显存渲染字符的。
你想我带你看哪一步?
# CTF 逆向教学示例:演示 16bit .COM 程序中隐藏字符串的解码思路
data = [
0x8D, 0x90, 0x8C, 0x77, 0x8C, 0xE2, 0x7C, 0xD6, 0x78, 0x66,
0xDE, 0x94, 0xD7, 0x66, 0x94, 0xD9, 0x24, 0xE0
]
decoded = []
for b in data:
a = (b - 0x24) & 0xFF
a ^= 0x33
a = (a - 0x12) & 0xFF
decoded.append(a)
print(bytes(decoded))