Created
January 20, 2021 18:31
-
-
Save nkoneko/d19c94f6911f73cffb2e7c71b80a217b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <mach/port.h> | |
#include <mach/mach.h> | |
#include <mach/vm_region.h> | |
#include <mach/vm_map.h> | |
#include <chrono> | |
#include <thread> | |
#include <utility> | |
#include <vector> | |
#include <memory> | |
namespace { | |
std::vector<std::pair<uint64_t, uint64_t>> ranges; | |
std::vector<uint64_t> addresses; | |
void find_regions(task_t task) { | |
vm_size_t size = 0; | |
vm_address_t address = 0; | |
struct vm_region_submap_info info; | |
uint32_t infoCount = VM_REGION_SUBMAP_INFO_COUNT; | |
uint32_t nesting_depth; | |
while (!vm_region_recurse_64(task, &address, &size, &nesting_depth, (vm_region_recurse_info_t) &info, &infoCount)) { | |
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) { | |
ranges.push_back(std::make_pair(address, address + size)); | |
} | |
address += size; | |
} | |
} | |
} | |
void search(task_t task, int value) { | |
for (auto& [start, end] : ranges) { | |
uint64_t size = end - start; | |
vm_size_t outsize; | |
uint64_t page_address = start & 0xFFFFFFFFFFFFF000ul; | |
std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]); | |
kern_return_t kret = vm_read_overwrite(task, page_address, size, (vm_address_t) buffer.get(), &outsize); | |
if (kret) { | |
fprintf(stderr, "failed to vm_read. %s\n", mach_error_string(kret)); | |
} | |
if (!outsize) { | |
fprintf(stderr, "failed to read\n"); | |
} | |
for (size_t i = 0; i < size; i += sizeof(int)) { | |
int *p = reinterpret_cast<int*>(buffer.get() + i); | |
if (*p == value) { | |
addresses.push_back(reinterpret_cast<uint64_t>(page_address + i)); | |
} | |
} | |
} | |
} | |
void filter(task_t task, int value) { | |
std::vector<uint64_t> new_addresses; | |
for (auto& addr : addresses) { | |
auto pageAddr = addr & 0xFFFFFFFFFFFFF000ul; | |
auto offset = addr - pageAddr; | |
vm_size_t outsize; | |
std::unique_ptr<uint8_t[]> buffer(new uint8_t[offset + sizeof(int)]); | |
kern_return_t kret = vm_read_overwrite(task, pageAddr, offset + sizeof(int), (vm_address_t) buffer.get(), &outsize); | |
if (kret) { | |
fprintf(stderr, "failed to vm_read (filter). %s\n", mach_error_string(kret)); | |
} | |
auto* p = reinterpret_cast<int*>((buffer.get() + offset)); | |
if (*p == value) { | |
new_addresses.push_back(addr); | |
} | |
} | |
addresses = new_addresses; | |
} | |
} | |
int main(int argc, char *argv[]) { | |
pid_t pid; | |
if (pid = fork()) { | |
printf("child pid=%d\n", pid); | |
for (int i = 0; i < 20; i++) { | |
printf("Parent: %d\n", i); | |
std::this_thread::sleep_for(std::chrono::seconds(1)); | |
} | |
} else { | |
pid_t ppid = getppid(); | |
printf("parent pid=%d\n", ppid); | |
std::this_thread::sleep_for(std::chrono::seconds(5)); | |
printf("Child\n"); | |
task_t task; | |
kern_return_t kret; | |
kret = task_for_pid(mach_task_self(), ppid, &task); | |
if (kret) { | |
fprintf(stderr, "failed to task_for_pid. %s\n", mach_error_string(kret)); | |
return 1; | |
} | |
kret = task_suspend(task); | |
if (kret) { | |
fprintf(stderr, "failed to suspend. %s\n", mach_error_string(kret)); | |
return 1; | |
} | |
find_regions(task); | |
search(task, 4); | |
std::this_thread::sleep_for(std::chrono::seconds(5)); | |
kret = task_resume(task); | |
if (kret) { | |
fprintf(stderr, "failed to resume. %s\n", mach_error_string(kret)); | |
return 1; | |
} | |
printf("resumed\n"); | |
std::this_thread::sleep_for(std::chrono::seconds(5)); | |
printf("nine?\n"); | |
kret = task_suspend(task); | |
if (kret) { | |
fprintf(stderr, "failed to suspend. %s\n", mach_error_string(kret)); | |
return 1; | |
} | |
filter(task, 9); | |
printf("found %lu addresses.\n", addresses.size()); | |
printf("overwriting...\n"); | |
int value = 3; | |
for (auto& addr : addresses) { | |
kret = vm_write(task, (vm_address_t) addr, (pointer_t) &value, sizeof(int)); | |
} | |
kret = task_resume(task); | |
if (kret) { | |
fprintf(stderr, "failed to resume. %s\n", mach_error_string(kret)); | |
return 1; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment