这一节先把开发环境和最小程序框架跑通。后面学习变量、数组、指针、函数栈、PE 和逆向分析时,所有实验都会回到同一个问题:代码如何被写出来、编译出来、运行起来。

1.0 前言:课程体系与学习路线

安全开发可以先按三层理解:

层次 内容 作用
语言基础 C 语言、C++ 基础、指针、结构体、函数、文件、内存 能写小程序,也能读懂后续示例代码
系统基础 Windows 程序、API、编译链接、调试、进程和内存 能理解程序如何在系统上运行
安全连接 逆向分析、漏洞成因、内存边界、注入和防护思路 把开发能力转成安全分析能力

本课程前期看起来很基础,但不要跳太快。后面的缓冲区、指针、函数栈、PE、ShellCode 和逆向分析,都会默认你已经理解“程序如何组织”和“代码如何运行”。

本章目标可以压缩成四句话:

  1. 能安装并打开 Visual Studio。
  2. 能创建 C/C++ 控制台项目。
  3. 能添加 .c.cpp 源文件并写出最小程序。
  4. 能运行、调试,并观察最小程序的执行结果。

1.1 Visual Studio 安装与配置

本课程使用 Visual Studio 作为主要实验环境。它既能写 C/C++,也能编译、调试、看调用栈和反汇编,比较适合安全开发和逆向入门阶段。

项目 说明
使用 IDE Visual Studio 2019
安装包 https://ruiqingyan.com/downloads/tools/Visual_studio_2019.exe
关键工作负载 使用 C++ 的桌面开发
建议权限 安装时以管理员身份启动

安装流程记录

步骤 示例图
以管理员身份启动 image-20260708103442355
等待安装包下载并释放资源 image-20260708103501570
选择工作负荷 image-20260708103643680
检查单个组件 image-20260708103723911
设置安装目录 image-20260708103814911
等待安装完成 image-20260708103827748
登录账号 image-20260708104006248
启动 Visual Studio image-20260708103925183

安装时最重要的是勾选“使用 C++ 的桌面开发”。如果缺少 C++ 编译工具,后面新建项目、编译 .c/.cpp 文件和调试程序都会出问题。

1.2 创建控制台项目

初学阶段建议优先创建“空项目”,这样项目结构最干净,也更容易理解每个文件是怎么来的。

步骤 示例图
创建新项目 image-20260708104053834
选择空项目 image-20260708155922893
设置工程名和存放路径 image-20260708160023640
配置窗口视图 image-20260708160111366

项目命名建议简单直接,例如:

场景 示例
C 语言基础实验 c_basic_demo
函数调用实验 function_call_demo
指针实验 pointer_demo
文件读写实验 file_io_demo

不要把所有实验都塞进同一个工程里。一个主题一个小项目,后面复盘会清楚很多。

1.3 添加源文件与代码框架

空项目创建后,需要手动添加源文件。

步骤 示例图
添加源文件 image-20260708104221258
修改文件后缀:C 语言用 .c,C++ 用 .cpp image-20260708160717469
书写对应代码 image-20260708160309637
理解程序组成 image-20260708160612816

1.4 调试和运行程序

代码能编译只是第一步,能观察它如何运行才是学习安全开发的关键。

方式 示例图
IDE 中调试运行 image-20260708161207183
直接运行生成的程序 image-20260708161236773image-20260708161244240
Win + R 呼出运行窗口,粘贴 exe 路径打开运行 image-20260708161410609

常见运行方式

操作 说明
F5 调试运行,适合配合断点、调用栈和变量窗口
Ctrl + F5 不调试运行,适合快速看输出结果
双击 .exe 直接运行生成文件,控制台可能一闪而过
命令行运行 最接近后续工具程序的使用方式

临时暂停窗口

初学时为了看清输出,可以临时加:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
printf("press any key to continue...\n");
system("pause");
return 0;
}

system("pause") 只适合初学阶段临时观察输出。正式项目里更推荐通过 IDE 断点、日志、命令行运行或输入等待来观察程序。

1.5 注释方式

注释的作用是方便自己和他人阅读代码。注释内容不参与编译,也不会影响程序执行结果。

1
2
3
4
5
6
7
// 单行注释:说明下一行或当前行代码的作用
printf("hello\n"); // 行尾注释

/*
多行注释:
适合说明一段代码、一个模块或一个实验记录。
*/

注释不应该重复代码表面意思,而应该解释“为什么这样写”。

