Issue
I was trying to emulate what Cheat Engine does on mac os in getting the memory address from values and modifying it. I have done this so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <libproc.h>
#include <mach/mach_init.h>
// Get array of all process ids
uint32_t* get_pids(uint16_t* size) {
uint32_t number_of_pids = proc_listpids(1, 0, NULL, 0);
uint32_t* buffer = malloc(sizeof(uint32_t) * number_of_pids);
uint8_t return_code = proc_listpids(1, 0, buffer, sizeof(buffer) * number_of_pids);
uint16_t sum = 0;
for(int i = 0; i < number_of_pids; i++) {
if(buffer[i] != 0) {
sum++;
}
}
uint32_t* final = malloc(sizeof(uint32_t) * sum);
for(int i = 0, t = 0; i < number_of_pids; i++) {
if(buffer[i]) {
final[t++] = buffer[i];
}
}
*size = sum;
return final;
}
int main() {
uint16_t size;
uint32_t* pids = get_pids(&size);
uint16_t maxpathlength = 1024;
uint16_t path_size = maxpathlength * 4;
char path_buffer[path_size];
uint32_t pid = 0;
for(int i = 0; i < size; i++) {
memset(path_buffer, '\0', sizeof(path_buffer));
uint8_t return_code = proc_pidpath(pids[i], path_buffer, path_size);
if(strstr(path_buffer, "Geometry Dash")) {
pid = pids[i];
}
//printf("PID: %d, Process: %s\n", pids[i], path_buffer);
}
mach_port_name_t port = 0;
if(task_for_pid(mach_task_self(), pid, &port)) {
printf("Run as root!\n");
}
printf("%d\n", port);
return 0;
}
So I got there and now have the mach port of the target pid however I am not sure where to go from here as I have found practically 0 good documentation on the mach_vm
methods and anything I try fails. How should I go about doing this?
Solution
So I did find out how to do this and it's pretty simple but again there is barely any documentation. To read the process memory you have this:
void* read_process_memory(vm_map_read_t task, mach_vm_address_t address, mach_vm_size_t size) {
vm_offset_t data;
mach_msg_type_number_t dataCnt;
kern_return_t ret = mach_vm_read(task, address, size, &data, &dataCnt);
if(ret != KERN_SUCCESS) {
printf("mach_vm_read() failed: %s\n", mach_error_string(ret));
}
return (void*) data;
}
Pretty self-explanatory but task
is the port the target pid which you can get with a simple:
mach_port_t task;
kern_return_t kern_return = task_for_pid(mach_task_self(), pid, &task);
but you do have the run with sudo
for it work. data
is the buffer in which the read memory will go into and dataCnt
is the maximum size of read memory on input and the actual memory size read on output. You also have to cast appropriately depending on data type of virtual memory to read it properly. So for strings, you have to cast the return of the above function to a char *
but for integers it would be int *
.
Writing memory to a process is almost the same:
void write_process_memory(vm_map_read_t task, mach_vm_address_t address, vm_offset_t data, mach_vm_size_t dataCnt) {
kern_return_t ret = mach_vm_write(task, address, data, dataCnt);
if(ret != KERN_SUCCESS) {
printf("mach_vm_write() failed: %s\n", mach_error_string(ret));
}
}
but this time dataCnt
is the bytes of the input that will be written and data
holds the bytes which you want to write to the target process virtual memory address.
Example:
Reading and writing string from python process memory (48 is offset of actual string from variable memory address in python3):
char* addy = read_process_memory(task, 0x1006c8130 + 48, 5);
printf("%s\n", addy);
and
char* data = "pizza";
write_process_memory(task, 0x1006c8130 + 48, (vm_offset_t) data, 5);
vs for integer
uint32_t* addy = read_process_memory(task, 0x1048cc7b0 + 24, 4);
printf("%d\n", *addy);
and
uint32_t data = 1000;
write_process_memory(task, 0x1048cc7b0 + 24, (vm_offset_t) &data, 4);
and 24 is offset to get the actual integer in python memory from the variable address.
Answered By - Aayush Answer Checked By - Mary Flores (WPSolving Volunteer)