前言
本文有配套资源 (红色框是x64dbg)x64dbg(也可自行下载)

其实在安装32位的时候顺便把OpenSSL库弄好了
有问题欢迎评论区交流!
实验步骤
一、实验目的和要求
1. 熟悉各种PE编辑查看工具,详细了解PE文件格式;
2. 重点分析PE文件文件头、引入表、引出表,以及资源表。
二、实验内容
首先编写一个win32EXE程序,命名为hello,程序中依次弹出三个对话框,三个对话框分别为:提示框标题为“Hell0-软件安全实验-1”,内容为:“这是”+你的姓名+学号+“第一个对话框”;提示框标题为“Hell0-软件安全实验-2”,内容为:“这是”+你的姓名+学号+“第二个对话框”;提示框标题为“Hell0-软件安全实验-3”,内容为:“这是”+你的姓名+学号+“第三个对话框”。
参考程序源码:
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
int main()
{
MessageBox(NULL,"这是姓名(学号)的第一个对话框","Hell0-软件安全实验-1",MB_OK);
MessageBox(NULL,"这是姓名(学号)的第二个对话框","Hell0-软件安全实验-2",MB_OK);
MessageBox(NULL,"这是姓名(学号)的第三个对话框","Hell0-软件安全实验-3",MB_OK);
getchar();
return 0;
}
三、实验前准备
1. 安装编译器工具链(MinGW-w64)(稍等后面有32位的!)
VS Code 本身不能编译代码,你需要先安装一个 Windows 下的 C 语言编译器。
- 下载 MinGW-w64:
- 你可以通过 MSYS2 官网下载安装器,这是目前在 Windows 上管理 MinGW 最推荐的方式。
- 或者直接搜索下载 "MinGW-w64" 的预编译版本。
- 配置环境变量:
- 安装完成后,找到 mingw64\\bin 文件夹(例如 C:\\msys64\\mingw64\\bin)。
- 将这个路径添加到系统的 PATH 环境变量中。
- 验证:打开一个新的命令提示符(CMD),输入 gcc –version。如果出现版本信息,说明配置成功。
2. 配置 VS Code 工作区
3. 编写代码与编译(核心步骤)
你需要通过 VS Code 的终端来执行编译命令。
打开终端:在 VS Code 菜单栏点击 终端 (Terminal) -> 新建终端 (New Terminal)。
输入编译命令:
- 在终端中输入以下命令并回车: gcc -m32 -o hello.exe hello.c -lgdi32
参数解释:
- gcc: 调用编译器。
- -m32: 非常重要。强制生成 32 位程序(实验要求的 Win32 程序),方便后续用 OllyDbg 分析。
- -o hello.exe: 指定输出文件名为 hello.exe。
- hello.c: 源文件名。
- -lgdi32: 链接库。因为 MessageBox 函数属于 Windows 的 GDI 子系统,必须加上这个链接参数,否则会报错 "undefined reference"。
运行程序:
- 编译成功后,终端中会生成 hello.exe。
- 输入 .\\hello.exe 运行程序,检查是否弹出了三个对话框。
4. 常见问题解决
- 报错:MessageBox 未定义:
- 确保你包含了头文件 #include <windows.h>。
- 确保编译命令末尾加了 -lgdi32。
- 报错:gcc 不是内部或外部命令:
- 说明 MinGW 没有正确安装或环境变量没配好,请回到步骤 1 检查。
由于这个实验要用 OllyDbg 分析,所以要强制生成 32 位程序,下载msys2的32位版本
或者直接下载x64dbg:x64dbg

(我这是已经下载了64位编译的没有32位的所以下载一下)嫌麻烦的话,直接下x64dbg
pacman -S mingw-w64-i686-toolchain

有问题的话,嫌麻烦直接搞镜像
在 MSYS2 终端 中执行命令来一键完成配置(不需要手动去文件夹里改文件,超方便!!):
echo "Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686" > /etc/pacman.d/mirrorlist.mingw32

注意: 如果你也需要配置 64 位环境或基础系统环境,可以顺便执行这两行:
# 配置 64 位源
echo "Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64" > /etc/pacman.d/mirrorlist.mingw64
# 配置基础系统源
echo "Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/\\$arch" > /etc/pacman.d/mirrorlist.msys
刷新数据库并安装
配置写好后,执行更新命令,你会发现速度飞快:
pacman -Syu
运行完了msys2直接就关闭了(反正我是这样)
可能下面这种方式直接搜不到:
![]()
只要去资源管理器里(就是文件夹里)藏在犄角旮旯里


有需要的话,直接创个快捷方式啥的,都放一起

以下是我在下载32位版本中出现的问题(没用清华源的)
可能出现的问题1:

这是网络问题,不用担心,你的安装已经成功了!
为什么会出现满屏的红色报错?
你现在应该做什么?
既然最后显示安装完成(回到了 $ 提示符),说明环境已经就绪。可以直接编译代码了
到vs的终端窗口运行
i686-w64-mingw32-gcc -fexec-charset=GBK -o hello.exe hello.c
可能会出现的问题2:
在vs code 终端执行命令还报错,(因为是要在msys2终端运行的,在vs里还要配置环境)

这个错误是因为你在 PowerShell 中执行了 i686-w64-mingw32-gcc,但系统找不到这个命令。这通常不是因为你没安装,而是因为 你当前使用的终端环境不对。
原因分析
- MSYS2 的环境隔离机制:MSYS2 提供了三种不同的终端环境(MINGW64、MINGW32、UCRT64)。
- 环境变量未加载:当你通过 pacman 安装了 32 位工具链后,这些新工具的路径只会被自动添加到 MINGW32 终端的环境中。
- 当前环境不匹配:从你的截图看,你使用的是普通的 PowerShell。在这个环境中,系统只知道默认的 64 位编译器路径,不知道刚才安装的 32 位编译器在哪里,所以报错“无法识别”。
解决方法(二选一)
方法一:使用正确的 MSYS2 终端(推荐)
方法二:在 VS Code 中配置正确的 Shell
如果你坚持要在 VS Code 的终端里操作,你需要把 VS Code 的默认终端改成 MSYS2 的 MINGW32 环境,或者手动将 32 位编译器的路径添加到系统环境变量 PATH 中(比较麻烦,不推荐新手操作)。
可能出现的问题3:

