You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Current »

Excuse the ads! We need some help to keep our site up.

List

Memory cheat(iOS)

  • 해당 내용은 2년전에 분석하고 개발한 Cheat tool에 대한 설명입니다.
  • 해당 Tool은 보안 점검시 Memory cheat에 대한 검증용으로 필요해 개발하였습니다.
  • 해당 정보와 Tool을 이용하여 부정 행위시 부정 행위를 한 사용자에게 책임이 있으며, 개발자에게는 책임이 없습니다.

Development Tools

  • 해당 Cheat tool을 개발하기 위해 Theos를 사용합니다.

Structure of Memory Cheat

  • Memory cheats tool은 다음과 같은 형태로 동작합니다.
The default behavior of the memory cheat tool.
1Process attach사용자 레벨에서 프로세스 주소 공간에 접근하기 위해 task_for_pid()를 사용해 대상 프로세스를 연결합니다.
2Check the process memory area효율적인 Memory 분석을 위해 대상 프로세스가 사용하는 Memory 정보(Memory map)를 확인합니다.
3Memory access대상 프로세스의 Memory를 분석 및 변경하기 위해 vm_read_overwrite, vm_write를 사용해 메모리의 값을 읽고 변경합니다.

Process list

SYNOPSIS - sysctl()

  • sysctl() 함수 개요는 다음과 같습니다.
sysctl SYNOPSIS
#include <sys/types.h>
#include <sys/sysctl.h>

int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
Management Information Base(MIB)
CTL_KERN
  • 해당 레벨을 사용해 확인할 수 있는 정보는 다음과 같습니다.
    • 프로세스 정보
    • 시스템 v 노드
    • 열린 파일 항목
    • 라우팅 테이블 항목
    • 가상 메모리 통계
    • 로드 평균 히스토리 및 클럭 속도 정보
KERN_PROC
  • 전체 프로세스 테이블 또는 그 하위 세트를 반환합니다.
    • struct kinfo_proc 구조체 배열이 리턴됩니다.

Example

  • 다음과 같이 sysctl() 함수를 사용해 Process list를 확인 할 수 있습니다.
RunningProcess.h
	int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
    size_t miblen = 4;
    size_t size = 0;

    int st = sysctl(mib, miblen, NULL, &size, NULL, 0);

    struct kinfo_proc *process = NULL;
    struct kinfo_proc *newprocess = NULL;
    
    do {
        size += size / 10;
        newprocess = (kinfo_proc * )realloc(process, size);
        if (!newprocess){
            if (process){
                free(process);
            }
            return nil;
        }
        process = newprocess;
        st = sysctl(mib, miblen, process, &size, NULL, 0);
    } while (st == -1 && errno == ENOMEM);


	for (int i = nprocess - 1; i >= 0; i--){
    	NSString *processID = [[NSString alloc] initWithFormat:@"%d", process[i].kp_proc.p_pid];
    	NSString *processName = [[NSString alloc] initWithFormat:@"%s", process[i].kp_proc.p_comm];
		[processID release];
		[processName release];
	}

	free(process);

Process attach

SYNOPSIS - task_for_pid()

  • task_for_pid() 함수 개요는 다음과 같습니다.
    • 해당 함수는 프로세스 ID이름을 지정하여 다른 프로세스에 대한 작업 포트를 가져옵니다.
    • 권한이 있는 프로세스 또는 사용자 ID가 동일한 프로세스만 허용됩니다.
SYNOPSIS - task_for_pid()
#include <mach/mach.h> 

kern_return_t task_for_pid(struct task_for_pid_args *args);

kern_return_t task_for_pid(struct task_for_pid_args *args)

Example

Process attach
int attach(){
    kern_return_t kret;
    tmp_target_task = 0;
    kret = task_for_pid(mach_task_self(),pid,&tmp_target_task);
    if (kret) {
        printf("task_for_pid() failed with message %s!\n",mach_error_string(kret));
    }else{
        printf("attach - target_task : %d, tmp_target_task : %d\n",target_task, tmp_target_task);
        kret = task_suspend(target_task);
        if (kret != KERN_SUCCESS) {
            printf("task_suspend() failed with message %s!\n",mach_error_string(kret));
        }else{
            printf("task_suspend - Success\n");
            return 1;
        }
    }
    return 0;
}

Check the process memory area

SYNOPSIS - vm_region_recurse()

  • vm_region_recurse() 함수 개요는 다음과 같습니다.
    • 해당 함수는 타겟 프로세스의 메모리 매핑 정보를 추출합니다.
    • 1번째 인자 값에는 task_for_pid()함수로 부터 전달받은 작업 포트를 전달 합니다.
    • 2번째 인자 값에는 서브맵의 주소 값을 저장 할 공간을 전달 합니다.
    • 3번째 인자 값에는 서브맵의 크기 값을 저장 할 공간을 전달 합니다.
    • 4번째 인자 값에는 서브맵의 개수 값을 저장 할 공간을 전달 합니다.
    • 5번째 인자 값에는 서브맵에 대한 정보를 저장 할 공간을 전달 합니다.
      • 해당 구조체의 protection, max_protection 값을 이용해 해당 서브맵의 권한을 확인 할 수 있습니다.
        • protection : 현재 설정되어 있는 접근 보호 권한
        • max_protection : 
    • 6번째 인자 값에는 "VM_REGION_SUBMAP_INFO_COUNT" 값을 전달 합니다.
