ipwn
[pwnable.kr] memcpy 본문
바로 소스코드를 보자.
#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(){ 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 (%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); 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; } |
가장먼저 메인함수를 보면 일정 범위만큼 사이즈를 10개 입력받아서 입력 받은만큼 각각 dest에 malloc으로 사이즈를 힙에 할당해주고, 각 dest에 slow_memcpy함수와 fast_memcpy로 값을 copy한다..
(rdtsc는 시간을 재는 용도로 쓰이는 것 같다.)
다른 함수들을 보자.
slow_memcpy함수를 보면 for 구문으로 인덱스를 하나하나 옮겨준다. 그래서 slow인듯하다.
그리고 fast_memcpy함수를 보면 대충 다 똑같은 코드인데 어셈 딴으로 여러 바이트를 한 번에 옮겨준다.
아마 src의 값을 전부 xmm 레지스터에 담고 그 레지스터에 담긴 값을 다시 dest에 담는듯 하다.
여기서 가장 중요한 xmm레지스터와 movqda, movntps에 대해서 알아보자.
XMM register
xmm0 ~ xmm7 이렇게 8개, 128bit로 이뤄진 레지스터이다.
MOVDQA(Move aligned Double Quad word) instruction
128bit 레지스터 또는 128bit 메모리에 정렬되어 있는 값을 읽어올 때 사용 한다.
movdqa xmm1, xmm2의 명령어가 존재한다고 하면, xmm2의 값을 xmm1에 옮긴다.
이 movdqa instruction은 소스 또는 대상 피연산자가 메모리 피연산자인 경우 피연산자는 128bit 정렬되어야하며 그렇지 않으면 에러를 뿜는다.
MOVNTPS instruction
정렬된 128bit의 값을 128bit메모리에 옮길 때 사용한다.
간단하게 옮겨질 메모리를 가진 피연산자는 xmm레지스터가 와야 하고 메모리를 옮겨받을 피연산자는 128bit의 메모리 공간이어야 한다.
이 instruction은 옮겨받을 피연산자가 128bit의 메모리공간이 아니라면 에러를 뿜는듯 하다.
이제 다시 문제로 돌아가서 소스코드를 보면 heap영역에 메모리를 할당해주고, 그 영역에 각각의 memcpy를 해주는 것을 알 수 있다.
일단 바로 파일을 실행시켜보자.
이렇게 정직하게 값을 넣어주면 어느정도 memcpy를 실행시키는 듯 하다가 segmentation fault를 띄우고 종료시킨다.
이게 종료가 되는 이유가 128bit로 정렬하는 데에서의 문제인 것 같아서 소스코드를 받아와서 조금 수정해서 dest의 주소를 나타내주는 코드를 조금 추가하고, 다시 값을 넣어봤다.
보면 아까의 instruction들은 128bit로 정렬이 되어있어야 한다고 했는데, 위 사진을 보면 주소 값들이 128bit로 정렬이 되어있지 않은 걸 알 수 있다.
이 이유는 16의 배수byte로 할당을 해도 heap영역에 할당을 하는 것이라면 부가적인 heap chunk들의 정보들을 담는데에 8byte가 추가적으로 필요해서라고 판단을 했고 첫 할당할 때의 주소는 상관 없으니 첫 할당할 때를 제외, 모든 곳을 할당할 때 8byte를 추가적으로 뒤로 밀어서 할당을 해주면 잘 정렬이 되어 모든 memcpy가 잘 실행될 것이라고 판단했다.
예상대로 정렬이 전부 잘 마무리 되고, flag가 출력된 것을 확인할 수 있다.
'Write up > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] leg (0) | 2018.07.13 |
---|---|
[pwnable.kr] input (0) | 2018.03.13 |
[pwnable.kr] coin1 (0) | 2018.03.12 |
[pwnable.kr] uaf (0) | 2018.02.19 |
[pwnable.kr] shellshock (0) | 2018.01.06 |