在开始菜单中找不到“MSYS2 MINGW32”这个终端入口。这通常是因为你的 MSYS2 安装版本较新,或者在安装时没有勾选创建所有终端的快捷方式。不过别担心,你依然可以成功编译 32 位程序,只是需要换一种方式来启动正确的环境。
✅ 正确解决方法(无需重新安装)
既然你已经通过 pacman -S mingw-w64-i686-gcc 安装了 32 位编译器,那么它的可执行文件 i686-w64-mingw32-gcc.exe 一定存在于 C:\\msys64\\mingw32\\bin\\ 目录下。你只需要手动启动一个能加载该路径的环境即可。
方法一:使用 VS Code 终端 + 手动设置环境变量(推荐)
然后直接运行编译命令:
i686-w64-mingw32-gcc -fexec-charset=GBK -o hello.exe hello.c
💡 这个方法简单直接,适合临时使用。每次打开新终端都需要重新执行第 3 步。
方法二:创建一个自定义的“MINGW32”终端快捷方式
方法三:在 VS Code 中配置自定义终端配置文件
"MSYS2 MINGW32": {
"path": "C:\\\\msys64\\\\usr\\\\bin\\\\bash.exe",
"args": ["–login", "-c", "export PATH=/mingw32/bin:$PATH; exec bash"]
}
},
"terminal.integrated.defaultProfile.windows": "MSYS2 MINGW32"
保存后,重启 VS Code,以后每次打开终端都会自动进入 MINGW32 环境。
📌 为什么会出现这个问题?
- 新版 MSYS2 默认不创建所有终端快捷方式:为了简化安装,新版本可能只创建了最常用的 MINGW64 和 UCRT64 终端。
- 环境变量隔离设计:MSYS2 的设计哲学是每个终端环境独立,避免不同架构的工具链互相干扰。因此,32 位工具不会自动出现在 64 位终端或系统全局 PATH 中。
✅ 最终验证
无论你选择哪种方法,只要在终端中能成功执行:
i686-w64-mingw32-gcc –version
并看到类似 i686-w64-mingw32-gcc (RevX, Built by MSYS2 project) XX.X.X 的输出,就说明环境已就绪,可以正常编译 32 位程序了。
可能出现的问题4:

在 PowerShell 中手动修改 $env:PATH 后,仍然无法识别 i686-w64-mingw32-gcc 命令。这并非因为路径没加对,而是因为 PowerShell 的会话环境变量是临时的,且 MSYS2 的工具链依赖其自身环境初始化机制,仅靠添加 PATH 并不足以让系统正确加载所有依赖。
问题根源分析
- MSYS2 的环境隔离机制:MSYS2 的 MINGW32 终端不仅添加了 /mingw32/bin 到 PATH,还会设置一系列关键环境变量(如 MINGW_PACKAGE_PREFIX、PKG_CONFIG_PATH、C_INCLUDE_PATH 等),这些变量是编译器正常工作的前提。
- PowerShell 的局限性:即使你手动将 C:\\msys64\\mingw32\\bin 加入 $env:PATH,PowerShell 也不会自动加载 MSYS2 的环境配置文件(如 /etc/profile 或 ~/.bashrc),导致编译器虽然“可见”,但缺少运行时依赖,最终仍会失败或行为异常。
- 命令未生效的可能原因:你可能在执行 $env:PATH = … 后,没有重新打开终端或没有在当前会话中立即执行编译命令;或者路径拼写错误(如大小写、斜杠方向)。
✅ 终极解决方案(推荐)
方法一:使用 VS Code 的“自定义终端”功能(最稳定)
"terminal.integrated.profiles.windows": {
"MSYS2 MINGW32": {
"path": "C:\\\\msys64\\\\usr\\\\bin\\\\bash.exe",
"args": ["–login", "-c", "export PATH=/mingw32/bin:$PATH; exec bash"]
}
},
"terminal.integrated.defaultProfile.windows": "MSYS2 MINGW32"
}
方法二:创建一个批处理脚本启动 MINGW32 环境
set PATH=C:\\msys64\\mingw32\\bin;%PATH%
cd /d E:\\desktop\\pe_lab
cmd /k
方法三:验证你的安装是否真正成功
在执行任何编译命令前,先确认 i686-w64-mingw32-gcc.exe 确实存在于 C:\\msys64\\mingw32\\bin\\ 目录下。你可以在 PowerShell 中输入:
Test-Path "C:\\msys64\\mingw32\\bin\\i686-w64-mingw32-gcc.exe"
如果返回 True,说明文件存在;如果返回 False,说明你之前的 pacman -S mingw-w64-i686-gcc 安装可能失败了,需要重新执行该命令并确保网络通畅。
💡 重要提醒
- 不要试图在普通 PowerShell 或 CMD 中“永久”配置 MSYS2 环境,这会破坏系统的稳定性,且容易与其他工具冲突。
- 始终优先使用 MSYS2 提供的专用终端入口,它们是经过精心配置的,能确保所有工具链正常工作。
- 如果你只是想快速编译一个 32 位程序用于调试(如 OllyDbg),方法二(批处理脚本)是最简单、最可靠的选择。
可能出现的问题5:
这张图片显示你在 CMD(命令提示符) 中尝试运行 i686-w64-mingw32-gcc,但系统依然报错:“不是内部或外部命令”。
这意味着:
你的电脑当前环境里,确实找不到这个程序。
这只有两种可能:
解决办法:
第一步:确认你是否真的安装了 32 位编译器
MSYS2 默认只带 64 位编译器,32 位的需要手动下载。请在你的 MSYS2 终端(那个黑色的窗口)里输入以下命令并回车:
pacman -S mingw-w64-i686-gcc
- 如果它开始下载并安装东西:说明你之前没装成功,或者根本没装。等它跑完(输入 y 确认),然后再去执行编译命令。
- 如果它提示 already installed(已安装):说明文件确实在硬盘里,只是你的 CMD 找不到它。请看第二步。
第二步:验证文件是否存在
在 MSYS2 终端中输入:
ls /mingw32/bin/i686-w64-mingw32-gcc.exe

