滴水逆向三期笔记与作业——02C语言——05 正向基础/05 循环语句
一、缓冲区溢出的HelloWorld
void HelloWorld(){
printf("Hello World");
getchar();
}
void Fun(){
int arr[5] = {1,2,3,4,5};
arr[6] = (int)HelloWorld;
}
int main(int argc, char* argv[]){
Fun();
return 0;
}
原理:arr[5]是ebp的位置,而arr[6]是ebp+0x4的位置,即函数的返回地址,赋值给arr[6]相当于是修改了eip寄存器。
二、永不停止的HelloWorld
void Fun(){
int i;
int arr[5] = {0};
for(i = 0; i <= 7; i++){
arr[i] = 0;
printf("Hello World %dn", i); //0-6循环出现
}
}
int main(int argc, char* argv[]){
Fun();
return 0;
}
反汇编:i等于7时,修改了rbp+0x1c-0x20即rbp-0x4位置的值,将其置为0,而该位置为i的值,所以每一轮for过后将i重新置0,就造成了不停的循环。
三、基础知识
3.1 变量的声明
- 声明一个变量就是告诉计算机,程序要用一块内存,这块内存需要保留,宽度和存储格式由数据类型决定;
- 计算机什么时候把这块内存给你,取决于变量的作用范围,如果是全局变量,在程序编译完就已经分配了空间,如果是局部变量,只有所在的程序被调用的时候,才会分配空间;
- 全局变量如果不赋初始值,默认是0,但局部变量在使用前一定要赋初值。
3.2 类型转换(一般用于小转大)
- MOVSX 先符号扩展(前面符号位是什么,就填充什么)
适用于有符号数
MOV AL,0xFF 000000FF
MOVSX CX,AL 0000FFFF
MOV AL,0x80 00000080
MOVSX CX,AL 0000FF80
- MOVZX 先零扩展
适用于无符号数
MOV AL,0xFF 000000FF
MOVZX CX,AL 000000FF
MOV AL,0x80 00000080
MOVZX CX,AL 00000080
- 类型转换举例
有符号数扩展 无符号数扩展
char i = 0xFF; unsigned char i = 0xFF;
short k = 0xFFFF; unsigned short k = 0x00FF;
int h = 0xFFFFFFFF; unsigned int h = 0x00000000FF;
- 示例代码
//有符号小转大
void Function1(){
char ci = 0xFF;
short si = ci;
int ni = ci;
}
//无符号小转大
void Function2(){
unsigned char ci = 0xFF;
unsigned short si = ci;
unsigned int ni = ci;
}
//有符号数大转小
//截取低位
void Function1(){
int ni = 0x12345678;
short si = ni;
char ci = si;
}
3.3 表达式
- 表达式的特点
- 表达式无论多么复杂,都只有一个结果;
- 只有表达式,可以编译通过,但并不生成代码,需要与赋值或者其他流程控制语句一起组合的时候才有意义;
- 当表达式中存在不同宽度的变量时,将结果转换为宽度最大的那个。
char a;
int b;
a = 10;
b = 20;
printf("%d",a+b);
- 当表达式中同时存在有符号和无符号数的时候,表达式的结构将转换为无符号数
unsigned char a;
char b;
a = 0xFE;
b = 1;
printf("%d",a+b);
char b = 1;
unsigned int a = 0xFFFFFFFE;
printf("%d",a+b);//结果为-1
原因:
• char b = 1;的汇编代码是mov byte ptr [ebp-4],1
• unsigned int a = 0xFFFFFFFE;的汇编代码是mov dword ptr [ebp-8],0xFFFFFFFE
• 执行a+b的操作前,首先将b的值使用有符号扩展,movsx eax,byte ptr [ebp-4],即0x00000001
• 接着执行相加操作0xFFFFFFFE+0x00000001=0xFFFFFFFF,在计算机的底层,架构存储32位1。
• 输出时,因为使用的是“d%”,所以计算机将0xFFFFFFFF当做有符号的-1输出,反之使用“u%”时则输出无符号的0xFFFFFFFF。(计算机底层存的东西是一样的,有无符号只对人类有意义)
3.4 语句和程序块
语句对cpu或内存有影响
if-else语句是整体
3.5 参数与返回值
略
3.6 关系运算符
“==”、“!=”、“>=”、“<=”、“>”、“<”
3.7 逻辑运算符:&& || !
3.8 单目运算符
输出11-11
输出11-10
3.9 三目运算符
四、作业
1、交换两个变量值
代码:
void exchange(int x, int y){
int temp;
temp = x;
x = y;
y = temp;
}
2、将一个数组中的数倒序输出
代码:
void fun(){
int arr[8] = {0,1,2,3,4,5,6,7};
int n = 7;
while (n>=0)
{
printf("%dn", arr[n]);
n--;
}
}
int main()
{
fun();
return 0;
}
3、找出数组里面最大的值,并返回
代码:
int fun(){
int arr[8] = {0,1,2,3,4,5,6,7};
int len = 8;
int i;
int max = 0;
for(i = 0; i < len; i++){
if(max <= arr[i]){
max = arr[i];
}
}
return max;
}
int main()
{
printf("%dn", fun());
return 0;
}
4、将数组所有的元素相加,将结果返回
代码:
int fun(){
int arr[8] = {0,1,2,3,4,5,6,7};
int len = 8;
int i;
int num = 0;
for(i = 0; i < len; i++){
num += arr[i];
}
return num;
}
int main()
{
printf("%dn", fun());
return 0;
}
5、将两个等长数组相同位置的值相加,存储到另外一个等长的数组中
代码:
int arr1[8] = {0,1,2,3,4,5,6,7};
int arr2[8] = {0,1,2,3,4,5,6,7};
int arr3[8] = {0};
void fun(){
int len = 8;
int i;
for(i = 0; i < len; i++){
arr3[i] = arr1[i] + arr2[i];
}
}
6、写一个函数int prime(int x),如果x是素数返回值为1,否则返回0
代码:
int prime(int x){
int i;
for(i = 2; i < x; i++){
if(x % i == 0){
return 0;
}
}
return 1;
}
7、俩俩比较数组的值,将最大的一个存储到数组的最后一个位置
代码:
int arr[8] = {7,1,2,3,4,5,6,0};
void Max(){
int g_r;
for(int i = 0; i < 8-1; i++){
if(arr[i] >= arr[i+1]){
g_r = arr[i];
arr[i] = arr[i+1];
arr[i+1] = g_r;
}
}
}
8、编写程序实现一个冒泡排序的算法
代码:
int n[8] = {7, 6, 5, 4, 3, 2, 1, 0};
void fun(){
int i, j, temp;
int len = 8;
for (i = 0; i <= len - 2 ; i++){
for (j = 0; j <= len - 1 - i; j++){
if (n[j] > n[j + 1]){
temp = n[j];
n[j] = n[j + 1];
n[j + 1] = temp;
}
}
}
}
9、判断数组是否是对称的,如果是返回1,不是返回0
代码:
int arr[10] = {0};
int fun(){
int i, temp;
int len = 10;
for (i = 0; i < len/2 ; i++){
if(arr[i] == arr[len-1-i]){
continue;
}else{
return 0;
}
}
return 1;
}