tmxklab

[pwnable.kr] memcpy 본문

War Game/pwnable.kr

[pwnable.kr] memcpy

tmxk4221 2020. 12. 2. 15:46

1. 문제 확인

memcpy@pwnable:~$ ls
memcpy.c  readme
memcpy@pwnable:~$ cat readme
the compiled binary of "memcpy.c" source code (with real flag) will be executed under memcpy_pwn privilege if you connect to port 9022.
execute the binary by connecting to daemon(nc 0 9022).

9022포트로 접속할 수 있는 듯하다.

 

 

10번의 size범위 안에 입력 값을 받고 실행되다가 멈춘다.

 

[ memcpy.c ]

// 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(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;
}

오우오우 좀 많아 보이지만 아까 코드 돌다가 끊긴 부분이 저 마지막 for문에서 끊긴 것 같다. 그리고 for문이 완전히 다 돌고 나면 플래그 값을 던져줄 것 같다.

 

 

마지막 for문안의 살펴보면

slow_memcpy()와 fast_memcpy()를 실행하고 각 실행한 것의 시간을 측정한다.

 

 

여기서 slow_memcpy()는

src배열의 값을 dest배열에 1byte씩 복사한다.

 

 

그리고 fast_memcpy()는

len이 64보다 크거나 같을 때 while문이 len/64만큼 돌고 while문안의 어셈코드가 동작하는 것 같다.

 

gdb로 확인

 


2. 접근 방법

fast_memcpy의 어셈코드에서 movdqamovntps가 사용되었다.

여기서 사용된 movdqa와 movntps는 SIMD(Single Instruction Multiple Data)연산자로 하나의 명령어로 다중 데이터를 처리하는 연산이다. 여기서 말하는 다중 데이터는 일반적인 크기의 데이터가 아니라 64, 128, 256비트 크기를 가진 자료를 말한다.

(예전에도 이런 비슷한 문제 푼 것 같은데 기억이 안나네..)

어쨌든 위와 같은 SIMD연산을 사용할 때 크게 중요한 부분이 있는데 바로 Alignment(정렬)이다.

정렬을 맞추지 않고 복사하면 뻑날 수 있다.

 

1) MOVDQA - Move Aligned Double Quadword

  • movdqa xmm1(dest), xmm2/m128(src) : Aligned double quadword를 src에서 dest로 이동
  • 16Byte(128bit) 기준으로 정렬

2) MOVNTPS - Move Aligned Four Packed Single-FP Non Temporal

  • movntps m128(dest), xmm(src) : 패킹된 단일 정밀도 부동 소수점 값을 src에서 dest로 이동
  • 16byte(128bit) 기준으로 정렬

 

 

 

 

따라서, 중간에 프로그램이 멈춘 이유는 16byte로 정렬되지 않은 dest값이 존재하기 때문인 것 같다.

src값은 이미 정해져 있고

 

dest값은 우리가 입력한 값들이 들어 있는 size배열을 malloc의 인자로 받고 힙 영역을 할당받아 주소 값을 dest에 넣는 것 같다.

 

Alignment를 맞추기 위해 16byte배수를 입력하면 될 줄 알았지만

또 멈춘다... 근데 로컬에서는 겁나 잘된다.(?????)

 

저 주소가 heap영역에 할당된 주소인데

음... 디버깅하면은 금방 풀 수 있을 것같은데 서버에는 바이너리파일도 없고 컴파일도 할 수 없다. 젠장...

 


3. 문제 풀이

 

결국 다른 사람들이 쓴 롸업을 봤는데 음... 왜 다른 사람은 뻑나고 나는 뻑 안나고 잘 되지..??ㅋㅋㅋㅋㅋ그냥 넘어가자

 

 


4. 몰랐던 개념

 

참고 자료)

 

SIMD 로 배열 copy 작성__declspec(align(16)) 과 movdqu, movdqa

movdqu mov : asm 에서의 mov 와 유사한 의미 dq : double quad (word) 로 quad word는 word 의 4배 인 8 byte 와 앞의 double 이 붙어 2*8 = 총 16 바이트인데 word 가 기본 pack 사이즈를 말함으로 2바이트씩..

3dmpengines.tistory.com

 

MOVNTPS--Move Aligned Four Packed Single-FP Non Temporal

MOVNTPS--Move Aligned Four Packed Single-FP Non Temporal Opcode Instruction Description 0F 2B /r MOVNTPS m128, xmm Move packed single-precision floating-point values from xmm to m128, minimizing pollution in the cache hierarchy. Description Moves the doubl

www.jaist.ac.jp

 

MOVDQA--Move Aligned Double Quadword

MOVDQA - Move Aligned Double Quadword Opcode Instruction Description 66 0F 6F /r MOVDQA xmm1, xmm2/m128 Move aligned double quadword from xmm2/m128 to xmm1. 66 0F 7F /r MOVDQA xmm2/m128, xmm1 Move aligned double quadword from xmm1 to xmm2/m128. Description

www.jaist.ac.jp

 

 

'War Game > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] unlink  (0) 2020.12.02
[pwnable.kr] asm  (0) 2020.12.02
[pwnable.kr] uaf  (0) 2020.12.02
[pwnable.kr] cmd2  (0) 2020.12.02
[pwnable.kr] cmd1  (0) 2020.12.02
Comments