- 如果有输出文件名:说明文件存在。
- 如果提示 No such file:说明第一步没装好,请重新执行第一步。
第三步:如何在 CMD 中使用它(关键!)
既然你现在的截图是 CMD 界面,而且你不想用复杂的配置,最简单的办法是使用绝对路径直接调用它。
假设你的 MSYS2 安装在默认的 C 盘根目录,请在 CMD 中输入完整的路径来编译:
C:\\msys64\\mingw32\\bin\\i686-w64-mingw32-gcc.exe -fexec-charset=GBK -o hello.exe hello.c
(注意:如果你的 MSYS2 安装在 D 盘或其他位置,请把 C:\\msys64 换成对应的盘符)
总结建议
看你的截图,你似乎在不同的终端(PowerShell、CMD、VS Code)之间反复横跳。为了避免混乱,建议你:
如果你只是想赶紧把作业交了: 直接在 MSYS2 的终端里敲 gcc hello.c -o hello.exe (默认就是 64 位),生成的 hello.exe 也是可以在 Windows 上运行的,通常不需要非得编译成 32 位(除非老师强制要求)。
可能出现的问题6:

这张图片非常关键,它揭示了你当前操作环境的一个核心问题:你正在 Windows 自带的 CMD(命令提示符) 中尝试运行 pacman,但系统找不到这个命令。
原因很简单: pacman 是 MSYS2 专用的包管理器,它只能在 MSYS2 自己的终端窗口里运行,不能在 Windows 的 CMD 或 PowerShell 里直接运行。
为了帮你彻底解决问题,我们需要分两步走:先找到正确的“入口”,再安装编译器。
解决方法
第一步:打开正确的“MSYS2 终端”
你不能直接在桌面的 CMD 里敲命令。请按照以下步骤找到 MSYS2 的专属窗口:
- 注意:不要选 CLANG64 或 UCRT64,除非你明确知道自己在做什么。对于初学者,MINGW64 最通用。
第二步:在 MSYS2 终端中安装 32 位编译器
当你打开了那个黑底白字的 MSYS2 窗口后(此时光标前通常会显示 user@电脑名 MINGW64 ~),在里面输入以下命令并回车:
pacman -S mingw-w64-i686-gcc
- 如果系统提示你确认安装(Proceed with installation? [Y/n]),请输入 y 然后回车。
- 等待进度条跑完,直到出现新的命令行提示符。
第三步:如何编译你的代码
安装完成后,你有两种方式来编译你的 C 语言代码:
方式 A:在 MSYS2 终端里编译(最简单)
你需要先用 cd 命令进入你的代码所在的文件夹。假设你的代码在 E 盘的 desktop\\pe_lab 文件夹:
cd /e/desktop/pe_lab
(注意:在 MSYS2 里,E盘要写成 /e/)
然后直接使用刚才安装的编译器:
i686-w64-mingw32-gcc -fexec-charset=GBK -o hello.exe hello.c
方式 B:回到 VS Code 或 CMD 编译(如果你不想换窗口)
既然你已经通过上面的步骤安装了编译器,现在你可以回到你截图中的 CMD 窗口,使用绝对路径来调用它(假设 MSYS2 安装在 C 盘默认位置):
C:\\msys64\\mingw32\\bin\\i686-w64-mingw32-gcc.exe -fexec-charset=GBK -o hello.exe hello.c
可能出现的问题7:

你当前遇到的问题是 MSYS2 在安装 32 位 MinGW-w64 工具链时,因网络镜像源失效导致部分包下载失败(如 mingw-w64-i686-tcl-8.6.17-1-any.pkg.tar.zst 返回 404)。这通常是因为默认的国外镜像源已过期或不可用。
别担心,这个问题很常见,解决起来也很简单。核心思路是:更换为国内高速镜像源 → 重新同步包数据库 → 再次安装工具链。
解决方法
第一步:更换为国内镜像源(推荐清华源)

💡 你也可以选择中科大源:https://mirrors.ustc.edu.cn/msys2/mingw/i686/
其实我换镜像的时候是有问题的,网站问题,反正实在不行就别用olldbg,改用x64dbg
第二步:更新包数据库并重新安装
- 这次应该能顺利下载所有包,不会再出现 404 错误。
- 如果提示选择包,直接按回车全选即可。
第三步:启动正确的终端并编译
安装完成后:
验证是否成功
编译完成后,你可以用以下命令确认生成的是 32 位程序:
file hello.exe
输出应包含 PE32 executable (console) Intel 80386,表明它是标准的 32 位 PE 文件,可以被 OllyDbg 正常加载和调试。
✅ 总结:你遇到的问题本质是“网络镜像源失效”,通过更换为国内镜像源即可彻底解决。这是 MSYS2 用户在新环境部署时最常遇到的坑之一,现在你已经掌握了正确的解决方法!
出现如图所示就好了:

图片内容分析
你操作对了环境:
- 注意看终端顶部的绿色文字 MINGW64。这说明你已经成功进入了 MSYS2 的专用终端(而不是之前的 Windows CMD),这是能运行 pacman 命令的前提。
编译器已存在:
- 系统提示:warning: mingw-w64-i686-gcc-15.2.0-14 is up to date — reinstalling。
- 含义:这句话的意思是“这个软件已经是最新版了”,系统正在为你重新确认安装一遍。
- 结论:这证明 mingw-w64-i686-gcc 这个包在你的电脑里是完好无损且已安装的。
为什么之前报错?
既然装好了,为什么你在 VS Code 或 CMD 里输入 i686-w64-mingw32-gcc 还是提示“找不到命令”?
原因只有一个:环境变量没配对。
- 你现在所在的窗口是 MINGW64 环境(绿色的字)。在这个环境里,系统只认识 64 位的编译器(x86_64-w64-mingw32-gcc)。
- 你要用的 32 位编译器(i686-w64-mingw32-gcc)虽然装在硬盘里(位于 C:\\msys64\\mingw32\\bin),但当前这个 MINGW64 窗口的 PATH 变量里并没有包含这个路径。
✅ 现在的解决方案(二选一)
既然文件都在,我们不需要再下载任何东西,只需要用对方法。
方案一:切换到正确的终端(最推荐,最简单)
既然你要编译 32 位程序(i686),你就应该进入 MINGW32 终端,而不是 MINGW64。
方案二:如果你非要在当前的 MINGW64 窗口里用
如果你不想切换窗口,必须手动告诉系统去哪里找 32 位编译器。在当前窗口输入:
export PATH="/mingw32/bin:$PATH"
然后再试一次:
i686-w64-mingw32-gcc –version
这样也能成功,但每次新开窗口都要输一次上面那行代码。
建议直接使用方案一,去开始菜单找 "MSYS2 MINGW32" 打开即可解决所有问题。
若你想使用方案2的话:
出现这个图的内容就好了

