1.环境的搭建

#pragma warning(disable:4996)

这个环境,就懒得说了,我用的是VS 2022

然后再linux下是之前那个Ubuntu,gcc版本是5.4.0

2.用编译器反编译

windows下

简单的写一个c代码

1
2
3
4
5
#include <stdio.h>
int main() {
printf("hello world");
return 0;
}

设置断点,单步调试,就可以看到一步一步的内容。

按下f11【要注意不是在黑窗口里面按】或者点vs2022上面的一步一步调试。

在设置了断点后可以反编译

调试->窗口->反汇编【ctrl+alt+d】

ok 懂的都懂

linux下

1
2
3
4
5
#include<stdio.h>
int main(){
printf("hello world!");
return 0;
}

首先生成这个文件哈

1
gcc hello.c -o hello -i 保存路径

image-20230506212836966

C文件编译都要分为4步:预处理,编译,汇编,连接

预处理

在预处理中,展开#开始的内容,比如:#if、#ifdef、#if ndef、 #else 、 #elif 、 # endif、#define、#include、#line、 #error、#pragma。其实就是把除了函数(包括main)以外的东西都展开成指定的形式。

1
2
gcc -E hello.c -o hello.i
cat hello.i

image-20230506213243648


编译

就是把预处理的文件进行一系列语法分析及优化后形成相应的汇编文件

这个汇编的代码看似像又不像的,是因为:这个是一种ATT格式

但是我之间所用的都是 Intel 汇编格式啦

1
gcc -S hello.i -o hello.s
image-20230506213510970

汇编

汇编就是把生成的汇编指令逐条翻译成机器可以识别的机器码,这一步会产生和平台相关性,决定了在哪种平台下运行

1
gcc -c hello.s -o hello.o

这里用cat直接打开有点奇怪,但是能看到头部是ELF

这种二进制代码就copy出来再win7虚拟机中用010打开啦或者用强大的EverEdit打开【需要选中hex模式】

image-20230506214143652

链接

这是最后一步啦,将生成的目标文件和其所依赖的库进行链接,生成一个可执行的可执行文件。

1
gcc hello.o -o hello

3.变量

变量

1.命名,这个就不再这里赘述了

2.数据类型

image-20230506222505883

再windows下的 win32 x64 还有linux下 i686 x86_4下所存储的大小再某些上面是不同的

image-20230506222920503

常量

1.数字:10进制就是直接表示,八进制需要0开头,十六进制需要0x开头

2.字符:字符常量书写:’x’。本质上是ascii码

3.转移字符

image-20230506223121064

4.用#define预处理定义常量

1
2
#define identifier value
#define PI 3.14

5.用const关键字

1
2
const type variable = value
const int var = 5

声明

  • 变量定义:用于变量分配存储空间,可以指定初始值。程序中,变量有且只有一个定义
  • 变量声明:用于程序表明变量的类型和名字
  • 定义也是声明

extern 声明 不是定义

1
2
3
4
extern int i; //声明,不是定义
int i; //声明也是定义,没初始化
extern double pi = 3.14; //定义
声明如果有初始化,则就被当作定义。只有当extern声明位于函数外部的时候,才可以被初始化

4.内存布局

1.一个程序上大部分是由BSS ,data ,text 段组冲的

  • BSS(baa segment)通常用来存放程序中未初始化的全局变量的一快内存。BSS段属于静态内存分配
  • 数据段(data segment)存放已经初始化的全局变量的一块内存区域。数据段属于静态内存分配
  • 代码段(text segment)用来存放程序执行代码的一块内存区域。这部分程序的大小再运行之前就已经确定了,再内存区域中属于只读。在代码段中,也可能包含一些只读的常数变量。【这里就想从xx线指过去的才是xx段,本质上都是内存空间】
image-20230506225036257

其中 .text为代码段,只读。.bss段包含程序中未初始化的全局变量,和static变量。data段包含:heap(堆),stack(栈),静态数据区

  • 堆:用于存放进程中被动态分配的内存段,大小不固定,可以扩张或者缩减。 当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到上(堆被扩张);当利⽤free等函数释放内存时,被释放的内存从中被剔除(堆被缩减)
  • 栈:存放程序临时创建的局部变量。也就是在函数{}中定义的变量,(不包括static声明的变量,static意味着在数据段中存放变量)
  • data段中的静态数据区存放的是程序中已经初始化的全局变量,静态变量,常量
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.c 
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
static int c =0//全局(静态)初始化区
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
}