注释方式 适合场景
单行注释 解释一行代码、一个变量、一个关键判断
多行注释 解释一段逻辑、函数目的、实验结论
行尾注释 简短补充,不要写太长

1.6 #include 预处理指令

#include 是预处理指令,不是普通 C 语句,所以结尾不需要分号。

尖括号和双引号

写法 常见用途
#include <xxx.h> 优先从系统头文件目录查找
#include "xxx.h" 优先从当前工程或当前文件所在目录查找,找不到再查系统目录

例如:

1
2
#include <stdio.h>
#include "my_header.h"

预处理的理解方式

可以先粗略理解为:

  1. 编译前,预处理器先扫描 # 开头的指令。
  2. 遇到 #include,把对应头文件内容纳入当前编译单元。
  3. 之后编译器再正式分析 C 语法。

后面写多文件项目时,头文件会承担函数声明、结构体声明、宏定义和公共接口描述等作用。

1.7 函数调用过程

函数可以把一段逻辑封装起来,让程序结构更清楚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

void print_banner(void)
{
printf("==== C Security Demo ====\n");
}

int add(int a, int b)
{
return a + b;
}

int main(void)
{
print_banner();

int result = add(3, 5);
printf("result = %d\n", result);

return 0;
}

调用流程

步骤 说明
1 程序从 main 开始
2 执行到 print_banner();,跳到 print_banner 函数
3 print_banner 执行完,返回 main
4 执行到 add(3, 5),把参数传给 add
5 add 返回计算结果
6 main 输出结果并结束

函数声明、定义和调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int add(int a, int b); // 函数声明

int main(void)
{
int result = add(1, 2); // 函数调用
printf("%d\n", result);
return 0;
}

int add(int a, int b) // 函数定义
{
return a + b;
}
概念 含义
声明 告诉编译器有这个函数,参数和返回值长什么样
定义 函数真正的实现代码
调用 在某个位置使用这个函数

后面学函数栈、调用约定、返回地址、栈溢出时,都会用到函数调用过程。现在先建立一个直觉:

  1. 调用函数时,程序执行流会临时跳到另一个函数。
  2. 函数执行完后,要回到调用位置继续执行。
  3. 参数和返回值是调用者与被调用者之间的数据交换方式。

1.8 控制台指令和命令行参数

控制台程序适合初学,因为输入输出直接、运行结果清楚。

指令 说明
cls Windows 控制台清屏
cd 切换目录
dir 查看当前目录文件
.\demo.exe 在当前目录运行程序

命令行参数预告

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);

for (int i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}

return 0;
}

命令行参数后续做工具类程序会很常见,比如指定文件路径、IP、端口、扫描目标或配置项。

1.9 语法规范风格

代码风格不影响程序能不能编译,但会影响能不能长期维护。

项目 建议
缩进 同一项目统一 4 空格或 Tab,不混用
命名 变量名表达含义,不用 a1tmp2 到处乱飞
括号 {} 风格统一,嵌套层级清楚
空行 用空行分隔逻辑块,不要整篇挤在一起
注释 解释原因、边界和坑点,不重复代码表面意思

示例:较乱的写法

1
2
#include<stdio.h>
int main(){int a=1;int b=2;printf("%d",a+b);return 0;}

示例:更适合复盘的写法

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
int left = 1;
int right = 2;
int sum = left + right;

printf("sum = %d\n", sum);

return 0;
}

本章最小复盘代码

这一章结束时,至少应该能独立写出下面这个小程序,并说清楚每一部分的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

void print_title(void)
{
printf("==== Chap1 Environment And Framework ====\n");
}

int add(int left, int right)
{
return left + right;
}

int main(void)
{
print_title();

int a = 10;
int b = 20;
int result = add(a, b);

printf("%d + %d = %d\n", a, b, result);

return 0;
}

学习检查清单

检查项 状态
能安装并打开 Visual Studio 的 C++ 桌面开发环境 待复盘
能新建控制台项目并添加 .c 文件 待复盘
能写出 Hello World 并解释每一行 待复盘
能区分注释、预处理指令、函数和语句 待复盘
能写一个普通函数并在 main 中调用它 待复盘
能使用 Ctrl + F5、断点或命令行观察运行结果 待复盘
能把代码整理成统一缩进和命名风格 待复盘

这一章的目标不是“背会所有概念”,而是让环境、项目、入口点和函数调用这条线跑通。后续学变量、类型、指针和内存时,所有实验都可以建立在这个最小框架上。