你成功执行了关键命令:
- export PATH="/mingw32/bin:$PATH":这行命令的作用是把 32 位编译器的路径“临时”加入到了当前窗口的系统变量中。
- i686-w64-mingw32-gcc –version:随后你尝试运行编译器查看版本。
编译器已成功识别并运行:
- 屏幕输出了 i686-w64-mingw32-gcc.exe … 15.2.0。
- 这意味着: 你的电脑里确实有 32 位编译器,而且通过手动修改环境变量,它现在已经可以被调用了!
🚀 接下来怎么做?(两种选择)
选择一:立刻编译代码(就在当前窗口)
既然现在环境已经通了,不要关闭窗口,直接在这个窗口里操作:
进入你的代码目录(假设在 E 盘 desktop 下):
cd /e/desktop/pe_lab
(注意:MSYS2 里 E 盘要写成 /e/)
执行编译命令:
i686-w64-mingw32-gcc -fexec-charset=GBK -o hello.exe hello.c
如果没报错,目录下就会生成 hello.exe。

选择二:一劳永逸(推荐,以后不用每次都输 export)
为了以后不再折腾,建议你以后直接打开正确的终端。
总结:你的环境没问题,文件也没丢,只是之前没找对“门”。现在你已经找到了钥匙(export 命令),可以开始写代码了!
四、实验结果(及调试过程)
步骤一:编译c语言文件
程序执行情况如下图所示:不报错就行了

生成exe文件

运行exe文件

可能出现的问题1:

这是一个非常典型的 字符编码不匹配(乱码) 问题。
简单来说:你的 VS Code 编辑器、编译器(GCC)和 Windows 系统控制台之间,对于“如何解读中文字符”没有达成共识。
1. 为什么会出现这种情况?
- VS Code (UTF-8):现代编辑器默认使用 UTF-8 编码保存文件。在 UTF-8 中,一个汉字通常占 3 个字节。
- Windows 控制台 (GBK/GB2312):传统的 Windows 中文环境(尤其是你使用的 MinGW/GCC 编译器生成的程序)默认期望的是 GBK 编码。在 GBK 中,一个汉字占 2 个字节。
结果就是: 程序把 UTF-8 的 3 字节数据强行当成 GBK 的 2 字节数据去显示,导致字节错位,原本的文字就变成了毫无意义的乱码符号。
2. 怎么解决?(两种方法任选其一)
方法一:修改编译命令(推荐,最简单)
你需要告诉 GCC 编译器:“我的源文件是 UTF-8 编码的,请帮我转换成 Windows 能看懂的格式”。
请在 VS Code 的终端中使用以下命令重新编译:
gcc -fexec-charset=GBK -o hello.exe hello.c
- 解释:-fexec-charset=GBK 参数强制编译器将执行时的字符集转换为 GBK,这样弹出的对话框就能正常显示中文了。
- 注意:如果你之前用了 -m32,请把两个参数拼在一起: gcc -m32 -fexec-charset=GBK -o hello.exe hello.c
方法二:修改代码(使用宽字符)
这是更规范的 Windows 编程写法,但改动稍微多一点。
修改后的代码示例:
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
int wmain() // 改为 wmain
{
// 注意字符串前面的 L,表示宽字符 Unicode
MessageBoxW(NULL, L"这是 的第一个对话框", L"Hello-软件安全实验-1", MB_OK);
MessageBoxW(NULL, L"这是 的第二个对话框", L"Hello-软件安全实验-2", MB_OK);
MessageBoxW(NULL, L"这是 的第三个对话框", L"Hello-软件安全实验-3", MB_OK);
getchar();
return 0;
}
编译命令(无需额外参数):
gcc -o hello.exe hello.c
可能出现的问题2:

你遇到的错误 cannot open output file hello.exe: Permission denied 是 Windows 下使用 GCC 编译时非常常见的权限冲突问题。这不是代码错误,而是系统或进程阻止了编译器写入文件。
核心原因
你的 hello.exe 文件正被另一个程序占用(最常见的是上一次运行的程序没有关闭),或者杀毒软件、Windows Defender 正在扫描它,导致 GCC 无法覆盖写入新文件。
解决方案:关闭正在运行的程序(最常用)
- 检查任务栏或系统托盘,看是否有 hello.exe 的窗口正在运行。
- 如果有,点击“X”关闭它。
- 如果找不到窗口,按 Ctrl + Shift + Esc 打开任务管理器,在“进程”标签页中找到 hello.exe,右键选择“结束任务”。
- 重新在终端中执行编译命令: gcc -fexec-charset=GBK -o hello.exe hello.c
步骤二:PE文件格式分析

可能出现的问题1:

OllyDbg (OD) 是 32 位的老软件,而你编译出来的 hello.exe 是 64 位的程序。
OllyDbg 只能调试 32 位程序。因为你在 VS Code 里编译时没有强制指定生成 32 位程序,GCC 默认生成了 64 位版本,所以 OD 打不开它。
去看本文的第三部分,实验前的准备,已经讲的很详细了
问题1:
(1) 使用使用UltraEdit或WinHex观察PE文件例子程序hello.exe的16进制数据,在打印稿中画出该PE文件基本结构,即指出:MZ头部+DOS stub+PE文件头+可选文件头+节表+节等信息。
并回答:如何判断打开文件是PE格式文件?
使用 WinHex (或 010 Editor) 查看文件头
这一步是为了回答实验报告中关于“PE文件结构”的问题。

- 看最左上角(偏移地址 0x00),你会看到 4D 5A。
- 这就是 "MZ" 的 ASCII 码,代表这是一个可执行文件。

