Pwnable之[Toddler’s Bottle](三)–memcpy
提示是:
Are you tired of hacking?, take some rest here.
Just help me out with my small experiment regarding memcpy performance.
after that, flag is yours.
黑累了吗?放松一下吧,就帮我个小忙,测试下关于memcpy的性能,flag就是你的。
看代码:
// compiled with : gcc -o memcpy memcpy.c -m32 -lm
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>
unsigned long long rdtsc(){
asm("rdtsc");
}
char* slow_memcpy(char* dest, const char* src, size_t len){//逐个复制
int i;
for (i=0; i<len; i++) {
dest[i] = src[i];
}
return dest;
}
char* fast_memcpy(char* dest, const char* src, size_t len){
size_t i;
// 64-byte block fast copy 64字节块快速复制(用xmm汇编的方式)
if(len >= 64){
i = len / 64;
len &= (64-1);
while(i-- > 0){
__asm__ __volatile__ (
"movdqa (%0), %%xmm0\n"
"movdqa 16(%0), %%xmm1\n"
"movdqa 32(%0), %%xmm2\n"
"movdqa 48(%0), %%xmm3\n"
"movntps %%xmm0, (%1)\n"
"movntps %%xmm1, 16(%1)\n"
"movntps %%xmm2, 32(%1)\n"
"movntps %%xmm3, 48(%1)\n"
::"r"(src),"r"(dest):"memory");
dest += 64;
src += 64;
}
}
// byte-to-byte slow copy
if(len) slow_memcpy(dest, src, len);//如果小于64位的话调用slow_memcpy逐字复制
return dest;
}
int main(void){
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
printf("Hey, I have a boring assignment for CS class.. :(\n");//嘿,我有一个无聊的CS课程作业。
printf("The assignment is simple.\n");//这个作业很简单
printf("-----------------------------------------------------\n");
printf("- What is the best implementation of memcpy? -\n");//什么是最好的memcpy实现方式
printf("- 1. implement your own slow/fast version of memcpy -\n");//试验你自己的快/慢版memcpy
printf("- 2. compare them with various size of data -\n");//将它们与各种数据大小进行比较
printf("- 3. conclude your experiment and submit report -\n");//结束你的实验并提交报告
printf("-----------------------------------------------------\n");
printf("This time, just help me out with my experiment and get flag\n");//请你帮我完成这个实验
printf("No fancy hacking, I promise :D\n");
unsigned long long t1, t2;
int e;
char* src;
char* dest;
unsigned int low, high;
unsigned int size;
// allocate memory 分配内存
char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
size_t sizes[10];
int i=0;
// setup experiment parameters //设置实验参数 从2^3~2^13
for(e=4; e<14; e++){ // 2^13 = 8K
low = pow(2,e-1);
high = pow(2,e);
printf("specify the memcpy amount between %d ~ %d : ", low, high);//指定的memcpy的量在low~high之间
scanf("%d", &size);
if( size < low || size > high ){
printf("don't mess with the experiment.\n");//不满足则退出
exit(0);
}
sizes[i++] = size;//将你输入的实验参数放入size数组
}
sleep(1);
printf("ok, lets run the experiment with your configuration\n");
sleep(1);
// run //开始实验
for(i=0; i<10; i++){
size = sizes[i];
printf("experiment %d : memcpy with buffer size %d\n", i+1, size);
dest = malloc( size );
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect 消除缓存效应
t1 = rdtsc();
slow_memcpy(dest, src, size); // byte-to-byte memcpy 逐个byte的内存复制
t2 = rdtsc();
printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1);//CUP运算时间
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect 消除缓存效应
t1 = rdtsc();
fast_memcpy(dest, src, size); // block-to-block memcpy 逐个内存块的内存复制
t2 = rdtsc();
printf("ellapsed CPU cycles for fast_memcpy : %llu\n", t2-t1);//CUP运算时间
printf("\n");
}
printf("thanks for helping my experiment!\n");
printf("flag : ----- erased in this source code -----\n");
return 0;
}
看代码,可以很清晰的了解程序的逻辑。
要你输入10次长度不同的bit,并且每次要满足不同的范围,不满足就退出。
然后分别用slow_memcpy()和fast_memcpy()慢快两种内存复制方式计算你输入长度的内存复制所花的时间。
运行10次后获得falg。
程序清楚了,那就直接开始测试下:
先吧memcpy复制到自己的机子。
安装下38位的编译环境:
yum install glibc-devel.i686
按题目提示编译:gcc -o memcpy memcpy.c -m32 -lm
测试下:
发现运行到第4次运算的时候停止了。
具体什么原因不知道,那就gdb调试下。
还是随便输入:
发现程序在 movntps 这个指令上出现异常。
百度下这个指令:
得到提示:内存地址必须是16字节对齐的。
而我们知道前面我们输入size也就是新malloc的内存空间大小。
那么只要保证我们输入的size所创建的内存空间是16字节对齐的,不可以了吗?
再查了下malloc的内存对齐方式, 是8字节对齐的。
那么当我们输入9时,就有2*8=16也就对齐了。
因为有个默认有个4字节的头部。
所以其创建的公式是:((j+4)/8+1)*8
j是我们输入的size
按这个思想写python爆破大致符合的size:
for i in range(4,14):
low = pow(2,i-1)
high = pow(2,i)
#print low,high
n=0
for j in range(low+1,high):
if(n==4):
break
k=((j+4)/8+1)*8
if((k)%16==0 or (k)%16>=9):
print j,
n=n+1
print ".........ok"
输出满足的4个size
然后。。。
这是我按输出的随机选的。
参考文章:
https://blog.csdn.net/misskissc/article/details/17717717
https://www.cnblogs.com/dongzhiquan/p/5621906.html
https://blog.csdn.net/celerychen2009/article/details/8839104
https://www.cnblogs.com/p4nda/p/7162378.html
https://blog.csdn.net/xiaominkong123/article/details/51733528