SYNOPSIS - vm_region_recurse()
#include <mach/mach.h>
kern_return_t vm_region_recurse(
	vm_map_t map,
	vm_offset_t *address,
	vm_size_t *size,
	natural_t *depth,
	vm_region_recurse_info_t info32,
	mach_msg_type_number_t *count)
  • 하지만 해당 함수는 최신 버전에서 64bit로 마이그레이션 되었습니다.
SYNOPSIS - vm_map_region_recurse_64()
kern_return_t
vm_map_region_recurse_64(
	vm_map_t		 map,
	vm_map_offset_t	*address,
	vm_map_size_t		*size,
	natural_t	 	*nesting_depth,
	vm_region_submap_info_64_t	submap_info,
	mach_msg_type_number_t	*count)

struct vm_region_submap_info

struct vm_region_submap_info
struct vm_region_submap_info {
	vm_prot_t			protection;     /* present access protection */
	vm_prot_t			max_protection; /* max avail through vm_prot */
	vm_inherit_t		inheritance;/* behavior of map/obj on fork */
	uint32_t			offset;		/* offset into object/map */
    unsigned int    	user_tag;	/* user tag on map entry */
    unsigned int    	pages_resident;	/* only valid for objects */
    unsigned int    	pages_shared_now_private; /* only for objects */
    unsigned int    	pages_swapped_out; /* only for objects */
    unsigned int    	pages_dirtied;   /* only for objects */
    unsigned int    	ref_count;	 /* obj/map mappers, etc */
    unsigned short  	shadow_depth; 	/* only for obj */
    unsigned char   	external_pager;  /* only for obj */
    unsigned char   	share_mode;	/* see enumeration */
	boolean_t			is_submap;	/* submap vs obj */
	vm_behavior_t		behavior;	/* access behavior hint */
	vm32_object_id_t	object_id;	/* obj/map name, not a handle */
	unsigned short		user_wired_count; 
};

Example

  • 다음과 같이 vm_region_recurse()함수를 이용해 서브맵 정보를 추출 할 수 있습니다.
    • info.protection, info.max_protection 값을 이용해 읽기, 쓰기 권한을 확인합니다.(VM_PROT_WRITE | VM_PROT_READ)
    • 읽기, 쓰기 권한이 있는 서브맵을 대상으로 메모리 검색을 진행합니다.
int findWriteableRegions()
int findWriteableRegions(){
    vm_size_t size;
    vm_address_t address;
    natural_t nesting_depth;
    mach_msg_type_number_t infoCnt;

    regionList.clear();

    size = 0;
    address = 0;
    struct vm_region_submap_info info;
    infoCnt = VM_REGION_SUBMAP_INFO_COUNT;

    for (; !vm_region_recurse(target_task,&address,&size,&nesting_depth,(vm_region_recurse_info_t)&info,&infoCnt);) {
        if (info.is_submap) {
            ++nesting_depth;
        }else{
            if ((info.protection & (VM_PROT_WRITE | VM_PROT_READ)) == 3 && (info.max_protection & (VM_PROT_WRITE | VM_PROT_READ)) == 3) {
                regionStruct.startAddr = address;
                regionStruct.endAddr = size + address;
                regionStruct.size = size;
                regionList.push_back(regionStruct);
                printf("region: %016x-%016x\n",regionStruct.startAddr,regionStruct.endAddr);
            }
            address += size;
        }
    }
    return 1;
}

Memory read - vm_read_overwrite(), vm_read()

  • 해당 함수들은 지정된 대상 작업의 주소 공간 범위를 읽어 들입니다.

SYNOPSIS - vm_read_overwrite()

  • vm_read_overwrite() 함수 개요는 다음과 같습니다.

    • 1번째 인자 값에는 task_for_pid()함수로 부터 전달받은 작업 포트를 전달 합니다.
    • 2번째 인자 값에는 읽기를 시작할 메모리 영역의 주소를 전달 합니다.
    • 3번째 인자 값에는 읽을 바이트 수를 전달 합니다.
    • 4번째 인자 값에는 읽어 들인 메모리 영역의 값을 저장 할 공간을 전달합니다.

    • 5번째 인자 값에는 읽어 들인 메모리 영역의 크기를 저장 할 공간을 전달합니다.

SYNOPSIS - vm_read_overwrite()
#include <mach/mach.h>