- 找到偏移地址 0x3C 这一行。
- 读取这里的 4 个字节(例如可能是 80 00 00 00 或 E0 00 00 00,具体取决于编译器)。
- 记录这个数值(假设是 80)。这意味着真正的 PE 头在文件的第 128 字节处。
- 按 Ctrl + G (Go to position),输入刚才看到的数值(如 80)。
- 你应该能看到 50 45 00 00,即 "PE\\0\\0"。这证实了 PE 头的起始位置。
🧩 PE 文件基本结构分析(基于图片内容)
根据 WinHex 中显示的十六进制数据和右侧 ASCII 文本,我们可以按顺序定位并标注出 PE 文件的核心组成部分:
1. MZ 头部(DOS Header)
- 位置:文件起始处,偏移量 0x00000000。
- 特征:前两个字节为 4D 5A,对应 ASCII 字符 “MZ”。
- 作用:这是所有 Windows 可执行文件的“签名”,用于标识这是一个 DOS/Windows 程序。即使现代系统不再依赖 DOS,这个头仍然保留以维持兼容性。
2. DOS Stub(DOS 存根程序)
- 位置:紧接在 MZ 头部之后,大约从偏移 0x00000040 开始。
- 特征:包含一段可读的 ASCII 字符串:“This program cannot be run in DOS mode.”
- 作用:当用户在纯 DOS 环境下运行此 .exe 文件时,会显示这条提示信息。在现代 Windows 系统中,这段代码会被跳过,直接加载真正的 PE 结构。
3. PE 文件头(PE Signature + COFF Header)
- 位置:通过 MZ 头部中的 e_lfanew 字段(位于偏移 0x0000003C,值为 B8 00 00 00,即十进制 184)跳转到该位置。
- 特征:在偏移 0x000000B8 处,可以看到四个字节 50 45 00 00,对应 ASCII “PE\\0\\0”,这是 PE 文件的正式标志。
- 作用:标志着真正 Windows 可执行文件格式的开始,后续是 COFF 文件头,描述文件的基本属性(如机器类型、节数等)。
4. 可选文件头(Optional Header)
- 位置:紧跟在 PE 签名和 COFF 头之后。
- 特征:虽然图中未完全展开,但通常在 PE 签名后约 20~24 字节处开始,包含入口点地址、镜像大小、数据目录表等关键信息。
- 作用:定义了程序如何被加载到内存、从哪里开始执行、需要哪些动态链接库等。
5. 节表(Section Table)
- 位置:在可选文件头之后,由多个固定大小的条目组成。
- 特征:图中可见 .text, .data, .rdata, .bss 等节名出现在 ASCII 区域,表明这些节的名称和属性已定义。
- 作用:每个节代表一块具有相同访问权限(读/写/执行)的数据或代码块,操作系统据此将不同部分映射到虚拟内存的不同区域。
6. 各节内容(Sections)
- .text 节:存放编译后的机器指令(代码),通常是只读且可执行的。
- .data / .rdata 节:分别存储初始化的全局变量和只读常量(如字符串)。
- .bss 节:预留空间给未初始化的全局变量,不占用磁盘空间但在运行时分配内存。
- 位置:从偏移 0x000001A0 起可见 .data,0x000001C0 起可见 .rdata,0x00000220 起可见 .bss。
❓ 如何判断一个文件是否为 PE 格式?
要确认一个文件是否为 PE 格式,只需检查以下两个关键点:
✅ 第一步:检查 MZ 签名
- 打开文件的前两个字节,若为 4D 5A(ASCII “MZ”),则说明它是一个 DOS/Windows 可执行文件的基础结构。
✅ 第二步:查找 PE 签名
- 读取 MZ 头部中偏移 0x3C 处的 4 字节值(称为 e_lfanew),它指向 PE 签名的实际位置。
- 跳转到该偏移地址,检查接下来的 4 字节是否为 50 45 00 00(ASCII “PE\\0\\0”)。
- 如果匹配,则该文件是标准的 PE 格式文件;否则可能是其他格式(如 ELF、Mach-O 或损坏文件)。
💡 示例验证:
- 图中 hello.exe 的 e_lfanew = B8 00 00 00 → 跳转至偏移 0xB8
- 在 0xB8 处确实看到 50 45 00 00 → 确认为合法 PE 文件


问题2:
(2)使用Ollydbg对该程序进行初步调试,了解该程序功能结构,在内存中观察该程序的完整结构。

并回答程序入口点位置是多少?
1. 程序入口点位置
结论: 程序的入口点(Entry Point)位于内存地址 00621460。
证据分析(基于截图):
- 右侧寄存器窗口 (Registers):
- 请看右上角的 EIP 寄存器行。
- 显示内容为:EIP 00621460 hello.<ModuleEntryPoint>。

- 解释: EIP (Extended Instruction Pointer) 寄存器始终指向 CPU 下一条将要执行的指令地址。当程序刚加载或暂停在入口处时,它指向的就是程序的入口点。OllyDbg 已经智能地将其标记为 <ModuleEntryPoint>。
- 左上角反汇编窗口 (Disassembler):
- 第一行高亮显示的指令地址正是 00621460。

- 该地址处的指令是 PUSH EBP,这是 C/C++ 编译器生成的标准函数序言(Function Prologue),标志着 main 函数或入口函数的开始。
- 底部内存转储窗口 (Dump):
- 左下角的地址栏显示当前查看的起始地址也是 00621460。

- 对应的十六进制数据 55 正是 PUSH EBP 指令的机器码。
2. 程序功能结构初步分析
通过观察 OllyDbg 中的反汇编代码和调用栈,可以推断出该程序的结构特征:
典型的控制台程序入口逻辑
虽然入口点是 00621460,但这通常是编译器生成的启动代码(CRT Startup Code,如 mainCRTStartup),而不是用户编写的 main 函数本身。
- 初始化环境: 代码开头进行了堆栈平衡操作(PUSH EBP, MOV EBP, ESP)。
- 参数获取: 随后调用了 msvcrt._getmainargs(在地址 00621438 处可见 CALL <JMP.&msvcrt._getm…>)。这个函数的作用是解析命令行参数(argc, argv),为后续的 main 函数做准备。

- 异常处理设置: 代码中包含了 SEH (Structured Exception Handling) 相关的指令(如 XOR EBX,EBX, TEST EBX,EBX, SETNE BL),这是为了设置结构化异常处理链,确保程序崩溃时能被系统捕获。
动态链接库调用
- 在反汇编窗口的下方(地址 00621487 附近),可以看到对 KERNEL32.SetUnhandledExceptionFilter 的调用引用。这说明程序正在注册一个未处理异常的过滤器,这是现代 Windows 程序的标准行为。

