逆向入门(2)C篇-基础知识

news2025/1/7 22:39:36

C基础

1、在C中,函数的变量是从右往左传递的,也就是test(x,y),先传入y,再传x
2、变量的分类:

(1)全局变量。在编译的时候就已经确定了内存地址和宽度,变量名就是内存地址的别名。如果不重新编译,那么全局变量的内存地址就会不变,这也就是平时所说的基址。并且全局变量中的值任何程序都可以改,是公用的。
(2)局部变量。是在函数内部进行声明的,如果函数没有执行的,局部变量就没有内存空间的。局部变量的内存是分配在堆栈中的,程序执行才会分配,也就是局部变量的内存地址是不固定的,所以局部变量只能在函数内部进行使用。
(3)全局变量可以没有初始值而使用使用。局部变量在使用前必须要赋值,因为这里C是通过mov将值传入到堆栈中的,如果不提前赋值的话,堆栈中此时就没有这个局部变量的位置了
在这里插入图片描述
当然了,为啥不能在遇到int x的时候就给他mov一个值进入呢,这个也是没有想明白。
在这里插入图片描述
变量与参数的内存布局,以EBP为分界,EBP往上是局部变量从EBP-4开始,往下是参数区从EBP+8开始

3、C语言数据类型
  1. 基本类型

1.整数类型
- char 8bit ,天啊,你居然是整数类型,而不是字符串,
- short 16bit
- int 32bit
- long 32bit

2.浮点类型
- 默认为 double 型 ,64bit ,居然超了32位,哈哈哈,查了一些资料,看着还挺有意思的。
- 声明 float 型常量时,须后加‘f’或‘F’,这个是32bit的。
- 详细说明

  1. 构造类型

1.数组类型
- 数组作为参数进行传递时,使用的是数组起始成员的地址
2.结构体类型
struct关键字,就是自定义一个数据
3.共用体(联合)类型

  1. 指针类型
  2. 空类型
4、数组的溢出攻击,这就是学C的魅力吗
#include "stdafx.h"
#include <windows.h>

void plusTest(){
	while(1){
		printf("test");
		Sleep(3000);
	}
}

int main(int argc, char* argv[])
{
	int arr[8];
	arr[9] = (int)&plusTest;
	return 0;
}

程序在执行完成后,会一直输出test字符。
在这里插入图片描述
通过进入汇编查看
在这里插入图片描述
简单说明一下会溢出成功的原因

push ebp
mov ebp,esp
sub esp,60h //提升堆栈
push ebx
push esi
push edi
lea edi,[ebnp-60h]
mov exc,18h
mov eax,0cccccccch
rep stos dword ptr [edi]  //向缓冲区中写入数据
mov dword ptr [ebp+4],offset @ILT+5(plusTest) (0040100a)  //这里就是关键了,他将获取到的函数地址写入到了ebp+4的位置,正常来说,数组当中最后一个成员应该是ebp-4,也就是缓存区最底下的位置。但是由于引用了一个超出范围的下标,所以就存到了ebp+4的地方,也就是原来存返回地址的地方!
xor eax eax  //此时将eax置0
pop edi
pop esi
pop ebx   //恢复寄存器的值
mov esp,ebp  //恢复esp原有的位置
pop ebp  //恢复ebp原有的位置
ret  //将eip的值改为esp的值,并且esp+4,这个esp+4,就是之前存了40100a的值的地方,所以CPU下一次执行就会执行到这个地址上。
5、多维数组的结构

在这里插入图片描述
所以int arr[3*4] int arr[3][4]在内存中是一样的,太强了!!!编译器如何查找多维数组的,其实还是换算成一组数组。
在这里插入图片描述
在这里插入图片描述

6、字节对齐

如果一个变量占用n个字节,则该变量的起始地址必须是n的整数倍,即:存放起始地址%n=0
如果是结构体,那么结构体的起始地址是其最宽数据类型成员的整数倍。
当对空间要求较高的时候,可以通过#pragma pack(n)来改变结构体成员的对齐方式

#pragma pack(1)
struct Test{
	char a;
	int b;
}
#prama pack()

C的指针

指针类型

指针也就是和其他数据类型一样的,也就只是一种类型!
任何类型都可以带*,加上*以后就是新的类型,统称为指针类型!这个*也可以加多个,比如char*、char** 、char***,他们都是指针类型。指针类型的变量宽度永远都是4字节,无论具体类型是什么,无论有几个*
给指针类型的变量进行赋值

int*** x;
x = (int***)1;

指针类型可以进行自加和自减,带*类型的变量,++或者--新增(减少)的数量是去掉一个*后变量的宽度。