kern_return_t vm_read_overwrite(
  vm_map_t    	map,
  vm_address_t  address,
  vm_size_t    	size,
  vm_address_t  data,
  vm_size_t    	*data_size);
  • vm_read 함수는 동적으로 할당된 바이트 배열의 데이터를 반환합니다.
  • vm_read_overwrite 함수는 데이터를 호출자가 지정한 퍼버(data_in 인자)에 저장합니다.
SYNOPSIS - vm_read()
kern_return_t vm_read(
	vm_task_t		target_task,
	vm_address_t	address,
	vm_size_t		size,
	size			data_out,
	target_task		data_count);

Example

  • 다음과 같은 코드를 이용해 프로세스의 메모리 값을 추출 할 수 있습니다.
    • vm_read_overwrite() 함수를 이용해 전달된 프로세스의 메모리 값을 읽어 옵니다.
      • 읽을 영역은 startAddress 에서 endAddress 까지 읽어 옵니다.
      • 읽은 메모리 값은 4096byte 씩 buffer에 저장됩니다.
void getValueArea(vm_address_t startAddress,vm_address_t endAddress, void* buffer,long number)
void getValueArea(vm_address_t startAddress,vm_address_t endAddress, void* buffer,long number){
    kern_return_t result;

    long readArea = 0;
    vm_size_t outsize;


    while(endAddress > startAddress){
		if (readArea != (startAddress & 0xFFFFFFFFFFFFF000)) {
			readArea = startAddress & 0xFFFFFFFFFFFFF000;
                
			outsize = 0;
			result = vm_read_overwrite(target_task, readArea, 4096, (vm_address_t)buffer, &outsize);
                
			if(!outsize){
				printf("stardAddress 64 : %lx, %lx\n",startAddress,endAddress);
				fprintf(stderr,"vm_read_overwrite failed: %lu\n",startAddress & 0xFFFFFFFFFFFFF000);
			}
		}
            
		if (result == KERN_SUCCESS) {
        	for (int i=0; i<512; i++) {
				memInfoStruct.address = startAddress;
				memInfoStruct.value = *(long*)((char*)buffer + ((startAddress - (startAddress & 0xFFFFFFFFFFFFF000)) & 0xFFFFFFFFFFFFFFF8));
				memDataList.push_back(memInfoStruct);
				startAddress += 8;
			}
		}else{
			startAddress += 8;
		}
    }
}

Memory write - vm_write()

  • 해당 함수는 대상 프로세스의 메모리 영역에 지정된 주소에 데이터를 씁니다.

SYNOPSIS - vm_write()

  • vm_write() 함수 개요는 다음과 같습니다.
    • 1번째 인자 값에는 task_for_pid()함수로 부터 전달받은 작업 포트를 전달 합니다.
    • 2번째 인자 값에는 데이터를 쓸 메모리 주소를 전달합니다.
    • 3번째 인자 값에는 메모리에 쓸 데이터가 저장된 메모리 주소를 전달합니다.
    • 4번째 인자 값에는 데이터의 크기를 전달 합니다.
SYNOPSIS - vm_write()
#include <mach/mach.h>
kern_return_t vm_write(
	vm_map_t    			map,
	vm_address_t    		address,
	pointer_t    			data,
	mach_msg_type_number_t  size)

Example

void MemoryWrite(vm_address_t address,long value)
void MemoryWrite(vm_address_t address,long value){
    vm_size_t outsize;
    vm_address_t startAddress = 0;
    
    unsigned int data;
    vm_read_overwrite(target_task, startAddress & 0xFFFFFFFFFFFFFFF8, 8, (vm_address_t)&data, &outsize);
    
    if (!outsize) {
        printf("vm_read_overwrite(%11lx) failed 1.",startAddress & 0xFFFFFFFFFFFFFFF8);
    }    
    
    unsigned int write_data;
    write_data = value;
    
    kern_return_t kr;
    kr = vm_write(target_task, address, (vm_address_t)&write_data, 8);
    if(kr){
        printf("Fail %x\n", kr);
    }else{
        printf("Sucess!\n");
    }
}

Memory fuzzing

  • 치트 툴에서 제공하는 Memory fuzz 기능은 다음과 같습니다.
    • 값이 이전과 같음
    • 값이 이전과 같지 않음
    • 값이 이전보다 증가
    • 값이 이전보다 감소
  • 해당 기능들은 다음과 같이 처리 됩니다.
    • 우선 Fuzz기능을 실행하면 대상 프로세스에서 사용중인 모든 메모리 값을 메모리 영역 또는 파일에 저장합니다.
    • 사용자가 치트 기능을 선택하면, 치트 툴은 앞에서 저장해둔 정보를 이용하여 값을 비교합니다.
      • 사용자가 입력한 조건에 만족하는 값만 메모리 영역 또는 파일에 저장합니다.
      • 이러한 과정을 반복합니다.

Related site

  • No labels