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

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为1int y = px->y;  //y为2px++;int x1 = px->x;  //x为3int 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]); // 输出: 10printf("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]); // 输出: 1printf("(*p2)[1]: %d\n", (*p2)[1]); // 输出: 2return 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.140000printf("Square of 5: %d\n", SQUARE(5)); // 输出: Square of 5: 25return 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 DEBUGint main() {
#ifdef DEBUGprintf("Debug mode is ON\n");
#elseprintf("Debug mode is OFF\n");
#endifreturn 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器数据恢复—离线盘数超过热备盘数导致raidz阵列崩溃的数据恢复

服务器数据恢复环境&故障&#xff1a; 一台配有32块硬盘的服务器在运行过程中突然崩溃不可用。经过初步检测&#xff0c;基本上确定服务器硬件不存在物理故障。管理员重启服务器后问题依旧。需要恢复该服务器中的数据。 服务器数据恢复环境&#xff1a; 1、将服务器中硬盘…

Echart实现3D饼图示例

在可视化项目中&#xff0c;很多地方会遇见图表&#xff1b;echart是最常见的&#xff1b;这个示例就是用Echart&#xff0c; echart-gl实现3D饼图效果&#xff0c;复制即可用 //需要安装&#xff0c;再引用依赖import * as echarts from "echarts"; import echar…

PostgreSQL学习笔记(一):PostgreSQL介绍和安装

目录 概念 PostgreSQL简介 PostgreSQL的关键特性 1. 标准兼容性 2. 扩展性 3. 数据完整性和可靠性 4. 丰富的数据类型 5. 查询能力 6. 事务和并发控制 7. 扩展和插件 8. 跨平台和多语言支持 9. 高可用性和扩展性 常用场景 安装 Linux apt安装 下载安装包安装 客…

Linux之信号量

目录 信号量 信号量相关接口 创建信号量 初始化信号量 等待信号量&#xff0c;P操作 发布信号量&#xff0c;V操作 销毁信号量 基于信号量的环形队列下的生产者和消费者模型 环形队列 代码实现 上期我们学习了线程同步的概念&#xff0c;掌握了基于阻塞队列的生产…

Redis--高可用(主从复制、哨兵模式、分片集群)

高可用&#xff08;主从复制、哨兵模式、分片集群&#xff09; 高可用性Redis如何实现高可用架构&#xff1f;主从复制原理1. 全量同步2. 命令传播3. 增量同步 Redis Sentinel&#xff08;哨兵模式&#xff09;为什么要有哨兵模式&#xff1f;哨兵机制是如何工作的&#xff1f;…

常用的数据结构API概览

List ArrayList 1、在初始化一个ArrayList的时候&#xff0c;如果我想同时set一些值 比如存放int[ ] List<int[]> list new ArrayList(Arrays.asList(new int[]{intervals[0][0],intervals[0][1]}));//或者int[] temp new int[]{intervals[0][0],intervals[0][1]}…

wordpress右侧浮动咨询台插件

简洁实用&#xff0c;操作方便&#xff0c;没有复杂的设置。 下载、安装、启用&#xff0c;即可使用。 wordpress在线客服插件-CS4&#xff0c;该插件适用于简站主题与精智主题。 下载 https://www.jianzhanpress.com/?p4622

Spring MVC实战指南:构建高效Web应用的架构与技巧(三)

响应数据和结果视图(7种) 返回值分类 创建web.xml&#xff08;spring、过滤器解决乱码、配置控制器dispatcherServlet、加载springmvc.xml文件、配置启动加载&#xff09;创建springmvc.xml文件 <!--配置了内容&#xff0c;启动Tomcat服务器的时候&#xff0c;就会被加载--…

使用LINUX的dd命令制作自己的img镜像

为了避免重复安装同一镜像&#xff0c;配置环境&#xff0c;首先我准备一个正常使用的完整系统。 使用Gparted软件先将母盘&#xff08;如U盘&#xff0c;TF卡&#xff09;分区调整为只有数据的大小。如&#xff1a;60G的TF卡&#xff0c;只用了3.5G&#xff0c;将未使用的空间…