char*** a;
a = (char***)100;
a++;
//最后a的结果是104
char** a;
a = (char**)100;
a++;
//最后a的结果是104
char* a;
a = (char*)100;
a++;
//最后a的结果是101

指针类型可以进行加减运算,但不可以乘或者除。指针类型变量与其他整数相加时:指针类型变量 + N = 指针类型变量 + N*(去掉一个*后类型的宽度)

char**** a;
a = (char****)100;
a = a + 5;
//此时a结果为120
char*** a;
a = (char***)100;
a = a + 5;
//此时a结果为120
char* a;
a = (char*)100;
a = a + 5;
//此时a结果为105
short* a;
a = (short*)100;
a = a + 5;
//此时a结果为110
int* a;
a = (int*)100;
a = a + 5;
//此时a结果为120

指针还可以做比较,所谓的类型都是给编译器看的,在汇编以后,指针显示的也只是一个无符号数。
在这里插入图片描述

&的使用

&为取地址符,可以获得任何一个变量所在的地址。&变量的类型为加上一个*号的类型

*的使用

*的几种用途:(1)乘法运算符 (2)定义新的类型 (3)取值运算符*(),即取变量地址内的值。*加指针类型的变量类型为减去一个*号的类型

int x = 1;  
int* p = &x;

printf("%x %x\n",p,*(p))

//p打印的是x的内存地址,*(p)打的是x的内存地址所对应的值1

指针取值的两种方式:arr[i]相当于*(p+i)

结构体指针

例子一:

struct Point{
	int x;
	int y;
}

void main(){
	Point p = {1,2};
	Point* px = &p;
	int x = px->x;  //利用结构体指针读值
	px->y = 100;  //利用结构体指针修改值
}

例子二:

struct Point{
	int x;
	int y;
}

void main(){
	int arr[10] = {1,2,3,4,5,6,7,8,9,0};
	Point* px = (struct Point *)arr;
	int x = px->x;  //x为1
	int y = px->y;  //y为2
	px++;
	int x1 = px->x;  //x为3
	int y1 = px->y;  //y为4
}
//太有意思了
数组指针和指针数组

int *p[5]int (*p)[5] 是两种不同的声明方式,分别代表不同的数据结构。以下是它们的具体解释和区别:

int *p[5]
含义:这是一个数组,数组的每个元素都是一个指向整数的指针。
结构:p 是一个包含 5 个元素的数组,每个元素都是 int * 类型。

int a = 10;
int b = 20;
int *p[5]; // 声明一个包含5个int指针的数组
p[0] = &a; // p[0] 指向 a
p[1] = &b; // p[1] 指向 b

int (*p)[5]
含义:这是一个指针,指向一个包含 5 个整数的数组。
结构:p 是一个指向数组的指针,该数组包含 5 个 int 类型的元素。

int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5]; // 声明一个指向包含5个int的数组的指针
p = &arr; // p 指向 arr 数组

总结
int *p[5] 是一个数组,包含5个指向整数的指针。
int (*p)[5] 是一个指针,指向一个数组,该数组包含5个整数。
例子,下面是一个简单的示例,展示了这两种声明的使用:

#include <stdio.h>

int main() {
    // 示例 1: int *p[5]
    int a = 10, b = 20;
    int *p1[5];
    p1[0] = &a;
    p1[1] = &b;

    printf("p1[0]: %d\n", *p1[0]); // 输出: 10
    printf("p1[1]: %d\n", *p1[1]); // 输出: 20

    // 示例 2: int (*p)[5]
    int arr[5] = {1, 2, 3, 4, 5};
    int (*p2)[5] = &arr;

    printf("(*p2)[0]: %d\n", (*p2)[0]); // 输出: 1
    printf("(*p2)[1]: %d\n", (*p2)[1]); // 输出: 2

    return 0;
}
在这个示例中,可以看到 p1 是一个指针数组,而 p2 是指向一个数组的指针。
调用约定

就是告诉编译器,参数怎么传递,结果怎么返回,堆栈怎么平衡的!

调用约定参数压栈顺序平衡堆栈
__cdecl从右至左入栈调用者清理堆栈
__stdcall从右至左入栈自身清理堆栈
__fastcallECX/EDX传送前两个,其余的从右至左入栈自身清理堆栈
函数指针

终于理解这里了,原来这就相当于frida里面的nativefunction,用来调用原程序中的原生函数。

 int (__cdecl* *pFun)(INT skill, INT obj, INT arg);
 pFun = (int (__cdecl* *pFun)(INT skill, INT obj, INT arg))0x12345678;
 
 pFun(0,0,0);  //假如0x12345678是技能的函数地址,那么执行pFun就相当于调用了此地址了