内存布局观察
- 代码段 (.text): 当前 EIP 所在的 0062xxxx 区域是可执行代码段。
- 数据段/堆栈: 右下角的堆栈窗口显示了当前的调用栈情况,包括 RETURN to KERNEL32…,表明程序是从操作系统加载器跳转进来的。
问题3:
3.熟悉各类PE文件格式查看和编辑工具(PEView、Stud_PE、PEditor等)
这是一个非常经典的逆向工程入门练习。通过手动分析 PE 文件结构,你能深入理解操作系统是如何加载和执行程序的。
(1)画出PE的各个关键结构和字段文件偏移地址和虚拟内存地址。
PE 文件的结构就像是一个精密的俄罗斯套娃。要理解它,必须区分“在硬盘上长什么样”(File Offset, FO)和“加载到内存后长什么样”(Virtual Address, VA/RVA)。
核心概念
- RVA (Relative Virtual Address):相对虚拟地址。即内存中的地址减去程序加载基址(ImageBase,通常是 0x00400000)。
- FO (File Offset):文件偏移。即数据在 .exe 文件中的第几个字节。
- 转换公式:VA = ImageBase + RVA。
PE 结构映射表
你可以参考下表来绘制你的结构图:
| DOS Header (MZ头) | e_magic: "MZ" (2字节) e_lfanew: 指向PE头的偏移 (4字节) | 0x0000 (固定) 0x003C (固定) | 此时还未进入内存映射,仅作为文件头存在。 |
| DOS Stub | "This program cannot be run…" | 0x0040 ~ e_lfanew | 加载到内存后通常被忽略或位于未初始化区域。 |
| PE Signature | "PE\\0\\0" | 由 e_lfanew 决定 (例如图中是 0xB8) | 标志着 PE 格式正式开始。 |
| COFF Header (标准PE头) | Machine: 机器类型 NumberOfSections: 节数量 SizeOfOptionalHeader: 可选头大小 | 紧接 PE Signature 之后 (例如 0xB8 + 4 = 0xBC) | 包含文件的基本属性,不直接参与内存执行逻辑,但指导加载器。 |
| Optional Header (可选头) | Magic: 32位(10B)或64位(20B) AddressOfEntryPoint: 入口点 RVA ImageBase: 建议加载基址 SectionAlignment: 内存对齐粒度 FileAlignment: 文件对齐粒度 | 紧接 COFF Header 之后 | 最关键的部分! 定义了代码在内存中的布局、入口点位置以及数据目录表的位置。 |
| Section Table (节表) | 每个节的描述: Name: 节名 (.text, .data…) VirtualSize: 内存中大小 VirtualAddress: 节在内存中的 RVA SizeOfRawData: 文件中大小 PointerToRawData: 节在文件中的偏移 | 紧接 Optional Header 之后 数组形式排列,数量由 COFF 头决定。 | 转换的核心桥梁! 通过对比 VirtualAddress 和 PointerToRawData,我们可以算出任意 RVA 对应的文件偏移。 |
| Sections (节数据) | .text (代码), .data (数据), .rdata (只读数据) | 由 PointerToRawData 指定 | 由 VirtualAddress 指定 加载器会将文件中的数据按 FileAlignment 读取,然后按 SectionAlignment 映射到内存。 |
示意图绘制建议
在你的打印稿上,可以画两个长条矩形,左边代表“磁盘文件”,右边代表“内存映像”,中间用箭头连接:


(2)找到程序中调用的API函数的地址。
在 PE 文件中,API 函数(如 printf, MessageBox)的地址并不是写死的,而是通过导入表 (Import Table) 动态获取的。
方法一:使用工具查看(最快)
使用 PEView 或 Stud_PE 等工具打开 hello.exe:
方法二:手动计算(原理分析)
如果你需要知道这些 API 在内存中的确切调用地址(IAT 地址),步骤如下:
定位导入表:
- 在 Optional Header 的数据目录(Data Directory)中,找到第 2 项(索引为 1),即 Import Table。
- 记下它的 RVA (例如 0x2050) 和 Size。
定位导入描述符 (Import Descriptor):
- 根据 RVA 0x2050,利用节表将其转换为文件偏移 (FO),在文件中找到 IMAGE_IMPORT_DESCRIPTOR 结构数组。
- 每个描述符对应一个 DLL。找到 OriginalFirstThunk (INT) 和 FirstThunk (IAT) 的 RVA。
定位导入查找表 (INT) / 导入地址表 (IAT):
- INT 指向的是函数名称字符串或序号。
- IAT 是程序实际调用 API 的地方。在程序刚加载还没运行时,IAT 的内容和 INT 是一样的(指向名字)。当程序运行起来后,Windows 加载器会把 IAT 里的内容覆盖成 API 在内存中的真实地址。

KERNEL32.GetModuleHandleA
- 位置: 0062148E
- 汇编指令: CALL DWORD PTR DS:[<&KERNEL32.GetModule…>]
- 参数: "libgcc_s_dw2-1.dll" (由上一行 MOV DWORD PTR SS:[LOCAL.11], OFFSET 0062… 传入)
- 功能: 获取指定模块(这里是 GCC 运行时库)在进程中的句柄。如果模块已加载,返回其基址;否则返回 NULL。

KERNEL32.LoadLibraryA
- 位置: 006214A4
- 汇编指令: CALL DWORD PTR DS:[<&KERNEL32.LoadLibra…>]
- 参数: "libgcc_s_dw2-1.dll"
- 功能: 将指定的 DLL 文件映射到调用进程的地址空间。这是典型的动态加载库的行为。
如何在工具中找到这些地址?
如果你需要自己找到这些地址,可以使用以下两种方法:
方法一:在反汇编窗口观察(如截图所示)
方法二:使用 PE 查看工具(PEView / Stud_PE)
虽然截图是调试器,但题目要求熟悉 PE 工具。要静态找到这些函数的“入口”,你需要查看 导入表。
- 在 PE Header -> Optional Header 中找到 DataDirectory[1] (Import Table)。
- 记录它的 RVA(相对虚拟地址)和 Size。
- 跳转到该 RVA 对应的文件偏移位置。
- 你会看到一个 IMAGE_IMPORT_DESCRIPTOR 结构数组。
- 找到名为 KERNEL32.dll 的描述符。
- 在该描述符下,查看 OriginalFirstThunk (INT) 或 FirstThunk (IAT) 指向的列表。
- 列表中会列出所有调用的函数名,如 GetModuleHandleA 和 LoadLibraryA。
问题4:
攻击程序:手工修改hello.exe程序,使得其只弹出第三个对话框。
要修改 hello.exe 使其只弹出第三个对话框,我们需要通过逆向分析找到控制这三个对话框的逻辑(通常是连续的 CALL 指令或跳转指令),然后修改汇编代码来跳过前两个对话框的执行。
具体的操作步骤:
1. 寻找入口点与主逻辑
首先,你需要在 OllyDbg 中找到程序的主函数(main 函数)。
- 定位入口: 打开程序后,OllyDbg 通常会停在系统领空(System Breakpoint)或者程序的入口点(OEP)。
- 进入用户代码: 按 F8(单步步过)几次,直到你看到类似 PUSH EBP, MOV EBP, ESP 这样的标准函数开头,这通常标志着进入了 main 函数。
- 识别对话框调用: 向下浏览反汇编代码,寻找调用消息框的 API。
- 如果是 ANSI 编码程序,找 CALL <JMP.&USER32.MessageBoxA>。
- 如果是 Unicode 编码程序,找 CALL <JMP.&USER32.MessageBoxW>。
- 你会看到连续出现了三次类似的 CALL 指令,分别对应第一、二、三个对话框。
2. 分析执行流程
假设你在反汇编窗口中看到了如下结构(地址仅为示例):

