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

List

Memory cheat(iOS)

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

Development Tools

Structure of Memory Cheat

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()

#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);
CTL_KERN
  • 해당 레벨을 사용해 확인할 수 있는 정보는 다음과 같습니다.
    • 프로세스 정보
    • 시스템 v 노드
    • 열린 파일 항목
    • 라우팅 테이블 항목
    • 가상 메모리 통계
    • 로드 평균 히스토리 및 클럭 속도 정보
KERN_PROC
  • 전체 프로세스 테이블 또는 그 하위 세트를 반환합니다.
    • struct kinfo_proc 구조체 배열이 리턴됩니다.

Example

	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()

#include <mach/mach.h> 

kern_return_t task_for_pid(struct task_for_pid_args *args);

Example

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()

#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)
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 {
	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

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()

#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);
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

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()

#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){
    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

Related site