预处理

预处理一般是指在程序源代码被转换为二进制代码之前,由预处理器对程序源代码文本进行处理,处理后的结果再由编译器进一步编译。
预处理功能主要包括宏定义文件包含条件编译三部分。

1. 宏定义 (#define)
宏定义用于定义常量和简化代码。使用 #define 可以创建宏,宏可以是常量,也可以是函数式宏。

#include <stdio.h>

// 定义一个常量宏
#define PI 3.14

// 定义一个函数式宏
#define SQUARE(x) ((x) * (x))

int main() {
    printf("PI: %f\n", PI); // 输出: PI: 3.140000
    printf("Square of 5: %d\n", SQUARE(5)); // 输出: Square of 5: 25
    return 0;
}
2. 文件包含 (#include)
文件包含用于将其他文件的内容插入到当前文件中,通常用于引入头文件。可以使用尖括号 < > 或双引号 "" 来包含文件。
尖括号 < >:通常用于引入系统头文件或标准库文件。
双引号 " ":通常用于引入用户自定义的头文件。编译器首先在当前源文件所在的目录中查找文件,如果未找到,再去系统标准目录查找。

#include <stdio.h> // 系统头文件
#include "myheader.h" // 用户自定义头文件

int main() {
    printf("Hello, World!\n");
    return 0;
}
myheader.h 示例内容:

// myheader.h
void greet() {
    printf("Greetings from the header file!\n");
}
在主文件中调用 greet() 函数:
greet(); // 输出: Greetings from the header file!
3. 条件编译 (#ifdef, #ifndef, #if, #else, #endif)
条件编译用于根据条件选择性地编译代码,可以用来控制代码的编译,通常用于调试或在不同平台上进行特定编译。
#include <stdio.h>

// 定义一个宏
#define DEBUG

int main() {
#ifdef DEBUG
    printf("Debug mode is ON\n");
#else
    printf("Debug mode is OFF\n");
#endif

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2272253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】_assert断言

目录 1. assert功能 2. 使用assert判指针有效性 3. assert的参数 4. NDEBUG宏与assert机制的关闭 5. Debug版本与Release版本 1. assert功能 assert ( ) 是assert.h头文件定义的宏&#xff0c;用于在运行时确保程序符合指定条件&#xff1a; 如果不符合&#xff08;条件…

在Unity中用Ab包加载资源(简单好抄)

第一步创建一个Editor文件夹 第二步编写BuildAb&#xff08;这个脚本一点要放在Editor中因为这是一个编辑器脚本&#xff0c;放在其他地方可能会报错&#xff09; using System.IO; using UnityEditor; using UnityEngine;public class BuildAb : MonoBehaviour {// 在Unity编…

【可实战】Bug的判定标准、分类、优先级、定位方法、提交Bug(包含常见面试题)

一、Bug相关概念 &#xff08;一&#xff09;bug判定标准 &#xff08;二&#xff09;常见 Bug 分类 &#xff08;三&#xff09;bug优先级 1.bug严重程度与优先级的关系 有些很严重的Bug&#xff0c;只在极端的条件下才出现&#xff0c;用户碰到的概率很低&#xff0c;这种情…

C语言:调试的概念和调试器的选择

所谓调试&#xff08;Dubug&#xff09;&#xff0c;就是跟踪程序的运行过程&#xff0c;从而发现程序的逻辑错误&#xff08;思路错误&#xff09;&#xff0c;或者隐藏的缺陷&#xff08;Bug&#xff09;。 在调试的过程中&#xff0c;我们可以监控程序的每一个细节&#xff…

30分钟搭建 Typecho 个人博客教程

Typecho是一款PHP博客程序&#xff0c;相比于WordPress&#xff0c;Typecho显得更加的轻量级和简洁。现在越来越多的人倾向于用Typecho来搭建个人博客——众所周知&#xff0c;能跑WordPress的机器都不便宜。 Typecho是一款国人团结打造的开源博客系统&#xff0c;和WordPress…

【软考网工笔记】计算机基础理论与安全——网络安全

病毒 Melissa 宏病毒 1. 是一种快速传播的能够感染那些使用MS Word 97 和MS Office 2000 的计算机宏病毒。 2. 前面有**Macro** 表示这是宏病毒&#xff1b; 3. 宏病毒可以感染后缀为.xls的文件&#xff1b;Worm 蠕虫病毒 1. 通常是通过网络或者系统漏洞进行传播。 2. 利用信…

数字图像处理 三 空间滤波

空间滤波是一种图像处理技术&#xff0c;它通过对图像像素及其邻域进行运算&#xff0c;利用均值&#xff0c;高斯&#xff0c;梯度&#xff0c;拉普拉斯等线性滤波和中值&#xff0c;最大最小&#xff0c;双边滤波等非线性滤波改变像素值&#xff0c;实现图像的平滑&#xff0…

记录一次电脑被入侵用来挖矿的过程(Trojan、Miner、Hack、turminoob)

文章目录 0、总结1、背景2、端倪3、有个微软的系统更新&#xff0c;就想着更新看看&#xff08;能否冲掉问题&#xff09;4、更新没成功&#xff0c;自动重启电脑5、风险文件&#xff08;好家伙命名还挺规范&#xff0c;一看名字就知道出问题了&#xff09;6、开机有一些注册表…

ES-深度分页问题

ES分页查询基本语法 # 分页 GET /hotel/_search {"query": {"match_all": {}},"sort": [{"price": "asc"}],"from": 0,"size": 10 }上面是ES查询hotel这个索引库的语句&#xff0c;其中做了分页查询&a…

弹性云服务器ECS“规格”

规格详细资料&#xff1a;规格清单&#xff08;x86&#xff09;_弹性云服务器 ECS_华为云 通用计算型 各规格详细介绍请参见通用计算型。 规格名称 计算 磁盘类型 网络 通用计算型X1 CPU/内存配比&#xff1a;自定义vCPU数量范围&#xff1a;1-16处理器&#xff1a;第三…

connect to host github.com port 22: Connection timed out 的解决方法

原因是 Github 被 GFW 屏蔽了。 Windows 系统&#xff0c;打开 C:\Windows\System32\drivers\etc&#xff0c;复制其中的 hosts 文件至桌面&#xff0c;用文本编辑器或者其他工具打开。 复制以下内容进去&#xff1a; 140.82.114.4 github.com 151.101.1.6 github.global.ss…

22408操作系统期末速成/复习(考研0基础上手)

第一部分:计算题&#xff1a; 考察范围&#xff1a;&#xff08;标红的是重点考&#xff09; 第一章&#xff1a;CPU利用率&#xff1a; 第二章&#xff1a; 进程调度算法&#xff08;需要注意不同调度算法的优先级和题目中给出的是否可以抢占【分为可抢占和不可抢占&#xff…

html本地字符串处理工具|去重、分割、求交集、求并集

源代码&#xff08;保存到本地文件命名为 xxx.html&#xff0c;用浏览器打开该文件即可使用&#xff09; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>数据处理</title><style>inpu…

基于Python的考研学习系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

计算机网络 —— 网络编程(TCP)

计算机网络 —— 网络编程&#xff08;TCP&#xff09; TCP和UDP的区别TCP (Transmission Control Protocol)UDP (User Datagram Protocol) 前期准备listen &#xff08;服务端&#xff09;函数原型返回值使用示例注意事项 accpect &#xff08;服务端&#xff09;函数原型返回…

Vue 项目自动化部署:Coding + Jenkins + Nginx 实践分享

前言 本文详细记录如何使用 Coding (以 Jenkinsfile 为核心) 和 Nginx 部署 Vue 项目&#xff0c;包含完整流程、配置细节及注意事项&#xff0c;为开发者提供一个高效的实践参考。 准备工作 这里借用一个优秀的开源项目做演示&#xff1a;芋道源码/yudao-ui-admin-vue2。 以…

Mysql运维利器之备份恢复-xtrabackup 安装

1、插件下载 xtrabackup 下载地址 找到自己mysql版本对应得 插件版本下载 2、执行安装命令 yum localinstall percona-xtrabackup-80-8.0.26-18.1.el7.x86_64.rpm 安装完毕&#xff01;查看版本信息 xtrabackup --version 安装完毕&#xff01;&#xff01;&#xff01;

【JAVA】神经网络的基本结构和前向传播算法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

ip属地的信息准确吗?ip归属地不准确怎么办

在数字化时代&#xff0c;IP属地信息成为了我们日常生活中不可或缺的一部分。在各大社交媒体平台上&#xff0c;IP属地信息都扮演着重要的角色。然而&#xff0c;随着技术的不断进步和网络的复杂性增加&#xff0c;IP属地信息的准确性问题也日益凸显。那么&#xff0c;IP属地信…

Flask----前后端不分离-登录

文章目录 扩展模块flask-wtf 的简单使用定义用户数据模型flask-login完成用户登录 扩展模块 flask-sqlalchmy&#xff0c;连接数据库flask-login&#xff0c;处理用户的登录&#xff0c;认证flask-session&#xff0c;会话保持&#xff0c;默认对用户数据加密&#xff0c;存储…