doris:基于 Arrow Flight SQL 的高速数据传输链路

Doris 基于 Arrow Flight SQL 协议实现了高速数据链路&#xff0c;支持多种语言使用 SQL 从 Doris 高速读取大批量数据。 用途​ 从 Doris 加载大批量数据到其他组件&#xff0c;如 Python/Java/Spark/Flink&#xff0c;可以使用基于 Arrow Flight SQL 的 ADBC/JDBC 替代过去…

Gitee图形界面上传(详细步骤)

目录 1.软件安装 2.安装顺序 3.创建仓库 4.克隆远程仓库到本地电脑 提交代码的三板斧 1.软件安装 Git - Downloads (git-scm.com) Download – TortoiseGit – Windows Shell Interface to Git 2.安装顺序 1. 首先安装git-2.33.1-64-bit.exe&#xff0c;顺序不能搞错2. …

用公网服务代理到本地电脑笔记

参考&#xff1a; 利用frp 穿透到内网的http/https网站&#xff0c;实现对外开放&#xff08;这篇博客有点老&#xff0c;需要改动&#xff0c;不能照抄&#xff09;&#xff1a;https://www.cnblogs.com/hahaha111122222/p/8509150.html frp内网穿透(windows和服务器)&#xf…

(leetcode算法题)384. 打乱数组 398. 随机数索引

问题转化&#xff1a; 题目要求将nums中的数字出现的次序随机打乱 转化成&#xff1a;对于 0 号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出现 ... && ... && 对于 n - 1号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出…

Redis - 5 ( 18000 字 Redis 入门级教程 )

一&#xff1a; 补充知识 1.1 渐进式遍历 Redis 使用 scan 命令以渐进式方式遍历键&#xff0c;避免了直接使用 keys 命令可能引发的阻塞问题。scan 的时间复杂度为 O(1)&#xff0c;但需要多次执行才能完成对所有键的遍历&#xff0c;整个过程分步进行&#xff0c;有效减少阻…

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

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

AI在电子制造中的应用:预测质量控制

一、 电子制造中存在的质量问题 电子制造过程中&#xff0c;由于生产工艺复杂、材料种类繁多、生产环境要求高等因素&#xff0c;可能会出现各种质量问题。 常见质量问题如下&#xff1a; 1. 空焊 原因&#xff1a;锡膏活性较弱、钢网开孔不佳、铜铂间距过大或大铜贴小元件、…

如何通过API实现淘宝商品评论数据抓取?item_review获取淘宝商品评论

前几天一个好朋友要我帮忙抓一下淘宝商品的评论数据&#xff0c;获取淘宝评论数据可以帮忙商家们做好市场调研&#xff0c;对自己的产品进行升级&#xff0c;从而更好地获取市场。我将详细爬取方法封装成API&#xff0c;以供方便调用。 item_review-获得淘宝商品评论 响应示例…

springboot550乐乐农产品销售系统(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统乐乐农产品销售系统信息管理难度大&#xff0c;容错率低&…

百度贴吧的ip属地什么意思?怎么看ip属地

在数字化时代&#xff0c;IP地址不仅是网络设备的唯一标识符&#xff0c;更承载着用户的网络身份与位置信息。百度贴吧作为广受欢迎的社交平台&#xff0c;也遵循相关规定&#xff0c;在用户个人主页等位置展示账号IP属地信息。那么&#xff0c;百度贴吧的IP属地究竟意味着什么…

[读书日志]从零开始学习Chisel 第一篇:书籍介绍,Scala与Chisel概述,Scala安装运行(敏捷硬件开发语言Chisel与数字系统设计)

简介&#xff1a;从20世纪90年代开始&#xff0c;利用硬件描述语言和综合技术设计实现复杂数字系统的方法已经在集成电路设计领域得到普及。随着集成电路集成度的不断提高&#xff0c;传统硬件描述语言和设计方法的开发效率低下的问题越来越明显。近年来逐渐崭露头角的敏捷化设…