security/포너블 - pwnable.kr

pwnable.kr - [Toddler's Bottle] memcpy

민사민서 2023. 2. 5. 22:33
// compiled with : gcc -o memcpy memcpy.c -m32 -lm (32bit, math 관련 라이브러리)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>

unsigned long long rdtsc(){
   // time stamp counter를 가져오는 명령어
        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
   if(len >= 64){
      i = len / 64;
      len &= (64-1);
      while(i-- > 0){
         __asm__ __volatile__ (
// movdqa 메모리 xxm : 메모리에 있는 16B 데이터를 한번에 xmm 레지스터로 전송
// movntps xmm 메모리 : xmm에 있는 16B 데이터를 메모리로 복사한다 
// 더블QWORD(=16B)를 XMM 레지스터와 128bit 메모리 간, XMM레지스터간 이동하는데 사용
// 피연산자가 메모리인경우 피연산자는 16Byte 경계에 정렬되어야 하며 아닐경우 예외 발생
// 16B씩 나눠서 src의 데이터를 xmm0~xmm3에 전송 후 
         "movdqa (%0), %%xmm0\n"
         "movdqa 16(%0), %%xmm1\n"
         "movdqa 32(%0), %%xmm2\n"
         "movdqa 48(%0), %%xmm3\n"
// xmm0~xmm3의 데이터를 dst 메모리로 복사 
         "movntps %%xmm0, (%1)\n"
         "movntps %%xmm1, 16(%1)\n"
         "movntps %%xmm2, 32(%1)\n"
         "movntps %%xmm3, 48(%1)\n"
         ::"r"(src),"r"(dest):"memory");
// 64B 증가시켜 복사 재개
         dest += 64;
         src += 64;
      }
   }

   // byte-to-byte slow copy
   if(len) slow_memcpy(dest, src, len);
   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");
   printf("The assignment is simple.\n");

   printf("-----------------------------------------------------\n");
   printf("- What is the best implementation of memcpy?        -\n");
   printf("- 1. implement your own slow/fast version of memcpy -\n");
   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
   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);
      scanf("%d", &size);
      if( size < low || size > high ){
         printf("don't mess with the experiment.\n");
         exit(0);
      }
      sizes[i++] = size;
   }

   sleep(1);
   printf("ok, lets run the experiment with your configuration\n");
   sleep(1);

   // run experiment
   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
      t2 = rdtsc();
      printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1);

      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);
      printf("\n");
   }

   printf("thanks for helping my experiment!\n");
   printf("flag : ----- erased in this source code -----\n");
   return 0;
}

// 로컬에서 컴파일해서 진행하면 잘 동작. 원격에선?
// 범위 내 최솟값만 넣었는데 step5 fast_memcpy에서 Segmentation fault 발생
// 문제서버의 tmp 아래 폴더 만들고 touch memcpy.c로 파일 만들고 vi memcpy 한 후 코드 붙여넣고 :Wq해서 저장 후 gcc 컴파일해서 gdb로 분석해보자
// fast_memcpy에서 movdqa 시작하는 부분(0x080487b9)에 bp 걸고 확인해보자
// stage5 fast_memcpy에서 0x80487cc에서 ni하니깐 SEG Fault 떴다
// 0x080487cc <+52>:   movntps %xmm0,(%edx) , p/x $edx 값은 0x804c4a8
// edx 값이 0x10 배수가 아니어서 그런가보다?
// edx에는 ebp+0x8=첫번째 인자가 들어가고, eax에는 ebp+0xc=두번째 인자가 들어간다
// stage5부터 문제가 생기면 edx 값 확인 후 +8 해주는 작업만 해주면 되네
// 64보다 작을 땐 slow_memcpy 루틴 따라서 상관 없지만
// 64 이상일 땐 fast_memcpy 루틴 따르므로 8+16*n 형태로 건네주어야 함
// movdqa, movntps 이용해 fastcpy 할 땐 16byte 메모리 alignment 맞춰주어야 한다~~

// cf) 서버에서 바이너리 만들 때 printf("dest: %p\n", dest); 추가해서 컴파일하면 분석 편할듯~
// 항상 src: 0xf7759000이므로 문제 x, dest만 조심하자