#include <assert.h>
#include <ctype.h>
#include <mach/mach.h>
#include <malloc/malloc.h>
#include <stdio.h>
#include <stdlib.h>
struct range_callback_info_t;
typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size);
typedef void zone_callback_t (void *info, const malloc_zone_t *zone);
typedef struct range_callback_info_tag
{
zone_callback_t *zone_callback;
range_callback_t *range_callback;
void *baton;
} range_callback_info_t;
typedef enum data_type
{
eDataTypeBytes,
eDataTypeCStr,
eDataTypeInteger
} data_type_t;
typedef struct range_contains_data_callback_info_tag
{
const uint8_t *data;
const size_t data_len;
const uint32_t align;
const data_type_t data_type;
uint32_t match_count;
} range_contains_data_callback_info_t;
static kern_return_t
task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory)
{
*local_memory = (void*) remote_address;
return KERN_SUCCESS;
}
static const void
foreach_zone_in_this_process (range_callback_info_t *info)
{
if (info == NULL || info->zone_callback == NULL)
return;
vm_address_t *zones = NULL;
unsigned int num_zones = 0;
kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones);
if (KERN_SUCCESS == err)
{
for (unsigned int i=0; i<num_zones; ++i)
{
info->zone_callback (info, (const malloc_zone_t *)zones[i]);
}
}
}
static void
range_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
{
printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size);
}
static void
ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count)
{
range_callback_info_t *info = (range_callback_info_t *)baton;
while(count--) {
info->range_callback (task, info->baton, type, ptrs->address, ptrs->size);
ptrs++;
}
}
static void
enumerate_range_in_zone (void *baton, const malloc_zone_t *zone)
{
range_callback_info_t *info = (range_callback_info_t *)baton;
if (zone && zone->introspect)
zone->introspect->enumerator (mach_task_self(),
info,
MALLOC_PTR_IN_USE_RANGE_TYPE,
(vm_address_t)zone,
task_peek,
ranges_callback);
}
const void
foreach_range_in_this_process (range_callback_t *callback, void *baton)
{
range_callback_info_t info = { enumerate_range_in_zone, callback ? callback : range_callback, baton };
foreach_zone_in_this_process (&info);
}
static void
range_contains_ptr_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
{
uint8_t *data = NULL;
range_contains_data_callback_info_t *data_info = (range_contains_data_callback_info_t *)baton;
if (data_info->data_len <= 0)
{
printf ("error: invalid data size: %zu\n", data_info->data_len);
}
else if (data_info->data_len > ptr_size)
{
return;
}
else if (task_peek (task, ptr_addr, ptr_size, (void **)&data) == KERN_SUCCESS)
{
assert (data);
const uint64_t end_addr = ptr_addr + ptr_size;
for (uint64_t addr = ptr_addr;
addr < end_addr && ((end_addr - addr) >= data_info->data_len);
addr += data_info->align, data += data_info->align)
{
if (memcmp (data_info->data, data, data_info->data_len) == 0)
{
++data_info->match_count;
printf ("0x%llx: ", addr);
uint32_t i;
switch (data_info->data_type)
{
case eDataTypeInteger:
{
for (i=0; i<data_info->data_len; ++i)
printf (i ? "%2.2x" : "0x%2.2x", data[data_info->data_len - (i + 1)]);
}
break;
case eDataTypeBytes:
{
for (i=0; i<data_info->data_len; ++i)
printf (" %2.2x", data[i]);
}
break;
case eDataTypeCStr:
{
putchar ('"');
for (i=0; i<data_info->data_len; ++i)
{
if (isprint (data[i]))
putchar (data[i]);
else
printf ("\\x%2.2x", data[i]);
}
putchar ('"');
}
break;
}
printf (" found in malloc block 0x%llx + %llu (malloc_size = %llu)\n", ptr_addr, addr - ptr_addr, ptr_size);
}
}
}
else
{
printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size);
}
}
uint32_t
find_pointer_in_heap (intptr_t addr)
{
range_contains_data_callback_info_t data_info = { (uint8_t *)&addr, sizeof(addr), sizeof(addr), eDataTypeInteger, 0};
range_callback_info_t info = { enumerate_range_in_zone, range_contains_ptr_callback, &data_info };
foreach_zone_in_this_process (&info);
return data_info.match_count;
}
uint32_t
find_cstring_in_heap (const char *s)
{
if (s && s[0])
{
range_contains_data_callback_info_t data_info = { (uint8_t *)s, strlen(s), 1, eDataTypeCStr, 0};
range_callback_info_t info = { enumerate_range_in_zone, range_contains_ptr_callback, &data_info };
foreach_zone_in_this_process (&info);
return data_info.match_count;
}
else
{
printf ("error: invalid argument (empty cstring)\n");
}
return 0;
}