#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mach/mach.h>
#include <sys/stat.h>

/* 
 * Read the contents of a page of VM from a process at a given memory offset
 * pid of -1 will perform on our own process
 * Totally ripped from _OSX Internals_ and the weasel iphone debugger
 *
 */

int main(int argc, char ** argv)
{
	kern_return_t 			k;
	pid_t					pid;
	mach_port_t				task;
	vm_address_t			page_address,scan_start_address, scan_end_address;
	vm_size_t				page_size = vm_page_size;
	vm_offset_t				local_addr;
	mach_msg_type_number_t	local_size = vm_page_size;
	int						page_offset, page_shift;
	int fp = -1;
	char * filename = (char*)malloc(sizeof(char) * 32);
	
	if(argc != 4) {
		fprintf(stderr, "[?] Usage: %s <pid> <starthex> <endhex>\n", argv[0]);
		exit(1);
	}
	
	pid = atoi(argv[1]);
	if(pid == -1)
		pid = getpid();
		
	scan_start_address = strtoul(argv[2], NULL, 16);
	scan_end_address   = strtoul(argv[3], NULL, 16);
	
	k = task_for_pid(mach_task_self(), pid, &task);
	if(k != KERN_SUCCESS) {
		mach_error("[!] task_for_pid() :", k);
		exit(k);
	}
	
	fprintf(stderr, "[*] Reading from pid %d @ %.8x\n",pid, page_address);
	
	page_address = scan_start_address; 
	while((page_address + page_size > page_address) && (page_address < scan_end_address)) {                 		
		// k = vm_region(task, address + )
		// wtf... k = vm_read_overwrite(task, page_address, 4096, (pointer_t)data, &local_size);
		
		k = vm_protect(task, page_address, page_size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
		if(k != KERN_SUCCESS) {
			k = vm_protect(task, page_address, page_size, 0, VM_PROT_READ | VM_PROT_EXECUTE);
			if(k != KERN_SUCCESS) {
				k = vm_protect(task, page_address, page_size, 0, VM_PROT_READ);
				if(k != KERN_SUCCESS) {
					// fprintf(stderr, "\n[!] %.8x could not unprotect memory", page_address);
					page_address += page_size;
					continue;
				}
			}
		}
		k = vm_read(task, page_address, page_size, &local_addr, &local_size);
		if(k != KERN_SUCCESS) {
/*
			mach_error("[!] mach_vm_read() :", k);
			if the vm_read fails, close the file, next time a page is read, open a new file. filenames include the starting address of the page
			There's a bug here I think with the naming of newfiles, still have to track it down, but the
			
			XXX: rename the file after closing it to include the ending address 
			XXX: rename to include process name?
			XXX: some kinda lame ass quicklook plugin for vmdump files	
*/
			if(fp != 1) {
				//printf("0x%.8x: Cannot read\n", page_address);
				close(fp);
				fp = -1;
			}
			
			page_address += page_size;
			continue;
		} else {			
			if(fp == -1) {
				printf("\n0x%.8x: Starting new file\n", page_address);
				sprintf(filename,"./dump/pid-%d-%.8x.vmdump",pid, local_addr); // bof in 64bit ;?
				fp = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );
			}
			
			write(fp, (void*)(local_addr + page_offset), local_size );
			// printf("\r0x%.8x", page_address);
			page_address = page_address + local_size;
		}
	}
	printf("\n");
	exit(0);
}