目标: 我们要让 CPU 直接“飞”到第 3 个对话框的代码处执行,忽略前两个。
3. 修改代码(核心步骤)
你有三种主要方法来实现这个目的。推荐使用 方法 A,因为它最简单且不容易破坏堆栈平衡。
方法 A:使用 JMP 指令跳转(推荐)
- 假设第三个对话框的 CALL 指令地址是 0040105E。
- 输入:JMP 0040105E
- 注意:如果原来的指令很短(比如只有1字节),而 JMP 需要5字节,可能会覆盖掉后面的一两条指令。只要不影响堆栈(ESP/EBP)的初始化即可。如果覆盖了 PUSH EBP,可能会导致程序崩溃。
- 更稳妥的做法: 如果 main 函数开头有堆栈初始化代码(如 PUSH EBP, MOV EBP, ESP),不要修改这几行。请找到第一个 CALL MessageBox 的位置,将其修改为 JMP 到第三个 CALL 的位置。
第一步:定位代码

- 00401050: CALL <MessageBoxA> —— 这是第1个弹窗
- … (中间可能有一些清理堆栈的代码)
- 00401060: CALL <MessageBoxA> —— 这是第2个弹窗
- …
- 00401070: CALL <MessageBoxA> —— 这是第3个弹窗(我们要保留的)


代码分析
让我们拆解一下这三段几乎一模一样的代码块:
-
第一个弹窗 (005C1561 – 005C1584):
- PUSH … OFFSET 005C404… (参数:标题/内容字符串)
- CALL EAX (调用 USER32.MessageBoxA) -> 这是第 1 个弹窗
-
第二个弹窗 (005C1589 – 005C15AC):
- PUSH … OFFSET 005C409… (参数:标题/内容字符串)
- CALL EAX (调用 USER32.MessageBoxA) -> 这是第 2 个弹窗
-
第三个弹窗 (005C15B1 – 005C15D4):
- PUSH … OFFSET 005C40E… (参数:标题/内容字符串)
- CALL EAX (调用 USER32.MessageBoxA) -> 这是第 3 个弹窗
第二步:写入跳转指令
JMP SHORT 008D15BA

原理说明:
- 目标地址 008D15BA:这是第三个 MessageBoxW 开始设置参数(SUB ESP,10)的地方。跳到这里,程序就会像正常流程一样去准备第三个弹窗的参数并弹出它。
- 指令长度:JMP SHORT 生成的机器码是 2 个字节 (EB xx)。
- 覆盖情况:它会覆盖掉原本的 PUSH EBP (1字节) 和 MOV EBP,ESP (2字节) 的一部分。虽然这破坏了标准的函数栈帧建立过程,但因为我们是直接跳过中间所有逻辑去执行后面的代码,且后面的代码并没有依赖 EBP 来寻址局部变量(它是通过 ESP 偏移或者直接传参),所以这样改是安全的,能成功只弹出第三个框。
出现的问题(很简单解决):

这个报错 "No room for this command" 的意思非常直白:空间不足。
🔍 为什么会报错?
- 你的指令太长了:你输入的 JMP SHORT 008D15BA 需要占用 2个字节(机器码是 EB 47)。
- 原来的位置太小了:调试器默认只允许你修改当前这一行指令的空间。地址 008D1560 处的原始指令是 PUSH EBP,它只有 1个字节(机器码 55)。
- 冲突:你想把一条 2字节的指令塞进 1字节的空间里,而且你还勾选了 "Keep size" (保持大小),调试器为了防止破坏下一行代码,就拒绝执行并报错。
✅ 解决方法(二选一)
方法一:取消勾选 "Keep size"(最简单,推荐)
- 原理:这样调试器就会允许写入 2个字节,虽然会吞掉下一行 MOV EBP,ESP 的一部分,但因为我们要直接跳走,被吞掉也没关系。

方法二:使用近跳转 JMP NEAR(更规范)
如果你不想取消勾选,或者想写得更“正规”一点,可以使用长跳转指令。

第三步:保存文件
选中修改区域 在反汇编窗口中,用鼠标左键点击并拖动,选中你修改过的所有代码行(例如从 008D1560 到 008D15BA 的跳转指令及后续被覆盖的代码)。
右键菜单操作 在选中的代码区域上点击 鼠标右键,在弹出的菜单中选择:
Edit → Copy to executable → All modifications

确认保存新文件 此时会弹出一个新的窗口(标题通常为 “File” 或 “Executable”),显示的是修改后程序的完整内存镜像。 在这个新窗口中,再次点击 鼠标右键,选择:
Save file…


这里点”是“就好了
为什么会出现这个弹窗? 你之前的操作生效了:你在反汇编窗口里把 JMP SHORT 008D15BA 写入到了内存中。 OD 的保护机制:当你试图关闭 OD 或者保存文件时,它会检查:“嘿,我在内存里跑的代码和你硬盘上那个原始的 exe 不一样了,你要不要把硬盘上的那个也改一下?”
输入新文件名 在弹出的文件保存对话框中,输入一个新文件名(如 hello_patched.exe),选择保存路径,点击“保存”。
方法 B:使用 NOP 填充(nop sled)
如果你不想处理复杂的跳转计算,可以用空指令填满前两个对话框的代码区域。
- 缺点:如果中间夹杂了局部变量的分配(如 SUB ESP, xx),全部 NOP 掉可能会导致后续代码访问内存出错。
第一步:确定要 NOP 掉的区域(关键)
你需要找到前两个 MessageBox 的调用位置。根据之前的分析,通常在 CALL 指令之前会有参数压栈(PUSH)。
- 通常长这样: PUSH 0 ; 参数4 (uType)
PUSH offset … ; 参数3 (Caption)
PUSH offset … ; 参数2 (Text)
PUSH 0 ; 参数1 (hWnd)
CALL USER32.MessageBoxA ; <— 关键点

