为什么先讲这些

逆向这件事,工具可以换、题可以刷,但有一层东西不钉死就会一直翻车:你看到的长度、地址、数字,到底是多少字节。

这一讲就是把这个地基打平,然后讲拿到一个陌生文件的第一步该做什么。


一、只有一句话需要背:1 字节 = 8 位

其余全是推论,而且不同软件会故意让你记错。

Windows 自带的计算器切到「程序员」模式是入门阶段最划算的一个动作:它能同时显示 2 / 8 / 10 / 16 进制,每一位都能点着切换,还能看到 B(字节)、W(字)、D(双字)、Q(四字)这几个档位。

  • 1 byte = 8 bit
  • 1 word = 2 byte
  • 1 dword = 2 word = 4 byte(D 就是 double)
  • 1 qword = 4 word = 8 byte(Q 是 quad,没有"八字"这个说法)

十六进制那层是额外的:一个十六进制位对应 4 个二进制位,两个十六进制位对应一个字节。 初学时把"十六进制"和"字节"混在一起看,后面读内存会一直算错。


二、按空格数长度,一定会翻车

这是这一讲最想留给你的一条。

十六进制编辑器、调试器、反汇编器里,数字之间会打空格或者画竖线。那个分隔是软件自己的显示习惯,不是数据的边界。 同一个字节序列,A 软件四个一分、B 软件八个一分,全都不一样。

三个具体的坑:

1)8 + 24 不是 8 位加 24 位。 调试器里会看到类似"位"的写法把一段数据标成 8 和 24。算字节数得自己除回来:(8 + 24) / 8 = 4 字节。它标的是位,你要的是字节。

2)64 位下堆的地址长一倍,栈却是"地址和内容都长一倍"。 因为栈里要放指针——返回地址、参数、局部变量的地址,全是指针,指针在 64 位下从 4 字节变成 8 字节。所以栈上每个槽位都变宽了,不只是地址变长。寄存器同理。

地址栏本身的长度也好算:64 位地址是 16 个十六进制字符(64 / 4 = 16),32 位是 8 个。注意是除以 4 不是除以 8,因为一个十六进制字符是 4 位。

3)别信显示,信"1 字节 = 8 位"这一句。 剩下的都现场换算。


三、溢出、符号位,以及为什么负数多一个值

在计算器里输 127 再 +1,结果变成 -128。切到二进制看原因很清楚:127 是 0111 1111,加 1 进位成 1000 0000,而最高位是 1 就表示它是负数。

这就是补码(反码是它的中间概念):计算机没有正负号这个符号,就把最高位借来当符号位。

由此得到一个容易忽略的事实:有符号范围里负数比正数多一个。8 位有符号是 -128 ~ 127,因为 0 被算在正的那一边,负数从 -1 开始排。以后遇到"长度刚好差一个"的越界,多半就是这个。

无符号则是 0 ~ 255,界限和有符号不一样,但占用的长度完全相同。

顺手记几个会碰到的范围:0 ~ 255、-128 ~ 127、0 ~ 65535、-32768 ~ 32767。端口号就是 16 位无符号,所以一台机器最多开六万多个端口——这类数字范围以后会以意想不到的方式回来找你。


四、为什么题目和病毒都偏爱 32 位

这一条不是知识,是判断依据:看到一个 32 位程序不要立刻假设它简单,看到一个 64 位程序也不要假设它更新。

恶意代码偏爱 32 位,理由很实际:32 位通用性最好,32 位和 64 位机器都能跑;而它不需要 64 位带来的那些能力,做 64 位只会给自己增加负担。

CTF 题也一样,32 位居多。所以实际分析里 32 位是主战场,64 位会遇到但不占多数。


五、拿到一个文件,只看两件事

PEiD 是老牌工具,我用的是 exeinfoPE,本质没区别,都是工具,没有高下。它界面上信息不少,但真正决定你下一步动作的只有两行:

  1. 它是几位的(32 还是 64,决定你用哪个调试器、地址多长)
  2. 它有没有壳(有壳就先脱,没壳才谈得上分析)

其余都是补充。它还会告诉你用什么工具打开——通常是 OD 或 IDA。

位数藏在 PE 头里

IMAGE_OPTIONAL_HEADER.Magic:

  • 0x10B → PE32(32 位)
  • 0x20B → PE32+(64 位)

工具只是把这一处抄给你看。想知道它在读什么,打开 PE 头自己找一遍,比记工具界面有用。

静态基址这个勾,直接决定你分析得顺不顺

PE 头里有一项关于镜像基址的标记。如果它没有勾选静态基址,加载器就会给它一个随机基址——这就是 ASLR。

后果非常具体:你每次打开这个文件,地址都不一样。 IDA 和 OD 的偏移对不上,你记下来的地址下一次全废。

所以做题时一个很实用的动作:尽量让程序落在固定基址上。看到基址是 0x400000 这一类开头的地址,说明它是静态基址、固定在这里,这是出题人给你留的方便,他不会折磨你;没有这个方便,你就得自己处理偏移漂移。

节表:只需要知道每个段装什么

  • .text —— 可执行代码,程序写的代码就在这
  • .data —— 数据,全局变量这类
  • .rdata —— 只读数据
  • .reloc —— 重定位表,只在镜像没有落在它的优先基址时才用得上

检查节表时真正要问的是:有没有 .reloc?如果有,说明这个文件可能被搬到别的基址运行,地址会整体平移,分析时要考虑重定位。

资源段先放着

资源是另一套东西。CTF 逆向题里用到资源的很少;真正常用资源的是病毒分析(图标、清单、内嵌载荷经常塞在资源里)。遇到再说,检索方向就三个词:怎么找资源、怎么提取资源、怎么改资源。


六、关于壳:有名字的才叫壳

这一讲只到"识别出有壳"为止,脱壳单独讲。但有一个概念值得先说清楚。

最常见的就是 UPX,或者 UPX 魔改一下——去掉一点 UPX 头,导致一键脱壳失效。UPX 官方自带加壳和脱壳两端,所以魔改的要看它改掉了什么。

为什么"壳"这个词有用: 壳、自修改代码、自加密代码、虚拟机保护,本质上是同一类东西——运行时才把真正的指令放到内存里。区别只在于它有没有名字。

  • 有名字的(UPX、VMProtect 这类),你听到名字就知道该上什么工具、用什么手法。
  • 没名字的,就只能叫它"自修改代码",得自己细分它到底用了什么技术。

所以"认壳"这件事的价值不在脱壳本身,而在于它把一个模糊的障碍变成一个有标准答案的问题。这也是这一讲讲 PEiD 的原因:它给你的不是结论,是下一步该查什么。


收尾:这一讲真正要带走的四条

  1. 只背一句:1 字节 = 8 位。其余长度现场换算,别信软件画的分隔。
  2. 看到"位"要换算成"字节",(8+24)/8 = 4 这种算式要能顺手写出来。
  3. 拿到文件先看两件事:几位、有没有壳。别的都是补充。
  4. 静态基址决定你舒不舒服:基址固定在 0x400000 一类的是题目给你的方便;开了 ASLR 的,先想清楚地址会漂。

下一讲讲静态调试和动态调试——那部分才是把代码真正拆开看。

这一讲没有配套代码,但它的全部原始内容在那节课的录像里(前约 19 分钟就是这一讲,后面是下一讲),和下一讲的两个小工程一起放在 r4-reverse-course,录像是加密存的。