본문 바로가기
[ ★ ]Study/War Game

[Toddler's Bottle] pwnable.kr memcpy 풀이

by nroses-taek 2017. 9. 16.
반응형

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// compiled with : gcc -o memcpy memcpy.c -m32 -lm
#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(00x40007, MAP_PRIVATE|MAP_ANONYMOUS, -10);
    char* cache2 = mmap(00x40007, MAP_PRIVATE|MAP_ANONYMOUS, -10);
    src = mmap(00x20007, MAP_PRIVATE|MAP_ANONYMOUS, -10);
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);
        ifsize < 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+1size);
        dest = mallocsize );
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;
}
 
cs
 
소스코드 위에
// compiled with : gcc -o memcpy memcpy.c -m32 -lm
이런 주석이 달려있다. 주는 이유는 분명 있을 것이다.
우리 VM에서 해보자.
 
64비트에서 컴파일 하시는 분은 아래와 같은 오류가 나올 있다.

에러는
apt-get install g++-multilib libc6-dev-i386
명령어로 해결할 있다.
 
그전에 명령어 2
MOVDQA
  • Move Aligned Double Quadword
데이터 전송 명령어로, 128bits 데이터를 번에 xmm 레지스터로 이동하거나, 반대로 xmm레지스터의 데이터를 메모리로 전송하는 처리를 한다.
MOVNTPS
Store Packed Single-Precision Floating-Point Values Using Non-Temporal Hint
번째 오퍼랜드로 입력받은 XMM 레지스터에 있는 float 데이터 4개를 캐시를 통하지 않고 번째 오퍼랜드로 입력받은 메모리 주소로 복사한다.
가장 특징은 weakly-ordered memory consistency model 이라는 겁니다.
ex) MOVNTPS m128, xmm
모두 정렬된이라는 특징을 가지고 있네요. 사실 속도 빠름에 있어서는 메모리 접근에 가장 영향이 있습니다. 참고 !

** 아래 명령어에서 가장 중요한 사실이 있습니다.
the operand must be aligned on a 16-byte boundary or a general-protection exception (#GP) will be generated.  <--- 즉 16바이트로 정렬해야 한다. 그렇지 않으면 일반 보호 예외가 발생한다고 한다.
MOVDQA

MOVNTPS

- 코드 설명 -
1
2
3
4
unsigned long long rdtsc(){
        asm("rdtsc");
}
 
cs
Read Time-Stamp Counter 라고 한다.
 
1
2
3
4
5
6
7
8
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;
}
 
cs
정말 slow하게 for문으로 하나하나 작업해주네요.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
}
 
cs

빠른지는 asm 명령어 설명할 하였다. 128bits 정보를 한번에 처리!
다만 아래 if문에서 len 참이면 slow_memcpy 가버리네요.
 
main 소스코드는 천천히 읽어주시면 됩니다… ^^
 
다시 문제 풀이로 돌아와서
 

실제 flag 받고 싶으면 nc 0 9022 연결해서 해결하라고 합니다. 씌익
설마 flag : erased ~~ 인증해보신 없으시죠 !?
 
프로그램을 실행시켜봅시다.

실험이 10번은 이루어져야 하는데, 5번에서 끝나버리네요 …

사진은 최소, 최대를 각각 입력시켜준 것입니다.
최소일 때는 5 최대일 때는 4번이네요.
 
하지만 처음에 말했듯이 우리의 VM에서 소스코드를 가져가면
우리 입맛대로 다시 수정할 있습니다.
 

fast_memcpy 다음에 주소가 얼마나 할당되는지 확인해봤습니다.
 
메모리에서 정렬된 규칙을 가진다라는 힌트를 생각해봤습니다.
숫자마다 차이를 구해도 보고, 주소값도 차를 구해서 규칙을 찾아보고.
이것저것 실험해봤습니다. ( 많은 삽질을 하게 됐네요… 이렇게 하는지 맞는가 싶기도 했고 .. )
 
우선 사진을 보면 끝나는 주소를 보면 8 or 0 으로 끝나는 것을 찾아볼 있었습니다. 실험 1번은 8 끝나니 실험 2번도 8 끝내보고자 16값이 아니라 8 더한 24값을 넣어봤습니다.
 

그러더니 주소 값의 차이가 16차이가 나네요. 하나만 바꿔서는 위에 보이는 실험 4까지 8 끝내게 바꾸어봤습니다.
 

메모리 정렬이 이렇게 되는구나 라고 느끼게 되었습니다.
이제 남은 실험들의 값들에 8 각각 더해주면 것이라고 생각했습니다.
 

 
8 24 40 72 136 264 520 1032 2056 4104
순으로 계속 입력했습니다.
 
1_w4nn4_br34K_th3_m3m0ry_4lignm3nt
 
피곤하냐며 쉬라고 하더니 이게 뭔일입니까 . 삽질 삽질 !


반응형

댓글