- 起始点:第一个 PUSH 指令的地址。
- 结束点:第二个 CALL 指令的下一行地址(即选中包含这个 CALL 指令)。
详细分析
第一个对话框区域(红框 1)
- 起始点:001E1560 (PUSH EBP)。虽然 PUSH EBP 通常是函数开头的保护现场指令,但在这个上下文中,它是这一整块连续代码的开始。如果你想把这块彻底“抹掉”,从这里开始选是完全没问题的。
- 核心操作:中间的 MOV 指令是在准备参数(比如设置 "Hello" 字符串),最后的 CALL EAX (在 001E158F) 是真正的弹窗动作。
- 结束点:001E1591 (ADD ESP, 10)。注意! 这个 ADD ESP, 10 是用来平衡堆栈的(清理刚才压入的4个参数)。如果你把这也 NOP 掉了,程序可能会因为堆栈不平衡而崩溃。建议保留这一行,或者只 NOP 到 CALL EAX 为止。
第二个对话框区域(红框 2)
- 同样的逻辑,从 001E159C 开始准备参数,到 001E15BA (CALL EAX) 执行弹窗。
- 同样要注意后面的 ADD ESP, 10 (001E15BC) 最好保留。
⚠️ 警告:避开堆栈平衡指令 在两个对话框之间,或者第二个对话框之后,可能会有 ADD ESP, xx 或 SUB ESP, xx 这样的指令。
- 千万不要 NOP 掉这些指令! 否则会导致程序崩溃(栈不平衡)。
- 只 NOP 掉负责“准备参数”和“调用函数”的那几行。
第二步:执行填充操作
- 鼠标左键点击第一行要 NOP 的代码(例如第一个 PUSH)。
- 按住 Shift 键,点击最后一行要 NOP 的代码(例如第二个 CALL)。
- 此时选中的区域会变成高亮颜色(通常是浅蓝色或黄色)。
- 在选中的高亮区域上点击 鼠标右键。
- 在菜单中选择 "edit"。
- 在子菜单中选择 "Fill with NOPs"(用 NOP 填充)。

- 注:如果是汉化版 OD,可能是 "二进制" -> "用 NOP 填充"。
第三步:验证与保存
- 你会看到选中的所有指令都变成了 90 (机器码) 和 NOP (助记符)。
- 红色的跳转箭头(如果有)应该消失了,因为代码现在是连续向下的。

- 在反汇编窗口任意位置 右键。
- 选择 "Copy to executable" 。

- 在弹出的新窗口中 右键 -> "Save file"。
- 保存为新的文件名(如 hello_nop.exe)。

💡 为什么这种方法有时候会失败?
正如你在提示中看到的,如果代码结构是这样的:
SUB ESP, 20 ; 分配局部变量空间 (千万别 NOP 这个!)
PUSH 0 ; \\
PUSH offset … ; |–> 这些可以 NOP
CALL MessageBox ; /
ADD ESP, 8 ; 恢复堆栈 (千万别 NOP 这个!)
如果你把中间全 NOP 了,程序运行到下面需要用到局部变量时,就会因为找不到数据而崩溃。
总结建议: 对于简单的 Hello World 弹窗程序,通常参数都是直接 PUSH 进去的,中间没有复杂的堆栈操作,所以 NOP 填充法 在这里是非常安全且有效的。大胆尝试吧!
方法C:手动汇编修改法(NOP 替换)
(其实和方法b没太大区别)。
第一步:跟之前一样定位到代码之后,选中整个“区块”
不要只选 CALL 那一行。
- 从第1个弹窗的第一行 MOV … (或者上面的 SUB ESP) 开始选。
- 一直选到第2个弹窗的最后一行 CALL 结束。
- 也就是把截图中 005C1561 到 005C1588 这一大坨全部选中。
第二步:一键 NOP(推荐)
- 在选中的这一大坨灰色区域上 右键。

- 选择 Binary -> Fill with NOPs。或者直接按快捷键 Ctrl+E

- 这一步其实就是我之前说的“方法二”(还更麻烦了一些),但这比你一个一个按空格键去改 CALL 要快得多,而且绝对不会出错(因为它会把中间的 MOV、SUB 全部抹平,不会留下任何隐患)。
第三步:填充 NOP (90)




- 你会看到窗口里的所有数据都变成了 90 90 90 …。
第四步:回到反汇编窗口确认
现在你应该能看到反汇编窗口里,选中的那些代码全部变成了灰色的 NOP。
4. 验证与保存
- 如果成功,程序应该直接弹出第三个对话框,点击确定后程序正常退出。

- 如果程序崩溃(Crash),说明你修改破坏了堆栈平衡(例如跳过了 SUB ESP 但没跳过对应的 ADD ESP,或者跳过了 PUSH EBP)。此时需要重新加载程序(Ctrl+F2)并调整修改位置。
- 之后保存了再打开文件,前两个弹窗内容就会显示灰色

- 确认修改无误后,在反汇编窗口中,右键点击你修改过的地方(或者全选)。
- 选择 "Copy to executable"(复制到可执行文件) -> "All modifications"(所有修改)。
- 在弹出的新窗口中,再次右键 -> "Save file"(保存文件)。
- 将文件另存为 hello_patched.exe。
总结操作流
按照上述步骤即可完成对程序的修改与验证。
四、实验结论与体会
通过本次实验,我掌握了使用OllyDbg对程序进行动态分析与修改的方法。在尝试NOP填充跳过前两个弹窗时,我深刻理解了堆栈平衡的重要性——若误将ADD ESP,10等清理指令也NOP掉,会导致程序崩溃。这让我认识到,逆向修改不仅要关注功能逻辑,更要尊重底层调用约定,否则看似简单的“空转”也可能引发严重错误。实践出真知,细节决定成败。
注意:
要快速编译.c文件,那就按我下面说的做,(x64位的)能用x64dbg打开

1.cd到要编译的.c文件夹里去
cd /e/desktop/pe_lab
2.执行编译文件命令
gcc -fexec-charset=GBK -o hello.exe hello.c
3.查看文件夹,是否编译成功

编译成功,出现exe文件就好了
如果还是想弄x32位的,又出现了问题,可以再翻到本篇文章开头,“可能出现的问题”里已经讲的很详细了,感觉我把改踩的坑都踩完了,还有问题可以在评论区讨论

