메모리 변조하는 방법에 대해서 일부 개발자만 동작원리와 개발하는 방법을 알고있다. 오늘 메모리 변조에 대한 기본 지식, 메모리 함수, 변조 방법까지 정리 할 예정이다.
Part 1. 기본 개념
1. Process 프로세스
우리는 HelloWorld.c 컴파일을 하면 HelloWorld.exe을 실행 파일을 생성 수 있다. iOS/OSX는 실행 가능한 파일들이 모두 실행에 필요한 데이터와 명령어가 포함된 March-O 포맷으로 되어있다. 운영체제에서 해당 실행파일을 실행하면 우리는 그걸 프로세스라고 불른다. 사실 실행파일과 프로세스는 데이터와 명령어의 집합이 다른 상태의 같은 오브젝트(객체)이다. 실행 파일은 정적, 프로세스는 동적이라고 볼 수있다. 실행 파일은 하드 디스크에 저장되어있고 프로세스는 메모리 내 저장된다.
2. Vitual memory, memory page and memory region 영역
운영체제에서 실행파일을 실행할경우, 운영체제는 실행파일을 디스크에서 메모리로 로드한다. 이 프로세스는 메모리 어디에 위치할까? ATM은 매우 복잡한 절차이며, 만일 꼭 알아야할 개념은 모든 프로세스는 별도의 메모리 공간을 차지한다는 것이다. 메모리 가상화를 통하여 프로세스 A와 프로세스 B는 다른 메모리 공간에 있다. 대부분의 메모리 작동은 가상 메모리를 기반한다. 그리고 ARMv7과 같은 32bit 프로세서의 경우 각 프로세스의 가상 메모리 사이즈는 2^32 = 4G바이트이다. 하지만 대부분의 프로세스는 실행 시 4GB를 사용하지 않고 4GB 보다 작은 부분을 소비한다. 프로세스 사용시 메모리의 실제 사이즈는 프로세스의 가상 메모리 공간이라고 불린다, 그리고 4GB 가상 메모리 공간은 많은 다양한 페이지 분할로 분할되어있다. 가상 메모리의 contiguous 블록은 고정(4096byte on iOS/OSX)되어있다. 메모리 동작의 최소한의 단위이다. 프로세스 가상 메모리 주소 공간은 다수의 메모리 영역으로 구성되어있다. 각각의 메모리 영역은 가상 메모리 페이지의 수를 포함하고, pay attention, 메모리 영역은 연속하지 않을 수 있다. 따라서 위의 개념은 다음 scrawls에 의해 설명 될 수 있다.
Part 2 Modeling 모델링
기본 개념을 정확하게 이해하고 난 다음에 메모리 변조에 대한 프로그래밍 모델을 구현하여 실제 코드를 작성해 본다.
메모리 변조란 무엇인가? 메모리에 있는 데이터를 수정하는 것이다. 어떻게 해야하는걸까? 메모리에는 주소가 존재하며, 시스템 APIs를 이용하여 우리가 찾을 값을 가지고 있는 주소를 읽고 쓸수가 있다. 하지만 우리가 찾을 주소는 어디있을까? 그 위치를 어떻게 찾을 수 있을까? 우리가 알고 있듯이 기계 코드는 쉽게 읽을 수가 없는 로우 레벨 코드로 작성되어있다. 하지만 기계가 읽을 수 있는 언어이다. 그리고 모든 기계 코드는 0과 1로 작성된 바이너리 포맷으로 되어있다. 즉, 궁극적으로 명령어와 데이타 모두 0과 1로 작성되어 있으며, 가상 메모리 상의 주소 공간은 모두 0과 1로 채워진다. 만일 찾을 값이 “010101”이면 가상 메모리 상의 주소 공간은 "00000111110101010110011000…”라고 작성되어지며 가상 메모리상의 주소 공간에서 우리가 목표한 값을 찾는건은 단락 내 문자열 패턴을 검색하는 것 만큼 쉽니다.
ARM은 little-endian형식으로 되어 있다.
메모리 수정 모델은 3개지로 나눈다
1) 모든 가상 메모리 영역을 나열 하여 대상 프로세스에서 가상 메모리 주소 공간을 얻는다.
2) 주소 공간에서 찾고자 하는 데이터를 찾는다. 그리고 찾고자하는 데이터의 주소를 얻는다.
3) 찾고자하는 데이터가 존재하는 주소의 데이터를 변경한다.
Part 3 functions 함수
코딩하기 전에, 빈번하게 접할 함수를 빠르게 살펴보자. 모두 mach_vm 함수들이다.
kern_return_t
mach_vm_read(
vm_map_t map,
mach_vm_address_t addr,
mach_vm_size_t size,
pointer_t *data,
mach_msg_type_number_t *data_size);
|
Description: Read/copy a range from one address space and return it to the caller.
$map$
is the port for the task whose memory is to be read; $addr$
is the address at which to start the read; $size$
is the number of bytes to read; $data$
is a buffer to store the read bytes; $data_size$
on input, is a pointer to the maximum size of the buffer; on output, points to the size read.
kern_return_t
mach_vm_write)
vm_map_t map,
mach_vm_address_t address,
pointer_t data,
__unused mach_msg_type_number_t size);
|
Description: Write data to the specified address in the target task's address space.
$map$
is the port for the task whose memory is to be written; $address$
is the address at which to start the write; $data$
is a buffer to be written; $size$
is the size of $data$
.
kern_retrun_t
mach_vm_region(
vm_map_t map,
mach_vm_offset_t *address,
mach_vm_size_t *size
vm_region_flavor_t flavor,
vm_region_info_t info
mach_msg_type_number_t *count,
mach_port_t *object_name);
|
Description: Return description of a virtual memory region.
$map$
is the port for the task whose address space contains the region; $addr$
on input, is the address at which to start looking for a region; on output, returns the starting address actually used; $size$
on output is the number of bytes in the located region; $flavor$
is the type of information to be returned, should be VM_REGION_BASIC_INFO
; $info$
returns region information, should be of type vm_region_basic_info_data_64_t
on both 32-bit and 64-bit OSes; $count$
on input, should be VM_REGION_BASIC_INFO_COUNT_64
; on output, the size of the region.
void *
memmem(const void *big, size_t big_len, const void *little, size_t little_len);
|
Description: Locates the first occurrence of the byte string
$little$
in the byte string $big$
. If it matches, a pointer to the first character of the first occurrence of $little$
is returned; else it returns NULL
.
Part 4 코딩
Part 5 테스트
약간 소스코드를 수정하여 String을 인식할 수 있도록 변경하였다.
DVIA라는 앱의 Binary Patching의 로그인 프로세스를 이용하여 메모리 변조가 잘 되는지 확인하려고 한다.
Login Method 1의 계정과 비밀번호는 Admin/This!sA5Ecret 이다.
이때 로그인 비밀번호에 qqqqqqqqqqqqq를 입력하면 로그인이 틀리게 된다.
./memory_char_iOS -> 실행하면 아래와 같이 PID를 읽어온다.
변경하고 싶은 프로세스 PID를 입력하고 문자열을 검색한다.
이전에 qqqqqqqqqqqqq를 입력 했으므로 해당 문자열을 검색한다.
Please choose your next action 1 -> Modify search results; 선택
0x메모리 주소 -> 입력
This!sA5Ecret -> 입력
Admin/qqqqqqqqqqqqq를 입력한 상태에서 memory_char_iOS를 이용하여 문자열을 This!sA5Ecret를 입력한다.
메모리 내에서는 입력패드로 입력한 비밀번호가 qqqqqqqqqqqqq에서 This!sA5Ecret로 변경된 상태이므로 로그인이 성공하게 된다.
'40. > 42. iOS' 카테고리의 다른 글
[DVIA] 11. Sensitive Information in memory (0) | 2015.01.26 |
---|---|
iOS GNU Debugger 설치 (0) | 2015.01.20 |
iRET, Snoop-it 동시 설치 (0) | 2014.11.23 |
iNalyzer5 (0) | 2014.11.23 |
[DVIA] 10. Binary Patching (0) | 2014.11.12 |