Skip to content

Instantly share code, notes, and snippets.

@dlevi309
Last active March 3, 2025 23:25
Show Gist options
  • Select an option

  • Save dlevi309/cb4a380b11b6ceb02fb14b9bd21f4ba5 to your computer and use it in GitHub Desktop.

Select an option

Save dlevi309/cb4a380b11b6ceb02fb14b9bd21f4ba5 to your computer and use it in GitHub Desktop.
NewOSXBook - listings
///////////////////////////////////////////////////////////////////////////////
//
/// \file 02_decompress.c
/// \brief Decompress .xz files to stdout
///
/// Usage: ./02_decompress INPUT_FILES... > OUTFILE
///
/// Example: ./02_decompress foo.xz bar.xz > foobar
//
// Author: Lasse Collin
//
// This file has been put into the public domain.
// You can do whatever you want with this file.
//
///////////////////////////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <lzma.h>
#include <unistd.h> // for write
static bool
init_decoder(lzma_stream *strm)
{
// Initialize a .xz decoder. The decoder supports a memory usage limit
// and a set of flags.
//
// The memory usage of the decompressor depends on the settings used
// to compress a .xz file. It can vary from less than a megabyte to
// a few gigabytes, but in practice (at least for now) it rarely
// exceeds 65 MiB because that's how much memory is required to
// decompress files created with "xz -9". Settings requiring more
// memory take extra effort to use and don't (at least for now)
// provide significantly better compression in most cases.
//
// Memory usage limit is useful if it is important that the
// decompressor won't consume gigabytes of memory. The need
// for limiting depends on the application. In this example,
// no memory usage limiting is used. This is done by setting
// the limit to UINT64_MAX.
//
// The .xz format allows concatenating compressed files as is:
//
// echo foo | xz > foobar.xz
// echo bar | xz >> foobar.xz
//
// When decompressing normal standalone .xz files, LZMA_CONCATENATED
// should always be used to support decompression of concatenated
// .xz files. If LZMA_CONCATENATED isn't used, the decoder will stop
// after the first .xz stream. This can be useful when .xz data has
// been embedded inside another file format.
//
// Flags other than LZMA_CONCATENATED are supported too, and can
// be combined with bitwise-or. See lzma/container.h
// (src/liblzma/api/lzma/container.h in the source package or e.g.
// /usr/include/lzma/container.h depending on the install prefix)
// for details.
lzma_ret ret = lzma_stream_decoder(
strm, UINT64_MAX, LZMA_CONCATENATED);
// Return successfully if the initialization went fine.
if (ret == LZMA_OK)
return true;
// Something went wrong. The possible errors are documented in
// lzma/container.h (src/liblzma/api/lzma/container.h in the source
// package or e.g. /usr/include/lzma/container.h depending on the
// install prefix).
//
// Note that LZMA_MEMLIMIT_ERROR is never possible here. If you
// specify a very tiny limit, the error will be delayed until
// the first headers have been parsed by a call to lzma_code().
const char *msg;
switch (ret) {
case LZMA_MEM_ERROR:
msg = "Memory allocation failed";
break;
case LZMA_OPTIONS_ERROR:
msg = "Unsupported decompressor flags";
break;
default:
// This is most likely LZMA_PROG_ERROR indicating a bug in
// this program or in liblzma. It is inconvenient to have a
// separate error message for errors that should be impossible
// to occur, but knowing the error code is important for
// debugging. That's why it is good to print the error code
// at least when there is no good error message to show.
msg = "Unknown error, possibly a bug";
break;
}
fprintf(stderr, "Error initializing the decoder: %s (error code %u)\n",
msg, ret);
return false;
}
static bool
decompress(lzma_stream *strm, const char *inname, FILE *infile, FILE *outfile)
{
// When LZMA_CONCATENATED flag was used when initializing the decoder,
// we need to tell lzma_code() when there will be no more input.
// This is done by setting action to LZMA_FINISH instead of LZMA_RUN
// in the same way as it is done when encoding.
//
// When LZMA_CONCATENATED isn't used, there is no need to use
// LZMA_FINISH to tell when all the input has been read, but it
// is still OK to use it if you want. When LZMA_CONCATENATED isn't
// used, the decoder will stop after the first .xz stream. In that
// case some unused data may be left in strm->next_in.
lzma_action action = LZMA_RUN;
uint8_t inbuf[BUFSIZ];
uint8_t outbuf[BUFSIZ];
strm->next_in = NULL;
strm->avail_in = 0;
strm->next_out = outbuf;
strm->avail_out = sizeof(outbuf);
while (true) {
if (strm->avail_in == 0 && !feof(infile)) {
strm->next_in = inbuf;
strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
infile);
if (ferror(infile)) {
fprintf(stderr, "%s: Read error: %s\n",
inname, strerror(errno));
return false;
}
// Once the end of the input file has been reached,
// we need to tell lzma_code() that no more input
// will be coming. As said before, this isn't required
// if the LZMA_CONATENATED flag isn't used when
// initializing the decoder.
if (feof(infile))
action = LZMA_FINISH;
}
lzma_ret ret = lzma_code(strm, action);
while (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
size_t write_size = sizeof(outbuf) - strm->avail_out;
if (fwrite(outbuf, 1, write_size, outfile)
!= write_size) {
fprintf(stderr, "Write error: %s\n",
strerror(errno));
return false;
}
strm->next_out = outbuf;
strm->avail_out = sizeof(outbuf);
}
if (ret != LZMA_OK) {
// Once everything has been decoded successfully, the
// return value of lzma_code() will be LZMA_STREAM_END.
//
// It is important to check for LZMA_STREAM_END. Do not
// assume that getting ret != LZMA_OK would mean that
// everything has gone well or that when you aren't
// getting more output it must have successfully
// decoded everything.
if (ret == LZMA_STREAM_END)
return true;
// It's not LZMA_OK nor LZMA_STREAM_END,
// so it must be an error code. See lzma/base.h
// (src/liblzma/api/lzma/base.h in the source package
// or e.g. /usr/include/lzma/base.h depending on the
// install prefix) for the list and documentation of
// possible values. Many values listen in lzma_ret
// enumeration aren't possible in this example, but
// can be made possible by enabling memory usage limit
// or adding flags to the decoder initialization.
const char *msg;
switch (ret) {
case LZMA_MEM_ERROR:
msg = "Memory allocation failed";
break;
case LZMA_FORMAT_ERROR:
// .xz magic bytes weren't found.
msg = "The input is not in the .xz format";
break;
case LZMA_OPTIONS_ERROR:
// For example, the headers specify a filter
// that isn't supported by this liblzma
// version (or it hasn't been enabled when
// building liblzma, but no-one sane does
// that unless building liblzma for an
// embedded system). Upgrading to a newer
// liblzma might help.
//
// Note that it is unlikely that the file has
// accidentally became corrupt if you get this
// error. The integrity of the .xz headers is
// always verified with a CRC32, so
// unintentionally corrupt files can be
// distinguished from unsupported files.
msg = "Unsupported compression options";
break;
case LZMA_DATA_ERROR:
msg = "Compressed file is corrupt";
break;
case LZMA_BUF_ERROR:
// Typically this error means that a valid
// file has got truncated, but it might also
// be a damaged part in the file that makes
// the decoder think the file is truncated.
// If you prefer, you can use the same error
// message for this as for LZMA_DATA_ERROR.
msg = "Compressed file is truncated or "
"otherwise corrupt";
break;
default:
// This is most likely LZMA_PROG_ERROR.
msg = "Unknown error, possibly a bug";
break;
}
fprintf(stderr, "%s: Decoder error: "
"%s (error code %u)\n",
inname, msg, ret);
return false;
}
}
}
#ifndef NOJ
// J's modifications
char *
decompressBytes(lzma_stream *strm, const char *inbytes, int size, char *Into, int *IntoSize)
{
// When LZMA_CONCATENATED flag was used when initializing the decoder,
// we need to tell lzma_code() when there will be no more input.
// This is done by setting action to LZMA_FINISH instead of LZMA_RUN
// in the same way as it is done when encoding.
//
// When LZMA_CONCATENATED isn't used, there is no need to use
// LZMA_FINISH to tell when all the input has been read, but it
// is still OK to use it if you want. When LZMA_CONCATENATED isn't
// used, the decoder will stop after the first .xz stream. In that
// case some unused data may be left in strm->next_in.
lzma_action action = LZMA_RUN;
uint8_t inbuf[BUFSIZ];
//uint8_t outbuf[BUFSIZ];
char *outbuf = Into;
strm->next_in = NULL;
strm->avail_in = 0;
strm->next_out = (uint8_t *)outbuf;
strm->avail_out = *IntoSize;
// Do just once, removed while loop
{
strm->next_in = (uint8_t *)inbytes;
strm->avail_in = size;
lzma_ret ret;
ret = lzma_code(strm, action);
int total = 0 ;
int ovf =0;
size_t write_size = *IntoSize - strm->avail_out;
total += write_size;
if (strm->avail_out == 0 && ret != LZMA_STREAM_END) {
fprintf(stderr,"OVERFLOW! Bufsize isn't enough\n");
return (NULL);
}
action = LZMA_FINISH;
ret = lzma_code(strm, action);
if (ret == LZMA_STREAM_END) {
if (getenv("JDEBUG"))fprintf(stderr,"OK! (%d bytes)\n", total);
*IntoSize = total;
return (outbuf);}
if (ret != LZMA_OK) {
// Once everything has been decoded successfully, the
// return value of lzma_code() will be LZMA_STREAM_END.
//
// It is important to check for LZMA_STREAM_END. Do not
// assume that getting ret != LZMA_OK would mean that
// everything has gone well or that when you aren't
// getting more output it must have successfully
// decoded everything.
if (ret == LZMA_STREAM_END)
return NULL; // unreachable since I handled that up there
// It's not LZMA_OK nor LZMA_STREAM_END,
// so it must be an error code. See lzma/base.h
// (src/liblzma/api/lzma/base.h in the source package
// or e.g. /usr/include/lzma/base.h depending on the
// install prefix) for the list and documentation of
// possible values. Many values listen in lzma_ret
// enumeration aren't possible in this example, but
// can be made possible by enabling memory usage limit
// or adding flags to the decoder initialization.
const char *msg;
switch (ret) {
case LZMA_MEM_ERROR:
msg = "Memory allocation failed";
break;
case LZMA_FORMAT_ERROR:
// .xz magic bytes weren't found.
msg = "The input is not in the .xz format";
break;
case LZMA_OPTIONS_ERROR:
// For example, the headers specify a filter
// that isn't supported by this liblzma
// version (or it hasn't been enabled when
// building liblzma, but no-one sane does
// that unless building liblzma for an
// embedded system). Upgrading to a newer
// liblzma might help.
//
// Note that it is unlikely that the file has
// accidentally became corrupt if you get this
// error. The integrity of the .xz headers is
// always verified with a CRC32, so
// unintentionally corrupt files can be
// distinguished from unsupported files.
msg = "Unsupported compression options";
break;
case LZMA_DATA_ERROR:
msg = "Compressed file is corrupt";
break;
case LZMA_BUF_ERROR:
// Typically this error means that a valid
// file has got truncated, but it might also
// be a damaged part in the file that makes
// the decoder think the file is truncated.
// If you prefer, you can use the same error
// message for this as for LZMA_DATA_ERROR.
msg = "Compressed file is truncated or "
"otherwise corrupt";
break;
default:
// This is most likely LZMA_PROG_ERROR.
msg = "Unknown error, possibly a bug";
break;
}
fprintf(stderr, " Decoder error: " "%s (error code %u)\n", msg, ret);
return NULL;
}
}
}
char * decompressXZChunk(char *buf, int size, char *Into, int *IntoSize)
{
lzma_stream strm = LZMA_STREAM_INIT;
init_decoder(&strm);
int outSize = 0;
char *returned = decompressBytes(&strm, buf, size, Into, IntoSize);
lzma_end(&strm); // Thanks Ryandesign.. :-)
return (returned);
}
#else // Original code unmodified
extern int
main(int argc, char **argv)
{
if (argc <= 1) {
fprintf(stderr, "Usage: %s FILES...\n", argv[0]);
return EXIT_FAILURE;
}
lzma_stream strm = LZMA_STREAM_INIT;
bool success = true;
// Try to decompress all files.
for (int i = 1; i < argc; ++i) {
if (!init_decoder(&strm)) {
// Decoder initialization failed. There's no point
// to retry it so we need to exit.
success = false;
break;
}
FILE *infile = fopen(argv[i], "rb");
if (infile == NULL) {
fprintf(stderr, "%s: Error opening the "
"input file: %s\n",
argv[i], strerror(errno));
success = false;
} else {
success &= decompress(&strm, argv[i], infile, stdout);
fclose(infile);
}
}
// Free the memory allocated for the decoder. This only needs to be
// done after the last file.
lzma_end(&strm);
if (fclose(stdout)) {
fprintf(stderr, "Write error: %s\n", strerror(errno));
success = false;
}
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
#endif
#include <mach/vm_map.h>
#include <stdio.h>
#include <mach-o/dyld_images.h>
/**
* vmmap(1) clone for OS X and iOS
* -------------------------------
*
* This is a simple example of using the mach_vm_region_info APIs in order to
* obtain a process' (technically, a task's) virtual memory address space, in a
* manner akin to /proc/[pid]/maps on Linux.
*
* The process is simple - get the task port, then call mach_vm_region_info until
* you've exhausted the address space (in iOS this happens around 0x40000000,
* where the commpage is). On iOS 6, for some peculiar reason the task port is
* invalidated after each call, so the quick workaround here solves the problem
* by regetting the port. The actual mach error code to check for is in the header
* files, though the code simply tries regetting.
*
* N.B - For this code to work, you MUST provide the entitlements to allow
* task-for-pid to work, else you'll fail with error 5. The entitlements are in
* the output in Chapter 3, but for those of you who haven't bought the book, it would be:
*
--- Cut here
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>proc_info-allow</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
<key>run-unsigned-code</key>
<true/>
</dict>
</plist>
--- Ok, enough :-)
*
* so - copy the above XML to a file, say, "ent.xml", and be sure to run "ldid -Sent.xml vmmap"
* before trying to run this. You can download the binary (already thus signed) if you're lazy
* (and trust me, because you *will* need root on your i-Device for this)
*
* As the book clearly states, once you have the task port, the world is your oyster. You can
* control the entire virtual memory space, reading and writing it as you please. Stay tuned
* for the corrupt tool (which will be provided soon in binary form)
*
*/
int g_pid = 0; // required in iOS 6 (read below)
/* 03/08/13 - Added List of Mach-O images: */
struct dyld_image_info *g_dii = NULL;
int g_imageCount;
unsigned char *
readProcessMemory (int pid, mach_vm_address_t addr, mach_msg_type_number_t *size)
{
// Helper function to read process memory (a la Win32 API of same name)
// To make it easier for inclusion elsewhere, it takes a pid, and
// does the task_for_pid by itself. Given that iOS invalidates task ports
// after use, it's actually a good idea, since we'd need to reget anyway
task_t t;
task_for_pid(mach_task_self(),pid, &t);
mach_msg_type_number_t dataCnt = size;
vm_offset_t readMem;
// Use vm_read, rather than mach_vm_read, since the latter is different
// in iOS.
kern_return_t kr = vm_read(t, // vm_map_t target_task,
addr, // mach_vm_address_t address,
*size, // mach_vm_size_t size
&readMem, //vm_offset_t *data,
size); // mach_msg_type_number_t *dataCnt
if (kr) {
// DANG..
fprintf (stderr, "Unable to read target task's memory @%p - kr 0x%x\n" , addr, kr);
return NULL;
}
return ( (unsigned char *) readMem);
}
kern_return_t mach_vm_read (vm_map_t, mach_vm_address_t, mach_vm_size_t, vm_offset_t *, mach_msg_type_number_t *);
void
findListOfBinaries(task_t t, mach_vm_address_t addr, int size)
{
kern_return_t kr;
mach_msg_type_number_t dataCnt = size;
unsigned char *readData = readProcessMemory (g_pid, addr, &dataCnt);
int machsig = 0xfeedface;
// Checking only 0xfeedfa is a simple way to catch both 64-bit (facf) and 32-bit (face) headers
// Machine endianness is automatically taken care of, too..
if (readData && memcmp (readData + 1, ((unsigned char *) &machsig) + 1 , 3) == 0)
{
// This is a Mach header
int i = 0;
// A MUCH better way would be to iterate through the LC and find the name of dyld
// but this would require my machlib.c (closed source) and really get the same result.
// This works because on both iOS and OS X dyld is at /usr/lib.
for (i = 0; i <dataCnt; i++)
{
if (memcmp(readData+i, "lib/dyld", 8) == 0)
{
unsigned int dyld_all_image_infos_offset ;
int imageCount = 0;
memcpy (&dyld_all_image_infos_offset, readData+DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET, sizeof (unsigned int));
struct dyld_all_image_infos *dyldaii ;
// Safeguard: should check that dyld_all_image_infos_offset is < size..
if (dyld_all_image_infos_offset > size)
{
// This is to be expected, since the dyld_all_image_infos is in a data region
//printf ("Offset %x is greater than region size : %x\n", dyld_all_image_infos_offset, size);
dataCnt = sizeof(dyld_all_image_infos);
readData = readProcessMemory (g_pid, addr + dyld_all_image_infos_offset , &dataCnt);
if (!readData) { return;}
dyldaii = (struct dyld_all_image_infos *) readData;
}
else
{
dyldaii = (struct dyld_all_image_infos *) (readData +dyld_all_image_infos_offset);
}
printf ("Version: %d, %d images at offset %p\n",
dyldaii->version, dyldaii->infoArrayCount, dyldaii->infoArray);
// Go to dyldaii->infoArray address
imageCount = dyldaii->infoArrayCount;
dataCnt = imageCount * sizeof(struct dyld_image_info);
g_dii = (struct dyld_image_info *) malloc (dataCnt);
g_imageCount = imageCount;
readData = readProcessMemory(g_pid, dyldaii->infoArray, &dataCnt);
if (!readData) { return;}
struct dyld_image_info *dii = (struct dyld_image_info *) readData;
// We don't need i anymore, anyway
for (i = 0; i < imageCount; i++)
{
dataCnt = 1024;
char *imageName = readProcessMemory (g_pid, dii[i].imageFilePath, &dataCnt);
if (imageName) g_dii[i].imageFilePath = strdup(imageName);
else g_dii[i].imageFilePath = NULL;
g_dii[i].imageLoadAddress = dii[i].imageLoadAddress;
}
break;
}
}
}
}
/* End 03/08/13 */
char *
behavior_to_text (vm_behavior_t b)
{
switch (b)
{
case VM_BEHAVIOR_DEFAULT: return("default");
case VM_BEHAVIOR_RANDOM: return("random");
case VM_BEHAVIOR_SEQUENTIAL: return("fwd-seq");
case VM_BEHAVIOR_RSEQNTL: return("rev-seq");
case VM_BEHAVIOR_WILLNEED: return("will-need");
case VM_BEHAVIOR_DONTNEED: return("will-need");
case VM_BEHAVIOR_FREE: return("free-nowb");
case VM_BEHAVIOR_ZERO_WIRED_PAGES: return("zero-wire");
case VM_BEHAVIOR_REUSABLE: return("reusable");
case VM_BEHAVIOR_REUSE: return("reuse");
case VM_BEHAVIOR_CAN_REUSE: return("canreuse");
default: return ("?");
}
}
char *
protection_bits_to_rwx (vm_prot_t p)
{
// previous version of this somehow lost the "p&", always returning rwx..
static char returned[4];
returned[0] = (p &VM_PROT_READ ? 'r' : '-');
returned[1] = (p &VM_PROT_WRITE ? 'w' : '-');
returned[2] = (p & VM_PROT_EXECUTE ? 'x' : '-');
returned[3] = '\0';
// memory leak here. No biggy
return (strdup(returned));
}
const char *
unparse_inheritance (vm_inherit_t i)
{
switch (i)
{
case VM_INHERIT_SHARE:
return "share";
case VM_INHERIT_COPY:
return "copy";
case VM_INHERIT_NONE:
return "none";
default:
return "???";
}
}
macosx_debug_regions (task_t task, mach_vm_address_t address, int max)
{
kern_return_t kret;
mach_vm_address_t prev_address;
/* @TODO: warning - potential overflow here - gotta fix this.. */
vm_region_basic_info_data_t prev_info,info;
mach_vm_size_t size, prev_size;
mach_port_t object_name;
mach_msg_type_number_t count;
int nsubregions = 0;
int num_printed = 0;
count = VM_REGION_BASIC_INFO_COUNT_64;
kret = mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
(vm_region_info_t) &info, &count, &object_name);
if (kret != KERN_SUCCESS)
{
printf ("mach_vm_region: Error %d - %s", kret, mach_error_string(kret));
return;
}
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));
prev_address = address;
prev_size = size;
nsubregions = 1;
for (;;)
{
int print = 0;
int done = 0;
address = prev_address + prev_size;
/* Check to see if address space has wrapped around. */
if (address == 0)
{
print = done = 1;
}
if (!done)
{
// Even on iOS, we use VM_REGION_BASIC_INFO_COUNT_64. This works.
count = VM_REGION_BASIC_INFO_COUNT_64;
kret =
mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
(vm_region_info_t) &info, &count, &object_name);
if (kret != KERN_SUCCESS)
{
/* iOS 6 workaround - attempt to reget the task port to avoiD */
/* "(ipc/send) invalid destination port" (1000003 or something) */
task_for_pid(mach_task_self(),g_pid, &task);
kret =
mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
(vm_region_info_t) &info, &count, &object_name);
}
if (kret != KERN_SUCCESS)
{
fprintf (stderr,"mach_vm_region failed for address %p - Error: %x\n", address,(kret));
size = 0;
if (address >= 0x4000000) return;
print = done = 1;
}
}
if (address != prev_address + prev_size)
print = 1;
if ((info.protection != prev_info.protection)
|| (info.max_protection != prev_info.max_protection)
|| (info.inheritance != prev_info.inheritance)
|| (info.shared != prev_info.reserved)
|| (info.reserved != prev_info.reserved))
print = 1;
if (print)
{
int print_size;
char *print_size_unit;
if (num_printed == 0)
printf ("Region ");
else
printf (" ... ");
findListOfBinaries(task, prev_address, prev_size);
/* Quick hack to show size of segment, which GDB does not */
print_size = prev_size;
if (print_size > 1024) { print_size /= 1024; print_size_unit = "K"; }
if (print_size > 1024) { print_size /= 1024; print_size_unit = "M"; }
if (print_size > 1024) { print_size /= 1024; print_size_unit = "G"; }
/* End Quick hack */
printf (" %p-%p [%d%s](%s/%s; %s, %s, %s) %s",
(prev_address),
(prev_address + prev_size),
print_size,
print_size_unit,
protection_bits_to_rwx (prev_info.protection),
protection_bits_to_rwx (prev_info.max_protection),
unparse_inheritance (prev_info.inheritance),
prev_info.shared ? "shared" : "private",
prev_info.reserved ? "reserved" : "not-reserved",
behavior_to_text (prev_info.behavior));
if (nsubregions > 1)
printf (" (%d sub-regions)", nsubregions);
printf ("\n");
prev_address = address;
prev_size = size;
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));
nsubregions = 1;
num_printed++;
}
else
{
prev_size += size;
nsubregions++;
}
if ((max > 0) && (num_printed >= max))
{
printf ("Max %d num_printed %d\n", max, num_printed);
done = 1;
}
if (done)
break;
}
}
void
main(int argc, char **argv)
{
struct vm_region_basic_info vmr;
kern_return_t rc;
mach_port_t task;
mach_vm_size_t size = 8;
vm_region_info_t info = (vm_region_info_t) malloc(10000);
mach_msg_type_number_t info_count;
mach_port_t object_name;
mach_vm_address_t addr =1;
int pid;
if (!argv[1]) { printf ("Usage: %s <PID>\n"); exit (1);}
pid = atoi(argv[1]);
g_pid = pid; // req for iOS 6
rc = task_for_pid(mach_task_self(),pid, &task);
if (rc) { fprintf (stderr, "task_for_pid() failed with error %d - %s\n", rc, mach_error_string(rc)); exit(1); }
printf ("RC %d - Task: %d\n",rc, task);
macosx_debug_regions (task, addr, 1000);
int i ;
for ( i = 0; i < g_imageCount; i++)
{
printf("Image: %s loaded @%p\n",
g_dii[i].imageFilePath, g_dii[i].imageLoadAddress);
}
printf("Done\n");
}
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> // malloc
#include <arpa/inet.h> // inet_ntop
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include "lsock.h"
#include <ncurses.h>
int wantCurses =1;
int wantUDP = 1;
int wantListen = 1;
int wantTCP = 1;
int wantColors = 1;
int wantRefresh = 1;
/**
* lsock.c : A TCPView inspired netstat(1)-clone (in text mode) for OS X/iOS
*
* Coded by Jonathan Levin - but may be freely distributed (not to mention improved!)
*
* You want version 0.2, not this. (http://newosxbook.com/files/lsock.tar)
*
* Arguments: "nc" - disable full-screen (will automatically disable curses if piping output)
* "tcp" - start in TCP-only mode
* "udp" - start in UDP-only mode
*
*
* Commands for full screen mode: 'T' - TCP, 'U' - UDP, 'L' - Listeners, 'C' - Colors
*
* Possible improvements (which I may actually get to, if I have the time/space)
*
* - GUI: Make this more like Windows' TCPView and Procmon
* - Remote monitoring
* - Periodic polling on sources: Currently only when a source notification is received do
* I poll its description
* - Add routing provider (prov #1)
* - Show provider statistics, too
*
* BUGS: When not filtering on TCP and/or UDP, sometimes not all sockets show. Need to fix that
* On iOS6, you need to kill UserEventAgent; some notifications don't make it.
*
* Note: To use curses on iOS, you will need to copy over /usr/share/terminfo from OS X.
* If you don't, ncurses will fail on initscr() - you will need "nc" instead
*
*
*/
int fd;
void
print_header (WantCurses)
{
char *header = "Time Local Addr \tRemote Addr \tIf\tState \tPID (Name)\n";
if (WantCurses) {
attrset(COLOR_PAIR(0));
attrset(A_UNDERLINE| A_BOLD);
mvwaddstr(stdscr, 1,0 ,header);
attroff(A_UNDERLINE); attron(A_NORMAL);
attrset(COLOR_PAIR(0));
}
else printf ("%s\n", header);
}
int
setupSocket(void)
{
struct sockaddr_ctl sc;
struct ctl_info ctlInfo;
unsigned int num = 0;
int fd;
memset(&ctlInfo, 0, sizeof(ctlInfo));
if (strlcpy(ctlInfo.ctl_name, "com.apple.network.statistics", sizeof(ctlInfo.ctl_name)) >=
sizeof(ctlInfo.ctl_name)) {
fprintf(stderr,"CONTROL NAME too long");
return -1;
}
if ((fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) == -1) {
fprintf(stderr,"socket(SYSPROTO_CONTROL): %s", strerror(errno));
return -1;
}
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
fprintf(stderr,"ioctl(CTLIOCGINFO): %s", strerror(errno));
close(fd);
return -1;
}
sc.sc_id = ctlInfo.ctl_id;
sc.sc_len = sizeof(sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
sc.sc_unit = num + 1; /* zero means unspecified */
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
fprintf(stderr,"connect(AF_SYS_CONTROL): %s\n", strerror(errno));
if (errno == EBUSY )
{
printf("Apparently some other process is using the system socket. "
"On iOS, this is usually UserEventAgent. Kill it (-9!) and immediately start this program\n");
}
close(fd);
return -1;
}
return fd;
}
const char *
stateToText(int State)
{
switch(State)
{
case TCPS_CLOSED: return ("CLOSED");
case TCPS_LISTEN: return ("LISTENING");
case TCPS_ESTABLISHED: return ("ESTABLISHED");
case TCPS_CLOSING: return ("CLOSING");
case TCPS_SYN_SENT: return ("SYN_SENT");
case TCPS_LAST_ACK: return ("LAST_ACK");
case TCPS_CLOSE_WAIT: return ("CLOSE_WAIT");
case TCPS_TIME_WAIT: return ("TIME_WAIT");
case TCPS_FIN_WAIT_1 : return ("FIN_WAIT_1");
case TCPS_FIN_WAIT_2 : return ("FIN_WAIT_2");
default:
return("?");
}
} // stateToText
#define MAX_DESC 2000
char *descriptors[MAX_DESC];
int descriptorsModded[MAX_DESC];
int maxDesc = 0;
char *message = " ";
void
setMessage (char *Message)
{
message = Message;
}
void
print_descriptors()
{
int i;
int j = 3;
if (!wantUDP && !wantTCP) { setMessage ( "Warning: Both TCP and UDP are filtered");}
if (wantCurses) {
attrset(COLOR_PAIR(0) | A_BOLD);
mvwaddstr(stdscr, 2,0 ,message);
attroff(A_BOLD);
}
if (wantCurses && wantRefresh)
{
wantRefresh = 0; erase();print_header(wantCurses);
}
for (i = 0; i < MAX_DESC; i++)
{
if (descriptors[i])
{
// This is a quick and dirty example - So instead of mucking around with
// descriptor data structures, I look at the text output of each, and figure
// out from it whether or not it's UDP, or the TCP state, etc..
if (strstr(descriptors[i], "N/A")) // UDP have a state of "N/A"
{
if ( ! wantUDP) continue;
if (wantColors) {
if (wantCurses) { init_pair(4,COLOR_CYAN, COLOR_BLACK); attrset(COLOR_PAIR(4)); }
else
printf (GREEN);
}
}
else // TCP (not showing route for now)
{
if (!wantTCP) continue;
if (wantColors)
{
if (wantCurses) attrset(COLOR_PAIR(0)); else printf(NORMAL);
}
if (strstr(descriptors[i], "ESTABLISHED") ) {
if (wantColors)
{
if (wantCurses) { init_pair(3, COLOR_GREEN, COLOR_BLACK); attrset(COLOR_PAIR(3));}
else printf(GREEN);
}
} // ESTABLISHED
if (strstr(descriptors[i], "LISTEN") ) {
if (!wantListen) continue;
if (wantColors)
{
if (wantCurses) { init_pair(2, COLOR_BLUE, COLOR_BLACK); attrset(COLOR_PAIR(2));}
else printf(BLUE);
}
} // ESTABLISHED
if (strstr(descriptors[i], "SYN_SENT") ) {
if (wantColors) {
if (wantCurses) { init_pair(5, COLOR_RED, COLOR_BLACK); attrset(COLOR_PAIR(5));}
else printf(RED);
}
} // SYN_SENT
} // TCP
if (wantCurses) {
mvwaddstr(stdscr, j++,0 ,descriptors[i]);
}
else
{ if (descriptorsModded[i]) printf( "%s\n", descriptors[i]); }
descriptorsModded[i] = 0;
if (wantColors && !wantCurses) printf (NORMAL);
}
}
clrtobot();
refresh();
}
char *
print_udp_descriptor (char *desc, int prov, int num)
{
nstat_udp_descriptor *nud = (nstat_udp_descriptor *) desc;
int f;
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
const char *localPrint, *remotePrint;
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
memset(timeBuf,'\0', 30);
strftime(timeBuf, 29, "%H:%M:%S", n);
// sprintf(timeBuf, "%d......", num);
sprintf (returned + strlen(returned), "%-8s ", timeBuf);
if (nud->local.v4.sin_family == AF_INET6) {
ipv6++; }
if (!ipv6)
{
localPort = ntohs(nud->local.v4.sin_port);
remotePort = ntohs(nud->remote.v4.sin_port);
}
else {
localPort = ntohs(nud->local.v6.sin6_port);
remotePort = ntohs(nud->remote.v6.sin6_port);
}
if (nud->remote.v6.sin6_family == AF_INET6) {
localPrint = inet_ntop(nud->local.v6.sin6_family,&(nud->local.v6.sin6_addr), localAddrPrint, 40);
remotePrint = inet_ntop(nud->remote.v6.sin6_family,&(nud->remote.v6.sin6_addr), remoteAddrPrint, 40);
}
if (nud->remote.v4.sin_family == AF_INET) {
localPrint = inet_ntop(nud->local.v4.sin_family,&(nud->local.v4.sin_addr), localAddrPrint, 40);
remotePrint = inet_ntop(nud->remote.v4.sin_family,&(nud->remote.v4.sin_addr), remoteAddrPrint, 40);
}
if (localAddrPrint) {
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-21s %-5hu\t", localAddrPrint, remoteAddrPrint, (unsigned short) remotePort);
}
sprintf(returned + strlen(returned), "%d\tN/A \t", nud->ifindex);
sprintf(returned + strlen(returned), "%-5d ", nud->pid);
if (nud->pname[0]) { sprintf(returned + strlen(returned), "(%s)", nud->pname );}
else { sprintf(returned + strlen(returned), " ");}
return (returned);
}
void refreshSrc (int Prov, int Num)
{
int rc ;
nstat_msg_get_src_description gsdreq;
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=Num;
gsdreq.hdr.context = Prov; ;//;nmsa->provider;
rc = write (fd, &gsdreq, sizeof(gsdreq));
}
char *
print_tcp_descriptor (char *desc, int prov, int num)
{
nstat_tcp_descriptor *ntd = (nstat_tcp_descriptor *) desc;
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
const char *localPrint, *remotePrint;
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
strftime(timeBuf, 29, "%H:%M:%S", n);
//sprintf(timeBuf, "%d......", num);
sprintf (returned + strlen(returned), "%-8s ", timeBuf);
// sprintf(returned+strlen(returned), "%d\t", ntd->sndbufused);
if (ntd->local.v4.sin_family == AF_INET6) {
//printf("IPv6\t");
ipv6++; }
if (!ipv6)
{
localPort = ntohs(ntd->local.v4.sin_port);
remotePort = ntohs(ntd->remote.v4.sin_port);
}
else {
localPort = ntohs(ntd->local.v6.sin6_port);
remotePort = ntohs(ntd->remote.v6.sin6_port);
}
if (ntd->remote.v6.sin6_family == AF_INET6) {
localPrint = inet_ntop(ntd->local.v6.sin6_family,&(ntd->local.v6.sin6_addr), localAddrPrint, 40);
remotePrint = inet_ntop(ntd->remote.v6.sin6_family,&(ntd->remote.v6.sin6_addr), remoteAddrPrint, 40);
}
if (ntd->remote.v4.sin_family == AF_INET) {
localPrint = inet_ntop(ntd->local.v4.sin_family,&(ntd->local.v4.sin_addr), localAddrPrint, 40);
remotePrint = inet_ntop(ntd->remote.v4.sin_family,&(ntd->remote.v4.sin_addr), remoteAddrPrint, 40);
}
if (remoteAddrPrint)
{
sprintf (remoteAddrPrint + strlen(remoteAddrPrint),":%-5hu", (unsigned short) remotePort);
}
else sprintf (remoteAddrPrint, "*.*");
if (localAddrPrint) {
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-25s\t", localAddrPrint, remoteAddrPrint);
}
sprintf(returned + strlen(returned), "%d\t%-10s\t", ntd->ifindex, stateToText(ntd->state));
sprintf(returned + strlen(returned), "%-5d ", ntd->pid);
if (ntd->pname[0]) { sprintf(returned + strlen(returned), "(%s)", ntd->pname );}
else { sprintf(returned + strlen(returned), " ");}
if ( strstr(returned, "CLOSED") && strstr(returned,"("))
{
refreshSrc(prov,num);
}
return (returned);
}
int
print_nstat_msg (void *msg, int size)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) msg;
switch (ns->type)
{
case 10001: // NSTAT_MSG_TYPE_SRC_ADDED = 10001
{
nstat_msg_src_added *nmsa = (nstat_msg_src_added *) (ns);
// printf("Src Added - %ld\n", sizeof(nstat_msg_src_added));
// printf ("PRovider: %d, SrcRef: %d\n", nmsa->provider, nmsa->srcref);
return (10001);
}
break;
case 10002: //NSTAT_MSG_TYPE_SRC_REMOVED = 10002
// remove this descriptor
return (10002);
//remove_descriptor(...)
break;
case 10003: // ,NSTAT_MSG_TYPE_SRC_DESC = 10003
{
nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
if (nmsd->provider == 2)
descriptors[nmsd->srcref] =print_tcp_descriptor((char *)nmsd->data, nmsd->provider,nmsd->srcref);
else if (nmsd->provider == 3)
{descriptors[nmsd->srcref] = print_udp_descriptor((char *) nmsd->data, nmsd->provider, nmsd->srcref);
}
descriptorsModded[nmsd->srcref] = 10;
}
break;
case 10004: //NSTAT_MSG_TYPE_SRC_COUNTS = 10004
{
nstat_msg_src_counts *nmsc = (nstat_msg_src_counts *) (ns );
// printf ("RX: %d TX:%d\n", nmsc->counts.nstat_rxpackets , nmsc->counts.nstat_txpackets );
} break;
case 1:
{
if (ns->context < MAX_DESC && !descriptors[ns->context]) break;
printf ("ERROR for context %lld - should be available\n", ns->context); break;
}
case 0: // printf("Success\n"); break;
break;
default:
printf("Type : %d\n", ns->type);
}
fflush(NULL);
return (ns->type) ;
}
int main (int argc, char **argv)
{
int rc = 0;
char c[2048];
memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
memset (&descriptorsModded, '\0', sizeof(int) * MAX_DESC);
fd =setupSocket ();
if (fd == -1)
{
fprintf(stderr,"Unable to continue without socket\n"); exit(1);
}
struct stat stbuf;
fstat(1, &stbuf);
if (stbuf.st_mode & S_IFCHR) { wantColors = 1; wantCurses = 1;} else {wantColors = 0; wantCurses = 0;}
int arg = 1;
for (arg = 1; arg < argc; arg++)
{
if (strcmp(argv[arg], "nc") == 0) wantCurses = 0;
if (strcmp(argv[arg], "udp") == 0) { wantUDP = 1; wantTCP = 0;}
if (strcmp(argv[arg], "tcp") == 0) { wantTCP = 1; wantUDP = 0;}
}
nstat_msg_query_src_req qsreq;
nstat_msg_add_all_srcs aasreq;
nstat_msg_get_src_description gsdreq;
char ch;
if (wantUDP)
{
aasreq.provider = 3; //
aasreq.hdr.type = 1002; //NSTAT_MSG_TYPE_ADD_ALL_SRCS
aasreq.hdr.context = 3;
rc = write (fd, &aasreq, sizeof(aasreq));
}
if (wantTCP)
{
aasreq.provider = 2 ;
aasreq.hdr.type = 1002; //NSTAT_MSG_TYPE_ADD_ALL_SRCS
aasreq.hdr.context = 2;
rc = write (fd, &aasreq, sizeof(aasreq));
}
if (wantCurses)
{
initscr(); cbreak(); noecho(); start_color();
nodelay (stdscr,1);
}
print_header(wantCurses);
while (1) { //rc > 0) {
if (wantCurses)
{
fd_set fds;
struct timeval to;
to.tv_sec = 0;
to.tv_usec = 100;
FD_ZERO (&fds);
FD_SET (fd, &fds);
// select on socket, rather than read..
rc = select(fd +1, &fds, NULL, NULL, &to);
if (rc > 0) rc = read(fd,c,2048);
}
else
rc = read (fd, c, 2048);
// check rc. Meh.
if (rc > 0)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
switch (ns->type)
{
case 10001:
{
nstat_msg_src_added *nmsa = (nstat_msg_src_added *) (c);
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=nmsa->srcref;
gsdreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources
rc = write (fd, &gsdreq, sizeof(gsdreq));
descriptorsModded[nmsa->srcref] = 1;
break;
}
case 10002:
{
//struct nstat_msg_src_removed *rem;
nstat_msg_src_removed *nmsr = (nstat_msg_src_removed *) (c);
if(wantCurses) mvhline(nmsr->srcref + 2, 1, '-' , strlen(descriptors[nmsr->srcref]));
descriptors[nmsr->srcref] = NULL; // or free..
descriptorsModded [nmsr->srcref] = 1; // or free..
break;
}
case 10003:
rc = print_nstat_msg(c,rc);
break;
case 10004:
rc = print_nstat_msg(c,rc);
break;
case 0:
break;
case 1:
//Error message - these are usually for dead sources (context will be 1005)
break;
default:
break;
}
}
if (wantCurses)
{
int i;
ch = getch();
if (ch != ERR) {
if (ch == 'H' || ch =='h') printf("HELP\n");
if (ch == 'U' || ch =='u') {wantUDP = !wantUDP; setMessage("Toggling UDP sockets"); }
if (ch == 'T' || ch == 't') { wantTCP = !wantTCP; setMessage("Toggling TCP sockets"); }
if (ch == 'L' || ch == 'l') { wantListen= !wantListen; setMessage("Toggling TCP listeners"); }
if (ch == 'C' || ch == 'c') { wantColors = !wantColors; setMessage("Toggling color display"); }
wantRefresh = 1;
}
}
print_descriptors();
}
return (0);
}
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h> // exit, etc.
// Simple User-Tunneling Proof of Concept - extends listing 17-15 in book
//
// Compiles for both iOS and OS X..
//
// Coded by Jonathan Levin. Go ahead; Copy, improve - all rights allowed.
//
// (though credit where credit is due would be nice ;-)
int
tun(void)
{
struct sockaddr_ctl sc;
struct ctl_info ctlInfo;
int fd;
memset(&ctlInfo, 0, sizeof(ctlInfo));
if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >=
sizeof(ctlInfo.ctl_name)) {
fprintf(stderr,"UTUN_CONTROL_NAME too long");
return -1;
}
fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (fd == -1) {
perror ("socket(SYSPROTO_CONTROL)");
return -1;
}
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
perror ("ioctl(CTLIOCGINFO)");
close(fd);
return -1;
}
sc.sc_id = ctlInfo.ctl_id;
sc.sc_len = sizeof(sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
sc.sc_unit = 2; /* Only have one, in this example... */
// If the connect is successful, a tun%d device will be created, where "%d"
// is our unit number -1
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
perror ("connect(AF_SYS_CONTROL)");
close(fd);
return -1;
}
return fd;
}
int
main (int argc, char **argv)
{
int utunfd = tun ();
if (utunfd == -1)
{
fprintf(stderr,"Unable to establish UTUN descriptor - aborting\n");
exit(1);
}
fprintf(stderr,"Utun interface is up.. Configure IPv4 using \"ifconfig utun1 _ipA_ _ipB_\"\n");
fprintf(stderr," Configure IPv6 using \"ifconfig utun1 inet6 _ip6_\"\n");
fprintf(stderr,"Then (e.g.) ping _ipB_ (IPv6 will automatically generate ND messages)\n");
// PoC - Just dump the packets...
for (;;)
{
unsigned char c[1500];
int len;
int i;
len = read (utunfd,c, 1500);
// First 4 bytes of read data are the AF: 2 for AF_INET, 1E for AF_INET6, etc..
for (i = 4; i< len; i++)
{
printf ("%02x ", c[i]);
if ( (i-4)%16 ==15) printf("\n");
}
printf ("\n");
}
return(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <net/ethernet.h>
int open_dev(void);
int check_dlt(int fd);
int set_options(int fd, char *iface);
int installFilter(int fd ,unsigned char Protocol, unsigned short Port);
void read_packets(int fd);
int
main(int argc, char *argv[])
{
int fd = 0;
char *iface = NULL;
iface = strdup(argc < 2 ? "en0" : argv[1]);
if (iface == NULL)
err(EXIT_FAILURE, "strdup");
fd = open_dev();
if (fd < 0)
err(EXIT_FAILURE, "open_dev");
if (set_options(fd, iface) < 0)
err(EXIT_FAILURE, "set_options");
if (check_dlt(fd) < 0)
err(EXIT_FAILURE, "check_dlt");
if (installFilter(fd, IPPROTO_TCP, 80) < 0)
err(EXIT_FAILURE, "installFilter");
read_packets(fd);
err(EXIT_FAILURE, "read_packets");
}
int
open_dev()
{
int fd = -1;
char dev[32];
int i = 0;
/* Open the bpf device */
for (i = 0; i < 255; i++) {
(void)snprintf(dev, sizeof(dev), "/dev/bpf%u", i);
(void)printf("Trying to open: %s\n", dev);
fd = open(dev, O_RDWR);
if (fd > -1)
return fd;
switch (errno) {
case EBUSY:
break;
default:
return -1;
}
}
errno = ENOENT;
return -1;
}
int
check_dlt(int fd)
{
u_int32_t dlt = 0;
/* Ensure we are dumping the datalink we expect */
if(ioctl(fd, BIOCGDLT, &dlt) < 0)
return -1;
(void)fprintf(stdout, "datalink type=%u\n", dlt);
switch (dlt) {
case DLT_EN10MB:
return 0;
default:
(void)fprintf(stderr, "Unsupported datalink type:%u", dlt);
errno = EINVAL;
return -1;
}
}
int
set_options(int fd, char *iface)
{
struct ifreq ifr;
u_int32_t enable = 1;
/* Associate the bpf device with an interface */
(void)strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
if(ioctl(fd, BIOCSETIF, &ifr) < 0)
return -1;
/* Set header complete mode */
// if(ioctl(fd, BIOCSHDRCMPLT, &enable) < 0)
// return -1;
/* Monitor packets sent from our interface */
if(ioctl(fd, BIOCSSEESENT, &enable) < 0)
return -1;
/* Return immediately when a packet received */
if(ioctl(fd, BIOCIMMEDIATE, &enable) < 0)
return -1;
return 0;
}
int installFilter(int fd,
unsigned char Protocol,
unsigned short Port)
{
struct bpf_program bpfProgram = {0};
/* dump IPv4 packets matching Protocol and Port only */
/* @param: fd - Open /dev/bpfX handle. */
/* As an exercise, you might want to extend this to IPv6, as well */
const int IPHeaderOffset = 14;
/* Assuming Ethernet II frames, We have:
*
* Ethernet header = 14 = 6 (dest) + 6 (src) + 2 (ethertype)
* Ethertype is 8-bits (BFP_P) at offset 12
* IP header len is at offset 14 of frame (lower 4 bytes). We use BPF_MSH to isolate field and multiply by 4
* IP fragment data is 16-bits (BFP_H) at offset 6 of IP header, 20 from frame
* IP protocol field is 8-bts (BFP_B) at offset 9 of IP header, 23 from frame
* TCP source port is right after IP header (HLEN*4 bytes from IP header)
* TCP destination port is two bytes later)
*/
struct bpf_insn insns[] = {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 6+6), // Load ethertype 16-bits (12 (6+6) bytes from beginning)
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K , ETHERTYPE_IP, 0, 10), // Compare to requested Ethertype or jump(10) to reject
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), // Load protocol (=14 + 9 (bytes from IP)) bytes from beginning
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K , Protocol, 0, 8), // Compare current to requested Protocol or jump(8) to reject
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), // Move 20 (=14 + 6) We are now on fragment offset field
BPF_JUMP(BPF_JMP + BPF_JSET+ BPF_K , 0x1fff, 6, 0), // Bitwise-AND with 0x1FF and jump(6) to reject if true
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, IPHeaderOffset), // Load IP Header Len (from offset 14) x 4 , into Index register
BPF_STMT(BPF_LD + BPF_H + BPF_IND, IPHeaderOffset), // Skip past IP header (off: 14 + hlen, in BPF_IND), load TCP src
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K , Port, 2, 0), // Compare src port to requested Port and jump to "port" if true
BPF_STMT(BPF_LD + BPF_H + BPF_IND, IPHeaderOffset+2), // Skip two more bytes (off: 14 + hlen + 2), to load TCP dest
/* port */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K , Port, 0, 1), // If port matches, ok. Else reject
/* ok: */
BPF_STMT(BPF_RET + BPF_K, (u_int)-1), // Return -1 (packet accepted)
/* reject: */
BPF_STMT(BPF_RET + BPF_K, 0) // Return 0 (packet rejected)
};
// Load filter into program
bpfProgram.bf_len = sizeof(insns) / sizeof(struct bpf_insn);
bpfProgram.bf_insns = &insns[0];
return(ioctl(fd, BIOCSETF, &bpfProgram));
}
void
read_packets(int fd)
{
char *buf = NULL;
char *p = NULL;
size_t blen = 0;
ssize_t n = 0;
struct bpf_hdr *bh = NULL;
struct ether_header *eh = NULL;
if(ioctl(fd, BIOCGBLEN, &blen) < 0)
return;
if ( (buf = malloc(blen)) == NULL)
return;
(void)printf("reading packets ...\n");
for ( ; ; ) {
(void)memset(buf, '\0', blen);
n = read(fd, buf, blen);
if (n <= 0)
return;
p = buf;
while (p < buf + n) {
bh = (struct bpf_hdr *)p;
/* Start of ethernet frame */
eh = (struct ether_header *)(p + bh->bh_hdrlen);
(void)printf("%02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x "
"[type=%u]\n",
eh->ether_shost[0], eh->ether_shost[1], eh->ether_shost[2],
eh->ether_shost[3], eh->ether_shost[4], eh->ether_shost[5],
eh->ether_dhost[0], eh->ether_dhost[1], eh->ether_dhost[2],
eh->ether_dhost[3], eh->ether_dhost[4], eh->ether_dhost[5],
eh->ether_type);
p += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
}
}
}
#include <CoreFoundation/CoreFoundation.h>
CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef kextIdentifiers,
CFArrayRef infoKeys);
void printUsage()
{
fprintf(stderr,"Usage: kextstat [-b name] [-v] [-x]\n");
fprintf(stderr,"Where: -b: Kext name or LoadTag\n");
fprintf(stderr," -v: verbose\n");
fprintf(stderr," -x: Output as XML (implies -v)\n");
}
const char *display(char *str)
{
// convenience function to trim "com.apple" since on iOS everything is com.apple.*
if (strstr(str,"com.apple."))
return str +strlen("com.apple.");
return (str);
}
// Convert a CFString to a standard C string:
const char* cstring (CFStringRef s) {
return ((const char*) CFStringGetCStringPtr(s,
kCFStringEncodingMacRoman)) ;
}
void
printKext(CFDictionaryRef dict, char *Kext, int format)
{
const void **keys, **values;
CFIndex count = CFDictionaryGetCount(dict);
CFIndex i,j;
int kextNum ;
if (Kext && (int) Kext < 300) { kextNum = (int) Kext; }
else if (Kext) kextNum = atoi(Kext);
if (format == 2) {
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)dict);
if (xml) {
write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml));
CFRelease(xml);
exit(1);
}
}
keys = (void **) malloc (sizeof(void *) * count);
values = (void **) malloc (sizeof(void *) * count);
CFDictionaryGetKeysAndValues (dict, //CFDictionaryRef theDict,
keys, // const void **keys,
values); // const void **values
i = 0;
while (i < count)
{
// we can ignore the key
// char *kextName = cstring(keys[i]);
// values[i] is a dict, so:
if (kextNum) if (i != kextNum) { i++; continue;}
for (j = 0; j < count; j++)
{
int kextTag;
char *name = cstring(CFDictionaryGetValue(values[j], CFSTR("CFBundleIdentifier")));
int l;
if (!kextNum && Kext) { if (!strstr(name, Kext)) { continue;} }
CFNumberGetValue(CFDictionaryGetValue(values[j], CFSTR("OSBundleLoadTag")),
kCFNumberSInt32Type , &kextTag);
if (kextTag == i)
{
int linked = 0;
CFArrayRef linkedAgainst = CFDictionaryGetValue(values[j], CFSTR("OSBundleDependencies"));
printf ("%d %s ", kextTag, display(name));
if (format == 3) { printf(" depends on:\n");}
if (linkedAgainst == NULL)
{
printf("\n");
continue;
}
CFIndex linkedCount = CFArrayGetCount(linkedAgainst);
CFMutableArrayRef marray = CFArrayCreateMutableCopy(NULL, linkedCount, linkedAgainst);
CFArraySortValues(marray,
CFRangeMake(0, linkedCount),
(CFComparatorFunction)CFNumberCompare,
NULL);
if (format <2) printf ("<");
for (l = 0 ; l < linkedCount;l++)
{
CFNumberGetValue(CFArrayGetValueAtIndex(marray,l),
kCFNumberSInt32Type, &linked);
if (format == 3)
{
printf ("\t"); printKext(dict, (char *)linked , 0);
}
else {
if (l) printf(" ");
printf ("%d" , linked);
}
}
if (format <2) printf (">\n");
else if (format ==3) printf ("\n");
}
}
i++;
}
}
int main (int argc, char **argv)
{
int i = 0;
char *kextName = NULL;
int xml = 0;
int verbose = 0;
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i] , "-x") == 0) { xml = 2; continue;}
if (strcmp(argv[i] , "-v") == 0) { verbose = 1; continue;}
if (strcmp(argv[i] , "-b") == 0) { xml = 3;kextName = argv[++i]; continue;}
printUsage();exit(1);
}
CFDictionaryRef kextDict =
OSKextCopyLoadedKextInfo(NULL, // CFArrayRef kextIdentifiers,
NULL); //CFArrayRef infoKeys)
printKext(kextDict ,kextName, xml);
}
#include <stdio.h>
#include <mach/mach.h>
#define IOKIT // to unlock device/device_types..
#include <device/device_types.h> // for io_name, io_string
#include <CoreFoundation/CoreFoundation.h>
// from IOKit/IOKitLib.h
extern const mach_port_t kIOMasterPortDefault;
// from IOKit/IOTypes.h
//typedef mach_port_t io_object_t;
typedef io_object_t io_connect_t;
typedef io_object_t io_enumerator_t;
typedef io_object_t io_iterator_t;
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
kern_return_t
IOServiceGetMatchingServices(
mach_port_t masterPort,
CFDictionaryRef matching,
io_iterator_t * existing );
CFMutableDictionaryRef
IOServiceMatching(
const char * name );
void (*mach_msg_hook)(void);
int main(int argc, char **argv)
{
io_iterator_t deviceList;
io_service_t device;
io_name_t deviceName;
io_string_t devicePath;
char *ioPlaneName = "IOService";
int dev = 0;
kern_return_t kr;
if (argv[1]) ioPlaneName = argv[1];
printf("So far..\n");
// Iterate over all services matching user provided class.
// Note the call to IOServiceMatching, to create the dictionary
hook("libSystem.B.dylib", "mach_msg",mach_msg_hook);
kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOService"),
&deviceList);
if (kr)
{
fprintf(stderr,"IOServiceGetMatchingServices: error\n");
exit(1);
}
if (!deviceList) { fprintf(stderr,"No devices matched\n"); exit(2); }
printf("So far..\n");
while ( IOIteratorIsValid(deviceList) &&
(device = IOIteratorNext(deviceList))) {
kr = IORegistryEntryGetName(device, deviceName);
if (kr)
{
fprintf (stderr,"Error getting name for device\n");
IOObjectRelease(device);
continue;
}
kr = IORegistryEntryGetPath(device, ioPlaneName, devicePath);
if (kr) {
// Device does not exist on this plane
IOObjectRelease(device);
continue;
}
dev++;
printf("%s\t%s\n",deviceName, devicePath);
}
if (device) {
fprintf (stderr,
"Iterator invalidated while getting devices. Did hardware configuration change?\n");
}
return kr;
}
#include <stdio.h>
#include <mach/mach.h>
#define IOKIT // to unlock device/device_types..
#include <device/device_types.h> // for io_name, io_string
#include <CoreFoundation/CoreFoundation.h>
// from IOKit/IOKitLib.h
extern const mach_port_t kIOMasterPortDefault;
// from IOKit/IOTypes.h
//typedef mach_port_t io_object_t;
typedef io_object_t io_connect_t;
typedef io_object_t io_enumerator_t;
typedef io_object_t io_iterator_t;
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
kern_return_t
IOServiceGetMatchingServices(
mach_port_t masterPort,
CFDictionaryRef matching,
io_iterator_t * existing );
CFMutableDictionaryRef
IOServiceMatching(
const char * name );
void listProps(io_service_t Service)
{
CFMutableDictionaryRef propertiesDict;
kern_return_t kr = IORegistryEntryCreateCFProperties( Service,
&propertiesDict,
kCFAllocatorDefault,
kNilOptions );
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)propertiesDict);
if (xml) {
write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml));
CFRelease(xml);
}
printf ("KR: %d\n", kr);
}
int main(int argc, char **argv)
{
io_iterator_t deviceList;
io_service_t device;
io_name_t deviceName;
io_string_t devicePath;
char *ioPlaneName = "IOService";
int dev = 0;
kern_return_t kr;
if (argv[1]) ioPlaneName = argv[1];
printf("So far..\n");
// Iterate over all services matching user provided class.
// Note the call to IOServiceMatching, to create the dictionary
kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOService"),
&deviceList);
if (kr)
{
fprintf(stderr,"IOServiceGetMatchingServices: error\n");
exit(1);
}
if (!deviceList) { fprintf(stderr,"No devices matched\n"); exit(2); }
printf("So far..\n");
while ( IOIteratorIsValid(deviceList) &&
(device = IOIteratorNext(deviceList))) {
kr = IORegistryEntryGetName(device, deviceName);
if (kr)
{
fprintf (stderr,"Error getting name for device\n");
IOObjectRelease(device);
continue;
}
kr = IORegistryEntryGetPath(device, ioPlaneName, devicePath);
if (kr) {
// Device does not exist on this plane
IOObjectRelease(device);
continue;
}
dev++;
printf("%s\t%s\n",deviceName, devicePath);
listProps(device);
}
if (device) {
fprintf (stderr,
"Iterator invalidated while getting devices. Did hardware configuration change?\n");
}
return kr;
}
/**
* filemon.c : A simple FSEvents monitor for iOS and OS X.
* Will compile neatly and run on both (tested on iOS 6.0.1)
*
* (Command line utility.. If you want the GUI, drop me a line)
*
* Comments and suggestions more than welcome!
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h> // for _IOW, a macro required by FSEVENTS_CLONE
#include <sys/types.h> // for uint32_t and friends, on which fsevents.h relies
//#include <sys/_types.h> // for uint32_t and friends, on which fsevents.h relies
#include <sys/sysctl.h> // for sysctl, KERN_PROC, etc.
#include <errno.h>
//#include <sys/fsevents.h> would have been nice, but it's no longer available, as Apple
// now wraps this with FSEventStream. So instead - rip what we need from the kernel headers..
// Actions for each event type
#define FSE_IGNORE 0
#define FSE_REPORT 1
#define FSE_ASK 2 // Not implemented yet
#define FSEVENTS_CLONE _IOW('s', 1, fsevent_clone_args)
#define FSE_INVALID -1
#define FSE_CREATE_FILE 0
#define FSE_DELETE 1
#define FSE_STAT_CHANGED 2
#define FSE_RENAME 3
#define FSE_CONTENT_MODIFIED 4
#define FSE_EXCHANGE 5
#define FSE_FINDER_INFO_CHANGED 6
#define FSE_CREATE_DIR 7
#define FSE_CHOWN 8
#define FSE_XATTR_MODIFIED 9
#define FSE_XATTR_REMOVED 10
#define FSE_MAX_EVENTS 11
#define FSE_ALL_EVENTS 998
#define FSE_EVENTS_DROPPED 999
// The types of each of the arguments for an event
// Each type is followed by the size and then the
// data. FSE_ARG_VNODE is just a path string
#define FSE_ARG_VNODE 0x0001 // next arg is a vnode pointer
#define FSE_ARG_STRING 0x0002 // next arg is length followed by string ptr
#define FSE_ARG_PATH 0x0003 // next arg is a full path
#define FSE_ARG_INT32 0x0004 // next arg is a 32-bit int
#define FSE_ARG_INT64 0x0005 // next arg is a 64-bit int
#define FSE_ARG_RAW 0x0006 // next arg is a length followed by a void ptr
#define FSE_ARG_INO 0x0007 // next arg is the inode number (ino_t)
#define FSE_ARG_UID 0x0008 // next arg is the file's uid (uid_t)
#define FSE_ARG_DEV 0x0009 // next arg is the file's dev_t
#define FSE_ARG_MODE 0x000a // next arg is the file's mode (as an int32, file type only)
#define FSE_ARG_GID 0x000b // next arg is the file's gid (gid_t)
#define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid)
#define FSE_ARG_DONE 0xb33f // no more arguments
#if __LP64__
typedef struct fsevent_clone_args {
int8_t *event_list;
int32_t num_events;
int32_t event_queue_depth;
int32_t *fd;
} fsevent_clone_args;
#else
typedef struct fsevent_clone_args {
int8_t *event_list;
int32_t pad1;
int32_t num_events;
int32_t event_queue_depth;
int32_t *fd;
int32_t pad2;
} fsevent_clone_args;
#endif
// copied from bsd/vfs/vfs_events.c
#pragma pack(1) // to be on the safe side. Not really necessary.. struct fields are aligned.
typedef struct kfs_event_a {
uint16_t type;
uint16_t refcount;
pid_t pid;
} kfs_event_a;
typedef struct kfs_event_arg {
uint16_t type;
uint16_t pathlen;
char data[0];
} kfs_event_arg;
#pragma pack()
#define BUFSIZE 64 *1024
// Utility functions
const char *
typeToString (uint32_t Type)
{
switch (Type)
{
case FSE_CREATE_FILE: return ("Created ");
case FSE_DELETE: return ("Deleted ");
case FSE_STAT_CHANGED: return ("Stat changed ");
case FSE_RENAME: return ("Renamed ");
case FSE_CONTENT_MODIFIED: return ("Modified ");
case FSE_CREATE_DIR: return ("Created dir ");
case FSE_CHOWN: return ("Chowned ");
case FSE_EXCHANGE: return ("Exchanged "); /* 5 */
case FSE_FINDER_INFO_CHANGED: return ("Finder Info changed for "); /* 6 */
case FSE_XATTR_MODIFIED: return ("Extended attributes changed for "); /* 9 */
case FSE_XATTR_REMOVED: return ("Extended attributesremoved for "); /* 10 */
default : return ("Not yet ");
}
}
char *
getProcName(long pid)
{
static char procName[4096];
int len = 1000;
int rc;
int mib[4];
memset(procName, '\0', 4096);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
if ((rc = sysctl(mib, 4, procName, &len, NULL,0)) < 0)
{
perror("trace facility failure, KERN_PROC_PID\n");
exit(1);
}
//printf ("GOT PID: %d and rc: %d - %s\n", mib[3], rc, ((struct kinfo_proc *)procName)->kp_proc.p_comm);
return (((struct kinfo_proc *)procName)->kp_proc.p_comm);
}
int
doArg(char *arg)
{
// Dump an arg value
// Quick and dirty, but does the trick..
unsigned short *argType = (unsigned short *) arg;
unsigned short *argLen = (unsigned short *) (arg + 2);
uint32_t *argVal = (uint32_t *) (arg+4);
uint64_t *argVal64 = (uint64_t *) (arg+4);
dev_t *dev;
char *str;
switch (*argType)
{
case FSE_ARG_INT64: // This is a timestamp field on the FSEvent
printf ("Arg64: %lld\n", *argVal64);
break;
case FSE_ARG_STRING: // This is a filename, for move/rename (Type 3)
str = (char *)argVal;
printf("%s ", str);
break;
case FSE_ARG_DEV: // Device, corresponding to block device on which fs is mounted
dev = (dev_t *) argVal;
printf ("DEV: %d,%d ", major(*dev), minor(*dev)); break;
case FSE_ARG_MODE: // mode bits, etc
printf("MODE: %x ", *argVal); break;
case FSE_ARG_PATH: // Not really used... Implement this later..
printf ("PATH: " ); break;
case FSE_ARG_INO: // Inode number (unique up to device)
printf ("INODE: %d ", *argVal); break;
case FSE_ARG_UID: // UID of operation performer
printf ("UID: %d ", *argVal); break;
case FSE_ARG_GID: // Ditto, GID
printf ("GID: %d ", *argVal); break;
case FSE_ARG_FINFO: // Not handling this yet.. Not really used, either..
printf ("FINFO\n"); break;
case FSE_ARG_DONE: printf("\n");return 2;
default:
printf ("(ARG of type %hd, len %hd)\n", *argType, *argLen);
}
return (4 + *argLen);
}
// And.. Ze Main
void
main (int argc, char **argv)
{
int fsed, cloned_fsed;
int i;
int rc;
fsevent_clone_args clone_args;
unsigned short *arg_type;
char buf[BUFSIZE];
// Open the device
fsed = open ("/dev/fsevents", O_RDONLY);
int8_t events[FSE_MAX_EVENTS];
if (geteuid())
{
fprintf(stderr,"Opening /dev/fsevents requires root permissions\n");
}
if (fsed < 0)
{
perror ("open");
exit(1);
}
// Prepare event mask list. In our simple example, we want everything
// (i.e. all events, so we say "FSE_REPORT" all). Otherwise, we
// would have to specifically toggle FSE_IGNORE for each:
//
// e.g.
// events[FSE_XATTR_MODIFIED] = FSE_IGNORE;
// events[FSE_XATTR_REMOVED] = FSE_IGNORE;
// etc..
for (i = 0; i < FSE_MAX_EVENTS; i++)
{
events[i] = FSE_REPORT;
}
// Get ready to clone the descriptor:
memset(&clone_args, '\0', sizeof(clone_args));
clone_args.fd = &cloned_fsed; // This is the descriptor we get back
clone_args.event_queue_depth = 10;
clone_args.event_list = events;
clone_args.num_events = FSE_MAX_EVENTS;
// Do it.
rc = ioctl (fsed, FSEVENTS_CLONE, &clone_args);
if (rc < 0) { perror ("ioctl"); exit(2);}
// We no longer need original..
close (fsed);
// And now we simply read, ad infinitum (aut nauseam)
while ((rc = read (cloned_fsed, buf, BUFSIZE)) > 0)
{
// rc returns the count of bytes for one or more events:
int offInBuf = 0;
while (offInBuf < rc) {
struct kfs_event_a *fse = (struct kfs_event_a *)(buf + offInBuf);
struct kfs_event_arg *fse_arg;
// if (offInBuf) { printf ("Next event: %d\n", offInBuf);};
printf ("%s (PID:%d) %s ", getProcName(fse->pid), fse->pid , typeToString(fse->type) );
offInBuf+= sizeof(struct kfs_event_a);
fse_arg = (struct kfs_event_arg *) &buf[offInBuf];
printf ("%s\n", fse_arg->data);
offInBuf += sizeof(kfs_event_arg) + fse_arg->pathlen ;
int arg_len = doArg(buf + offInBuf);
offInBuf += arg_len;
while (arg_len >2)
{
arg_len = doArg(buf + offInBuf);
offInBuf += arg_len;
}
}
if (rc > offInBuf) { printf ("***Warning: Some events may be lost\n"); }
}
}
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h> // for _IOW, a macro required by FSEVENTS_CLONE
#include <sys/types.h> // for uint32_t and friends, on which fsevents.h relies
#include <unistd.h>
#include <string.h> // memset
//#include <sys/_types.h> // for uint32_t and friends, on which fsevents.h relies
#include <sys/sysctl.h> // for sysctl, KERN_PROC, etc.
#include <errno.h>
//#include <sys/fsevents.h>
#include "fsevents.h"
#include "colors.h"
#include <signal.h> // for kill
/**
*
* Filemon: A simple but useful fsevents client utilty for OS X and iOS
*
* Author: Jonathan Levin, TWTR: @Morpheus______ http://NewOSXBook.com/
*
* For details, q.v. MOXiI 1st Ed, chapter 3 (pp 74-78), or MOXiI II, Volume I, Chapter 5
*
* Source is wide open, so you are free to use and abuse, but leaving credit to the original
* author and the book website (http://NewOSXBook.com/) would be appreciated.
*
* New in 2.0:
* - Supports filters: Process IDs, names or events
* - Supports auto-stop and auto-link
* - Color
*
*/
int g_dumpArgs =0;
#define BUFSIZE 256 *1024
#define COLOR_OP YELLOW
#define COLOR_PROC BLUE
#define COLOR_PATH CYAN
// Utility functions
static char *
typeToString (uint32_t Type)
{
switch (Type)
{
case FSE_CREATE_FILE: return ("Created ");
case FSE_DELETE: return ("Deleted ");
case FSE_STAT_CHANGED: return ("Changed stat ");
case FSE_RENAME: return ("Renamed ");
case FSE_CONTENT_MODIFIED: return ("Modified ");
case FSE_CREATE_DIR: return ("Created dir ");
case FSE_CHOWN: return ("Chowned ");
case FSE_EXCHANGE: return ("Exchanged "); /* 5 */
case FSE_FINDER_INFO_CHANGED: return ("Finder Info "); /* 6 */
case FSE_XATTR_MODIFIED: return ("Changed xattr "); /* 9 */
case FSE_XATTR_REMOVED: return ("Removed xattr "); /* 10 */
case FSE_DOCID_CREATED: return ("DocID created "); // 11
case FSE_DOCID_CHANGED: return ("DocID changed "); // 12
default : return ("?! ");
}
}
int lastPID = 0;
static char *
getProcName(long pid)
{
static char procName[4096];
size_t len = 1000;
int rc;
int mib[4];
// minor optimization
if (pid != lastPID)
{
memset(procName, '\0', 4096);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
if ((rc = sysctl(mib, 4, procName, &len, NULL,0)) < 0)
{
perror("trace facility failure, KERN_PROC_PID\n");
exit(1);
}
//printf ("GOT PID: %d and rc: %d - %s\n", mib[3], rc, ((struct kinfo_proc *)procName)->kp_proc.p_comm);
lastPID = pid;
}
return (((struct kinfo_proc *)procName)->kp_proc.p_comm);
}
int
doArg(char *arg, int Print)
{
// Dump an arg value
unsigned short *argType = (unsigned short *) arg;
unsigned short *argLen = (unsigned short *) (arg + 2);
uint32_t *argVal = (uint32_t *) (arg+4);
uint64_t *argVal64 = (uint64_t *) (arg+4);
dev_t *dev;
char *str;
switch (*argType)
{
case FSE_ARG_INT64: // This is a timestamp field on the FSEvent
if (g_dumpArgs) printf ("Arg64: %lld ", *argVal64);
break;
case FSE_ARG_STRING:
str = (char *)argVal;
if (Print) printf("%s ", str);
break;
case FSE_ARG_DEV:
dev = (dev_t *) argVal;
if (g_dumpArgs) printf ("DEV: %d,%d ", major(*dev), minor(*dev)); break;
case FSE_ARG_MODE:
if (g_dumpArgs) printf("MODE: %x ", *argVal); break;
case FSE_ARG_PATH:
printf ("PATH: " ); break;
case FSE_ARG_INO:
if (g_dumpArgs) printf ("INODE: %d ", *argVal); break;
case FSE_ARG_UID:
if (g_dumpArgs) printf ("UID: %d ", *argVal); break;
case FSE_ARG_GID:
if (g_dumpArgs) printf ("GID: %d ", *argVal); break;
#define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid)
case FSE_ARG_FINFO:
printf ("FINFO\n"); break;
case FSE_ARG_DONE: if (Print)printf("\n");return 2;
default:
printf ("(ARG of type %hd, len %hd)\n", *argType, *argLen);
exit(0);
}
return (4 + *argLen);
}
#define COLOR_OPTION "-c"
#define COLOR_LONG_OPTION "--color"
#define FILTER_PROC_OPTION "-p"
#define FILTER_PROC_LONG_OPTION "--proc"
#define FILTER_FILE_OPTION "-f"
#define FILTER_FILE_LONG_OPTION "--file"
#define FILTER_EVENT_OPTION "-e"
#define FILTER_EVENT_LONG_OPTION "--event"
#define STOP_OPTION "-s"
#define STOP_LONG_OPTION "--stop"
#define LINK_OPTION "-l"
#define LINK_LONG_OPTION "--link"
void
usage()
{
fprintf(stderr,"Usage: %s [options]\n", getprogname());
fprintf(stderr,"Where [options] are optional, and may be any of:\n");
fprintf(stderr,"\t" FILTER_PROC_OPTION "|" FILTER_PROC_LONG_OPTION " pid/procname: filter only this process or PID\n");
fprintf(stderr,"\t" FILTER_FILE_OPTION "|" FILTER_FILE_LONG_OPTION " string[,string]: filter only paths containing this string (/ will catch everything)\n");
fprintf(stderr,"\t" FILTER_EVENT_OPTION "|" FILTER_EVENT_LONG_OPTION " event[,event]: filter only these events\n");
fprintf(stderr,"\t" STOP_OPTION "|" STOP_LONG_OPTION ": auto-stop the process generating event\n");
fprintf(stderr,"\t" LINK_OPTION "|" LINK_LONG_OPTION ": auto-create a hard link to file (prevents deletion by program :-)\n");
fprintf(stderr,"\t" COLOR_OPTION "|" COLOR_LONG_OPTION " (or set JCOLOR=1 first)\n");
}
// And.. Ze Main
#define MAX_FILTERS 10
int
interesting_process (int pid, char *Filters[], int NumFilters)
{
if (!NumFilters) return 1;
return 0;
}
int
interesting_file (char *FileName, char *Filters[], int NumFilters)
{
// if no filters - everything is interesting
if (!NumFilters) return 1;
while (NumFilters > 0)
{
// fprintf(stderr,"Checking %s vs %s\n", FileName, Filters[NumFilters-1]);
if (strstr(FileName, Filters[NumFilters-1])) return 1;
NumFilters--;
}
return 0;
}
int
main (int argc, char **argv)
{
int fsed, cloned_fsed;
int i;
int rc;
fsevent_clone_args clone_args;
unsigned short *arg_type;
char buf[BUFSIZE];
int autostop = 0;
int autolink = 0;
char *fileFilters[MAX_FILTERS]= { 0 };
char *procFilters[MAX_FILTERS]= { 0 };
int numFileFilters = 0;
int numProcFilters = 0;
int color = 0;
if (getenv("JCOLOR")) color++;
//if (argc > 1) { if (strcmp(argv[1],"-v") == 0) g_dumpArgs++; }
int arg = 1;
for (arg = 1; arg < argc; arg++)
{
if (strcmp(argv[arg], "-h") == 0) { usage(); exit(1);}
if ((strcmp(argv[arg], FILTER_PROC_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_PROC_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
numProcFilters++;
arg++; continue;
}
if ((strcmp(argv[arg], FILTER_EVENT_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_EVENT_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
arg++; continue;
}
if ((strcmp(argv[arg], FILTER_FILE_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_FILE_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
// Got it - add filters, separate by ","
char *begin = argv[arg+1];
char *sep = strchr (begin, ',');
while (sep)
{
*sep = '\0';
fprintf(stderr,"Adding File filter %d: %s\n", numFileFilters, begin);
fileFilters[numFileFilters++] = strdup(begin);
begin = sep + 1;
sep = strchr (begin, ',');
}
fprintf(stderr,"Adding File filter %d: %s\n", numFileFilters, begin);
fileFilters[numFileFilters++] = strdup(begin);
arg++; continue;
}
if ((strcmp(argv[arg], COLOR_OPTION) == 0) || (strcmp(argv[arg], COLOR_LONG_OPTION) == 0))
{
color++;
continue;
}
if ((strcmp(argv[arg], STOP_OPTION) == 0) || (strcmp(argv[arg], STOP_LONG_OPTION) == 0))
{
autostop++;
continue;
}
if ((strcmp(argv[arg], LINK_OPTION) == 0) || (strcmp(argv[arg], LINK_LONG_OPTION) == 0))
{
autolink++;
continue;
}
fprintf(stderr, "%s: Unknown option\n", argv[arg]); exit(3);
}
// This is for your own good: If autostop is allowed on everything, there is a chance a kill -STOP will be
// sent to your own terminal app or SSH.
if (autostop && (!numFileFilters && !numProcFilters))
{
fprintf(stderr,"Error: Cannot allow auto-stopping of processes without either a file or process filter.\nIf you are sure you want to do this, set a null filter\n"); exit(4);
}
// Open the device
fsed = open ("/dev/fsevents", O_RDONLY);
int8_t events[FSE_MAX_EVENTS];
if (geteuid())
{
fprintf(stderr,"Opening /dev/fsevents requires root permissions\n");
}
if (fsed < 0)
{
perror ("open");
exit(1);
}
// Prepare event mask list. In our simple example, we want everything
// (i.e. all events, so we say "FSE_REPORT" all). Otherwise, we
// would have to specifically toggle FSE_IGNORE for each:
//
// e.g.
// events[FSE_XATTR_MODIFIED] = FSE_IGNORE;
// events[FSE_XATTR_REMOVED] = FSE_IGNORE;
// etc..
for (i = 0; i < FSE_MAX_EVENTS; i++)
{
events[i] = FSE_REPORT;
}
// But in v2.0, we allow user to specify events
// Get ready to clone the descriptor:
memset(&clone_args, '\0', sizeof(clone_args));
clone_args.fd = &cloned_fsed; // This is the descriptor we get back
clone_args.event_queue_depth = 100; // Makes all the difference
clone_args.event_list = events;
clone_args.num_events = FSE_MAX_EVENTS;
// Do it.
rc = ioctl (fsed, FSEVENTS_CLONE, &clone_args);
if (rc < 0) { perror ("ioctl"); exit(2);}
// We no longer need original..
close (fsed);
// And now we simply read, ad infinitum (aut nauseam)
while ((rc = read (cloned_fsed, buf, BUFSIZE)) > 0)
{
// rc returns the count of bytes for one or more events:
int offInBuf = 0;
while (offInBuf < rc) {
// printf("----%d/%d bytes\n",offInBuf,rc);
struct kfs_event_a *fse = (struct kfs_event_a *)(buf + offInBuf);
struct kfs_event_arg *fse_arg;
// if (offInBuf) { printf ("Next event: %d\n", offInBuf);};
if (fse->type == FSE_EVENTS_DROPPED)
{
printf("Some events dropped\n");
break;
}
if (!fse->pid){ printf ("%x %x\n", fse->type, fse->refcount); }
int print = 0;
char *procName = getProcName(fse->pid);
offInBuf+= sizeof(struct kfs_event_a);
fse_arg = (struct kfs_event_arg *) &buf[offInBuf];
if (interesting_process(fse->pid, procFilters, numProcFilters)
&& interesting_file (fse_arg->data, fileFilters, numFileFilters))
{
printf ("%5d %s%s%s\t%s%s%s ", fse->pid,
color ? COLOR_PROC: "" , procName, color ? NORMAL : "",
color ? COLOR_OP : "", typeToString(fse->type), color ? NORMAL : "" );
// The path name is null terminated, so that's cool
printf ("%s%s%s\t",
color ? COLOR_PATH : "" , fse_arg->data, color ? NORMAL :"");
if (fse->type == FSE_CREATE_FILE && autolink && (fse->pid != getpid()))
{
int fileLen = strlen(fse_arg->data);
char *linkName = malloc (fileLen + 20);
strcpy(linkName, fse_arg->data);
snprintf(linkName + fileLen, fileLen + 20, ".filemon.%d", autolink);
int rc = link (fse_arg->data, linkName);
if (rc) { fprintf(stderr,"%sWarning: Unable to autolink %s%s - file must have been deleted already\n",
color ? RED : "",
fse_arg->data,
color ? NORMAL : "");}
else { fprintf(stderr,"%sAuto-linked %s to %s%s\n",
color ? GREEN : "",
fse_arg->data, linkName,
color ? NORMAL :"");
autolink++;
}
free (linkName);
}
// Autostop only if this is a file creation, and interesting
if (autostop && fse->type == FSE_CREATE_FILE ) {
fprintf(stderr, "%sAuto-stopping process %s (%d) on file operation%s\n",
color ? RED : "",
procName,
fse->pid,
color ? NORMAL : "");
kill (fse->pid, SIGSTOP);
}
print = 1;
}
offInBuf += sizeof(kfs_event_arg) + fse_arg->pathlen ;
int arg_len = doArg(buf + offInBuf,print);
offInBuf += arg_len;
while (arg_len >2 && offInBuf < rc)
{
arg_len = doArg(buf + offInBuf, print);
offInBuf += arg_len;
}
}
memset (buf,'\0', BUFSIZE);
if (rc > offInBuf) { printf ("*** Warning: Some events may be lost\n"); }
}
} // end main
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <malloc/malloc.h> // for malloc_printf()
// Note: Compile with GCC, not cc (important)
//
//
// This is the expected interpose structure
typedef struct interpose_s { void *new_func;
void *orig_func; } interpose_t;
// Our prototypes - requires since we are putting them in
// the interposing_functions, below
void *my_malloc(int size); // matches real malloc()
void my_free (void *); // matches real free()
// For clang, add attribute(used)
static const interpose_t interposing_functions[] \
__attribute__ ((used, section("__DATA, __interpose"))) = {
{ (void *)my_free, (void *)free },
{ (void *)my_malloc, (void *)malloc }
};
void *
my_malloc (int size) {
// In our function we have access to the real malloc() -
// and since we don’t want to mess with the heap ourselves,
// just call it
//
void *returned = malloc(size);
// call malloc_printf() because the real printf() calls malloc()
// // internally - and would end up calling us, recursing ad infinitum
malloc_printf ( "+ %p %d\n", returned, size); return (returned);
}
void
my_free (void *freed) {
// Free - just print the address, then call the real free()
malloc_printf ( "- %p\n", freed); free(freed);
}
#if 0
From output 4-11:
morpheus@Ergo(~)$ gcc -dynamiclib l.c -o libMTrace.dylib -Wall // compile to dylib
morpheus@Ergo(~)$ DYLD_INSERT_LIBRARIES=libMTrace.dylib ls // force insert into ls
ls(24346) malloc: + 0x100100020 88
ls(24346) malloc: + 0x100800000 4096
ls(24346) malloc: + 0x100801000 2160
ls(24346) malloc: - 0x100800000
ls(24346) malloc: + 0x100801a00 3312 ... // etc.
#endif
#include <stdlib.h> // for malloc
#include <stdio.h>
#include <string.h>
#include <sys/sysctl.h>
#include <errno.h>
/***
* OBSOLETE - AAPL invalidated the stack_snapshot system call (#365) in favor
* of micro_stackshot[with_config] (#492/491) in XNU 3248 and later
*
***/
//typedef unsigned int uint32_t;
//typedef unsigned long long uint64_t;
#define STACKSHOT_TASK_SNAPSHOT_MAGIC 0xdecafbad
#define STACKSHOT_THREAD_SNAPSHOT_MAGIC 0xfeedface
#define STACKSHOT_MEM_SNAPSHOT_MAGIC 0xabcddcba
// This is new in ML, apparently. Looks like this API ain't going away anytime soon!
#define STACKSHOT_MEM_AND_IO_SNAPSHOT_MAGIC 0xbfcabcde
// The following are from osfmk/kern/thread.h
#define TH_WAIT 0x01 /* queued for waiting */
#define TH_SUSP 0x02 /* stopped or requested to stop */
#define TH_RUN 0x04 /* running or on runq */
#define TH_UNINT 0x08 /* waiting uninteruptibly */
#define TH_TERMINATE 0x10 /* halted at termination */
#define TH_TERMINATE2 0x20 /* added to termination queue */
#define TH_IDLE 0x80 /* idling processor */
char *stateToText (int32_t state)
{
static char stateText[160];
// using snprintf and strncat to avoid linking with _chk variants
snprintf(stateText,80,"%d - ", state);
if (state & TH_WAIT) strncat (stateText , "waiting ",80);
if (state & TH_SUSP) strncat (stateText , "suspended ",80);
if (state & TH_RUN) strncat (stateText , "running ",80);
if (state & TH_UNINT) strncat (stateText , "Uninterruptible ",80 );
if (state & TH_TERMINATE) strncat (stateText , "Halted at termination ",80);
if (state & TH_TERMINATE2) strncat (stateText , "Added to termination queue ",80);
if (state & TH_IDLE) strncat (stateText , "Idling processor ",80);
return (stateText);
}
#pragma pack(0)
#ifdef LP64
struct frame {
void *retaddr;
void *fp;
};
struct uframe {
void *retaddr;
void *fp;
};
#else
struct uframe {
void *retaddr;
void *fp;
};
struct frame {
uint32_t retaddr;
uint32_t fp;
};
#endif
// Ripped from osfmk/kern/debug.h
struct mem_and_io_snapshot {
uint32_t snapshot_magic;
uint32_t free_pages;
uint32_t active_pages;
uint32_t inactive_pages;
uint32_t purgeable_pages;
uint32_t wired_pages;
uint32_t speculative_pages;
uint32_t throttled_pages;
int busy_buffer_count;
uint32_t pages_wanted;
uint32_t pages_reclaimed;
uint8_t pages_wanted_reclaimed_valid; // did mach_vm_pressure_monitor succeed?
} __attribute__((packed));
#ifndef LION
struct task_snapshot {
uint32_t snapshot_magic;
int32_t pid;
uint32_t nloadinfos;
char ss_flags;
/* We restrict ourselves to a statically defined
* (current as of 2009) length for the
* p_comm string, due to scoping issues (osfmk/bsd and user/kernel
* binary compatibility).
*/
char p_comm[17];
} __attribute__ ((packed));
struct thread_snapshot {
uint32_t snapshot_magic;
uint32_t nkern_frames;
uint32_t nuser_frames;
uint64_t wait_event;
uint64_t continuation;
uint64_t thread_id;
int32_t state;
char ss_flags;
} __attribute__ ((packed));
#else
struct thread_snapshot {
uint32_t snapshot_magic;
uint32_t nkern_frames;
uint32_t nuser_frames;
uint64_t wait_event;
uint64_t continuation;
uint64_t thread_id;
uint64_t user_time;
uint64_t system_time;
int32_t state;
char ss_flags;
// Struct appears to be off by 8. These are always 0. Possibly reserved? Need to check..
uint32_t res1;
uint32_t res2;
} __attribute__ ((packed));
struct task_snapshot {
uint32_t snapshot_magic;
int32_t pid;
uint32_t nloadinfos;
uint64_t user_time_in_terminated_threads;
uint64_t system_time_in_terminated_threads;
int suspend_count;
int task_size; // pages
int faults; // number of page faults
int pageins; // number of actual pageins
int cow_faults; // number of copy-on-write faults
char ss_flags;
/* We restrict ourselves to a statically defined
* (current as of 2009) length for the
* p_comm string, due to scoping issues (osfmk/bsd and user/kernel
* binary compatibility).
*/
char p_comm[17];
} __attribute__ ((packed));
#endif
#pragma pack()
int stack_snapshot(int pid, char *tracebuf, int bufsize, int options)
{
/* Inputs: uap->pid - process id of process to be traced, or -1
* for the entire system
* uap->tracebuf - address of the user space destination
* buffer
* uap->tracebuf_size - size of the user space trace buffer
* uap->options - various options, including the maximum
* number of frames to trace.
*/
return syscall (365, pid, tracebuf, bufsize, options);
}
int dump_mem_and_io_snapshot(struct mem_and_io_snapshot *mais)
{
printf ("Pages: Free: %-8d Active: %-8d Purgeable: %-8d Wired: %-8d\n",
mais->free_pages, mais->active_pages, mais->purgeable_pages, mais->wired_pages,
mais->speculative_pages);
printf (" Speculative: %-8d Throttled: %-8d Wanted: %-8d Reclaimed: %-8d\n",
mais->speculative_pages, mais->throttled_pages, mais->pages_wanted, mais->pages_reclaimed);
return (0);
}
int dump_thread_snapshot(struct thread_snapshot *ths)
{
if (ths->snapshot_magic != STACKSHOT_THREAD_SNAPSHOT_MAGIC)
{
fprintf(stderr,"Error: Magic %p expected, Found %p\n", STACKSHOT_TASK_SNAPSHOT_MAGIC, ths->snapshot_magic);
return (0);
}
printf ("\tThread ID: 0x%x ", ths->thread_id) ;
printf ("State: %s\n" , stateToText(ths->state));
if (ths->wait_event) printf ("\tWaiting on: 0x%x ", ths->wait_event) ;
if (ths->continuation) {
printf ("\tContinuation: %p\n", ths->continuation);
}
return (ths->nkern_frames + ths->nuser_frames);
}
void dump_task_snapshot(struct task_snapshot *ts)
{
if (ts->snapshot_magic != STACKSHOT_TASK_SNAPSHOT_MAGIC)
{
fprintf(stderr,"Error: Magic %p expected, Found %p\n", STACKSHOT_TASK_SNAPSHOT_MAGIC, ts->snapshot_magic);
return;
}
fprintf(stdout, "PID: %d (%s) Flags: %x \n", ts->pid, ts->p_comm,ts->ss_flags);
}
int main (int argc, char **argv)
{
// Have to supply a really large buffer - else we'll get error 28
// Since this is a proof of concept, just put 500,000 - that oughtta do
// the trick. The right way would be to malloc/realloc an increasingly larger
// buffer while failing
#define BUFSIZE 500000
char buf[BUFSIZE];
int rc = stack_snapshot(-1, // All processes
buf, // Save to buffer
BUFSIZE, // Duh
100); // # of frames to trace
struct task_snapshot *ts;
struct thread_snapshot *ths;
int off = 0;
int warn = 0;
int nframes = 0;
int is64bit = 0;
if (rc <0) { perror ("stack_snapshot"); return (-1); }
while (off< rc) {
int x;
ts = (struct task_snapshot *) (buf + off);
ths = (struct thread_snapshot *) (buf + off);
switch (ts->snapshot_magic)
{
case STACKSHOT_MEM_AND_IO_SNAPSHOT_MAGIC:
dump_mem_and_io_snapshot(ts);
off += sizeof(struct mem_and_io_snapshot);
break;
case STACKSHOT_TASK_SNAPSHOT_MAGIC:
printf ("OFF: %d\n", off);
dump_task_snapshot(ts);
if (ts->ss_flags & 0x1) is64bit = 1; else is64bit = 0;
off+= sizeof(struct task_snapshot);
warn = 0;
break;
case STACKSHOT_THREAD_SNAPSHOT_MAGIC:
printf ("OFF: %d\n", off);
nframes = dump_thread_snapshot(ths);
off+= sizeof(struct thread_snapshot);
if (ths->nkern_frames || ths->nuser_frames)
{
printf ("\tFrames: %d kernel %d user\n", ths->nkern_frames, ths->nuser_frames);
// User first
while (ths->nuser_frames)
{
// This could be a 32-bit frame or a 64-bit frame
struct uframe *f = (struct uframe *) (buf + off);
printf ("\t\t%p\t%p\n", f->retaddr, f->fp);
ths->nuser_frames--;
off += (is64bit)? 16 : 8;
}
while (ths->nkern_frames)
{
// This could be a 32-bit frame or a 64-bit frame
struct frame *f = (struct frame *) (buf + off);
printf ("\t\t%p\t%p\n", f->retaddr, f->fp);
ths->nkern_frames--;
off += 16;
}
}
warn = 0;
break;
case STACKSHOT_MEM_SNAPSHOT_MAGIC:
printf ("MEM magic\n");
break;
default:
if (!warn) {
warn++;
fprintf(stdout, "Magic %p at offset %d? Seeking to next magic\n", ts->snapshot_magic, off);}
off++;;
} // end switch
} // end while
}
#include <sys/mman.h> // For mmap(2)
#include <sys/stat.h> // For stat(2)
#include <unistd.h> // For everything else
#include <fcntl.h> // O_RDONLY
#include <stdio.h> // printf!
#include <string.h> // str*, mem*
#include <stdlib.h> // exit..
/**
* Imagine: A rudimentary (decrypted) img3 file format dumper,
* With specific focus on device tree files
*
*
* (No, this will NOT decrypt the files - you'll need xpwntool or other
* utility to do that)
*
* Coded by Jonathan Levin - http://www.newosxbook.com
*
* Possible improvements:
* - Refactor into a library
* - Tidy up the (very dirty) code
* - Show tree values, not just names (left as an exercise)
*
*/
typedef unsigned int uint32_t;
#include "dt.h" // for DeviceTree
typedef struct img3 {
uint32_t magic;
uint32_t fullSize;
uint32_t sizeNoPack;
uint32_t sigCheckArea;
uint32_t ident;
} img3;
typedef struct tag {
uint32_t magic;
uint32_t total_length;
uint32_t data_length;
unsigned char data[0];
} tag;
#define IMG3_MAGIC 0x496d6733
#define TAG_TYPE 0x54595045
#define TAG_DATA 0x44415441
#define TAG_VERS 0x56455253
#define TAG_SEPO 0x5345504f
#define TAG_CHIP 0x43484950
#define TAG_BORD 0x424f5244
#define TAG_KBAG 0x4b424147
#define TAG_SHSH 0x53485348
#define TAG_CERT 0x43455254
#define TYPE_DTRE 0x65727464
int g_Dump = 0;
void
dump (unsigned char *data, int len)
{
int i;
for (i = 0 ; i < len; i++)
{
printf ("%02x ", data[i]);
}
printf ("\n");
}
void copyValue (char *dest, char *src, int length)
{
char temp[1024];
int i = 0;
for (i = 0; src[i] || i < length; i++);
if (i != length){ strcpy(dest, "(null)"); return;}
memcpy(dest, src,length);
}
uint32_t
dumpTreeNode(DeviceTreeNode *Node, int indent)
{
char buffer[40960];
char temp[10240];
char *name;
int prop = 0, child = 0;
int i = 0;
memset(buffer, '\0', 4096);
DeviceTreeNodeProperty *dtp = (DeviceTreeNodeProperty * ) ((char*)Node + sizeof(DeviceTreeNode));
char *offset = 0;
for (prop = 0; prop < Node->nProperties; prop++)
{
char *val;
temp[0] = '\0'; // strcat will do the rest
for (i=0; i< indent ; i++) { strcat(temp,"| "); }
strcat (temp, "+--");
strncat (buffer, temp, 1024);
sprintf (temp, "%s %d bytes: ", dtp->name, dtp->length);
strncat (buffer, temp, 1024);
if (strcmp(dtp->name,"name") == 0)
{
name = (char *) &dtp->length + sizeof(uint32_t);
strncat(buffer, name, dtp->length);
strcat (buffer,"\n");
}
else
{
copyValue (temp, ((char *) &dtp->length) + sizeof(uint32_t), dtp->length);
// Yeah, Yeah, Buffer overflows, etc.. :-)
strcat (buffer, temp);
strcat(buffer, "\n");
}
dtp = ((char *) dtp) + sizeof(DeviceTreeNodeProperty) + dtp->length ;
// Align
dtp = (((long) dtp %4) ? ((char *) dtp) + (4 - ((long)dtp) %4) : dtp);
offset = (char *) dtp;
}
for (i=0; i< indent-1; i++) { printf(" "); }
if (indent>1) printf ("+--");
printf ("%s:\n", name);
printf (buffer);
// Now do children:
for (child = 0; child < Node->nChildren; child++)
{
offset+= dumpTreeNode ( (DeviceTreeNode *) offset, indent+1 );
}
return ( (char *) offset - (char*) Node);
}
void
doData (char *data, int tag, int len)
{
printf ("\tData of type 0x%x and length %d bytes\n", tag, len);
switch (tag)
{
case TYPE_DTRE:
{
DeviceTreeNode *dtn = (DeviceTreeNode *) data;
DeviceTreeNode *root = (DeviceTreeNode *) data;
int prop = 0;
if (dtn->nProperties > 20)
{
printf ("\tMore than 20 properties? Did you hand me an encrypted file?\n");
return;
}
printf ("\tDevice Tree with %d properties and %d children\n",
dtn->nProperties, dtn->nChildren);
if (g_Dump)
{
printf ("Properties:\n");
dumpTreeNode (dtn,1);
}
else { printf("\tUse -d to dump the device tree\n");}
}
}
}
int
main(int argc, char **argv)
{
struct stat stbuf;
char *filename;
int rc;
int fd;
int filesize;
char *mmapped;
img3 *img3Header;
tag *tag;
char ident[5];
char type[5];
int i;
// Usage/arguments could be better. This is just a simple quick and dirty
// example. Excuse my brevity..
if (argc < 2)
{ fprintf (stderr,"Usage: %s [-d] _filename_\n", argv[0]); exit(0);}
if (strcmp(argv[1], "-d") == 0) { g_Dump++; }
filename = argv[argc -1];
rc = stat(filename, &stbuf);
if (rc == -1) { perror (filename); exit (1); }
filesize = stbuf.st_size;
fd = open (filename, O_RDONLY);
if (fd < 0) { perror (filename); exit(2);}
mmapped = mmap(NULL,
filesize, // size_t len,
PROT_READ, // int prot,
MAP_SHARED | MAP_FILE, // int flags,
fd, // int fd,
0); // off_t offset);
if (!mmapped) { perror ("mmap"); exit(3);}
img3Header = (img3 *) mmapped;
if (img3Header->magic != IMG3_MAGIC)
{
fprintf(stderr,"%s is not an IMG3 file!\n", filename);
exit(1);
}
ident[4] ='\0';
for (i = 0; i < 4; i++)
{
ident[i] = * (((char *)&(img3Header->ident)) + 3-i);
}
printf ("Ident: %s\n", ident);
tag = (struct tag *) (mmapped + sizeof(img3));
while ( ((char *)tag) - ((char *) mmapped) < filesize ) {
for (i = 0; i < 4; i++)
{
ident[i] = * (((char *)&(tag->magic)) + 3-i);
}
printf ("Tag: %s (%x) Length 0x%x\n", ident, tag->magic, tag->total_length);
switch (tag->magic)
{
case TAG_TYPE:
printf ("\tType: ");
for (i = 0; i < 4; i++)
{
type[i] = * (((char *)&(tag->data)) + 3-i);
}
printf ("%s\n", type);
break;
case TAG_BORD:
printf ("\tBoard: ");
dump (tag->data,tag->data_length);
break;
case TAG_VERS:
printf ("\tVersion: ");
printf ("%s\n", tag->data + 4);
break;
case TAG_SEPO:
printf ("\tSecurity Epoch: ");
dump (tag->data,tag->data_length);
break;
case TAG_CHIP:
printf ("\tChip: ");
dump (tag->data,tag->data_length);
break;
case TAG_DATA:
doData(tag->data, *((int *) type), tag->data_length);
break;
default:
break;
}
tag = (( (char *) tag) + (tag->total_length));
}
return 0;
}
#include <sys/mman.h> // For mmap(2)
#include <sys/stat.h> // For stat(2)
#include <unistd.h> // For everything else
#include <fcntl.h> // O_RDONLY
#include <stdio.h> // printf!
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/loader.h> // struct mach_header
#include "machlib.h"
/**
*
* A simple program to home in on XNU's system call table.
* Coded specifically for iOS kernels. Seeks XNU version string
* and signature of beginning of system call table. Then dumps
* all system calls. Can work on the kernel proper, or the kernel cache.
*
*
* System call names auto-generated from iOS's <sys/syscall.h>
* (/Developer/Platforms/iPhoneOS.platform/DeviceSupport/Latest/Symbols/usr/include/sys)
*
* can also be generated from OS X's <sys/syscall.h>, with minor tweaks (e.g. include
* ledger, pid_shutdown_sockets, etc..)
*
* Note, that just because a syscall is present, doesn't imply it's implemented -
* System calls can either point to nosys, or can be stubs returning an error code,
* as is the case with audit syscalls (350-359), among others.
*
* Tested on iOS 2.0 through 7.1
*
* 03/20/14: Updated to dump sysctls, code cleaned, more messy code added
*
* @TODO:
* - Port to ARMv8/x86_64,
* - Create a companion .dSYM
*
* Coded by Jonathan Levin, [email protected]
*
**/
#
char *syscall_names[] = { "syscall", "exit", "fork", "read", "write", "open", "close", "wait4", "8 old creat", "link", "unlink", "11 old execv", "chdir", "fchdir", "mknod", "chmod", "chown", "17 old break", "getfsstat", "19 old lseek", "getpid", "21 old mount", "22 old umount", "setuid", "getuid", "geteuid", "ptrace", "recvmsg", "sendmsg", "recvfrom", "accept", "getpeername", "getsockname", "access", "chflags", "fchflags", "sync", "kill", "38 old stat", "getppid", "40 old lstat", "dup", "pipe", "getegid", "profil", "45 old ktrace", "sigaction", "getgid", "sigprocmask", "getlogin", "setlogin", "acct", "sigpending", "sigaltstack", "ioctl", "reboot", "revoke", "symlink", "readlink", "execve", "umask", "chroot", "62 old fstat", "63 used internally , reserved", "64 old getpagesize", "msync", "vfork", "67 old vread", "68 old vwrite", "69 old sbrk", "70 old sstk", "71 old mmap", "72 old vadvise", "munmap", "mprotect", "madvise", "76 old vhangup", "77 old vlimit", "mincore", "getgroups", "setgroups", "getpgrp", "setpgid", "setitimer", "84 old wait", "swapon", "getitimer", "87 old gethostname", "88 old sethostname", "getdtablesize", "dup2", "91 old getdopt", "fcntl", "select", "94 old setdopt", "fsync", "setpriority", "socket", "connect", "99 old accept", "getpriority", "101 old send", "102 old recv", "103 old sigreturn", "bind", "setsockopt", "listen", "107 old vtimes", "108 old sigvec", "109 old sigblock", "110 old sigsetmask", "sigsuspend", "112 old sigstack", "113 old recvmsg", "114 old sendmsg", "115 old vtrace", "gettimeofday", "getrusage", "getsockopt", "119 old resuba", "readv", "writev", "settimeofday", "fchown", "fchmod", "125 old recvfrom", "setreuid", "setregid", "rename", "129 old truncate", "130 old ftruncate", "flock", "mkfifo", "sendto", "shutdown", "socketpair", "mkdir", "rmdir", "utimes", "futimes", "adjtime", "141 old getpeername", "gethostuuid", "143 old sethostid", "144 old getrlimit", "145 old setrlimit", "146 old killpg", "setsid", "148 old setquota", "149 old qquota", "150 old getsockname", "getpgid", "setprivexec", "pread", "pwrite", "nfssvc", "156 old getdirentries", "statfs", "fstatfs", "unmount", "160 old async_daemon", "getfh", "162 old getdomainname", "163 old setdomainname", "164", "quotactl", "166 old exportfs", "mount", "168 old ustat", "csops", "csops_audittoken", "171 old wait3", "172 old rpause", "waitid", "174 old getdents", "175 old gc_control", "add_profil", "177", "178", "179", "kdebug_trace", "setgid", "setegid", "seteuid", "sigreturn", "chud", "186", "fdatasync", "stat", "fstat", "lstat", "pathconf", "fpathconf", "193", "getrlimit", "setrlimit", "getdirentries", "mmap", "198 __syscall", "lseek", "truncate", "ftruncate", "__sysctl", "mlock", "munlock", "undelete", "ATsocket", "ATgetmsg", "ATputmsg", "ATPsndreq", "ATPsndrsp", "ATPgetreq", "ATPgetrsp", "213 Reserved for AppleTalk", "214", "215", "mkcomplex", "statv", "lstatv", "fstatv", "getattrlist", "setattrlist", "getdirentriesattr", "exchangedata", "224 old checkuseraccess / fsgetpath ( which moved to 427 )", "searchfs", "delete", "copyfile", "fgetattrlist", "fsetattrlist", "poll", "watchevent", "waitevent", "modwatch", "getxattr", "fgetxattr", "setxattr", "fsetxattr", "removexattr", "fremovexattr", "listxattr", "flistxattr", "fsctl", "initgroups", "posix_spawn", "ffsctl", "246", "nfsclnt", "fhopen", "249", "minherit", "semsys", "msgsys", "shmsys", "semctl", "semget", "semop", "257", "msgctl", "msgget", "msgsnd", "msgrcv", "shmat", "shmctl", "shmdt", "shmget", "shm_open", "shm_unlink", "sem_open", "sem_close", "sem_unlink", "sem_wait", "sem_trywait", "sem_post", "sem_getvalue", "sem_init", "sem_destroy", "open_extended", "umask_extended", "stat_extended", "lstat_extended", "fstat_extended", "chmod_extended", "fchmod_extended", "access_extended", "settid", "gettid", "setsgroups", "getsgroups", "setwgroups", "getwgroups", "mkfifo_extended", "mkdir_extended", "identitysvc", "shared_region_check_np", "shared_region_map_np", "vm_pressure_monitor", "psynch_rw_longrdlock", "psynch_rw_yieldwrlock", "psynch_rw_downgrade", "psynch_rw_upgrade", "psynch_mutexwait", "psynch_mutexdrop", "psynch_cvbroad", "psynch_cvsignal", "psynch_cvwait", "psynch_rw_rdlock", "psynch_rw_wrlock", "psynch_rw_unlock", "psynch_rw_unlock2", "getsid", "settid_with_pid", "psynch_cvclrprepost", "aio_fsync", "aio_return", "aio_suspend", "aio_cancel", "aio_error", "aio_read", "aio_write", "lio_listio", "321 old __pthread_cond_wait", "iopolicysys", "process_policy", "mlockall", "munlockall", "326", "issetugid", "__pthread_kill", "__pthread_sigmask", "__sigwait", "__disable_threadsignal", "__pthread_markcancel", "__pthread_canceled", "__semwait_signal", "335 old utrace", "proc_info", "sendfile", "stat64", "fstat64", "lstat64", "stat64_extended", "lstat64_extended", "fstat64_extended", "getdirentries64", "statfs64", "fstatfs64", "getfsstat64", "__pthread_chdir", "__pthread_fchdir", "audit", "auditon", "352", "getauid", "setauid", "getaudit", "setaudit", "getaudit_addr", "setaudit_addr", "auditctl", "bsdthread_create", "bsdthread_terminate", "kqueue", "kevent", "lchown", "stack_snapshot", "bsdthread_register", "workq_open", "workq_kernreturn", "kevent64", "__old_semwait_signal", "__old_semwait_signal_nocancel", "thread_selfid", "ledger", "374", "375", "376", "377", "378", "379", "__mac_execve", "__mac_syscall", "__mac_get_file", "__mac_set_file", "__mac_get_link", "__mac_set_link", "__mac_get_proc", "__mac_set_proc", "__mac_get_fd", "__mac_set_fd", "__mac_get_pid", "__mac_get_lcid", "__mac_get_lctx", "__mac_set_lctx", "setlcid", "getlcid", "read_nocancel", "write_nocancel", "open_nocancel", "close_nocancel", "wait4_nocancel", "recvmsg_nocancel", "sendmsg_nocancel", "recvfrom_nocancel", "accept_nocancel", "msync_nocancel", "fcntl_nocancel", "select_nocancel", "fsync_nocancel", "connect_nocancel", "sigsuspend_nocancel", "readv_nocancel", "writev_nocancel", "sendto_nocancel", "pread_nocancel", "pwrite_nocancel", "waitid_nocancel", "poll_nocancel", "msgsnd_nocancel", "msgrcv_nocancel", "sem_wait_nocancel", "aio_suspend_nocancel", "__sigwait_nocancel", "__semwait_signal_nocancel", "__mac_mount", "__mac_get_mount", "__mac_getfsstat", "fsgetpath", "audit_session_self", "audit_session_join", "fileport_makeport", "fileport_makefd", "audit_session_port","pid_suspend", "pid_resume", "pid_hibernate", "pid_shutdown_sockets", "437 old shared_region_slide_np", "shared_region_map_and_slide_np" ,
"kas_info", "memorystatus_control", "guarded_open_np","guarded_close_np",
"guarded_kqueue_np",
"change_fdguard_np",
"old __proc_suppress",
"proc_rlimit_control",
"proc_connectx",
"proc_disconnectx",
"proc_peeloff",
"proc_socket_delegate",
"proc_telemetry",
"proc_uuid_policy", // 452
"memorystatus_get_level", // 453
NULL
};
// That MOV PC,R9 always gives it away , now..
const char *ARMExcVector = "\x09\xf0\xa0\xe1\xfe\xff\xff\xea";
const char * mach_syscall_name_table[128] = {
/* 0 */ "kern_invalid",
/* 1 */ "kern_invalid",
/* 2 */ "kern_invalid",
/* 3 */ "kern_invalid",
/* 4 */ "kern_invalid",
/* 5 */ "kern_invalid",
/* 6 */ "kern_invalid",
/* 7 */ "kern_invalid",
/* 8 */ "kern_invalid",
/* 9 */ "kern_invalid",
/* 10 */ "_kernelrpc_mach_vm_allocate_trap", // OS X : "kern_invalid",
/* 11 */ "_kernelrpc_vm_allocate_trap", // OS X : "kern_invalid",
/* 12 */ "_kernelrpc_mach_vm_deallocate_trap", // OS X: "kern_invalid",
/* 13 */ "_kernelrpc_vm_deallocate_trap" , // "kern_invalid",
/* 14 */ "_kernelrpc_mach_vm_protect_trap", //"kern_invalid",
/* 15 */ "_kernelrpc_vm_protect_trap", // kern_invalid",
/* 16 */ "_kernelrpc_mach_port_allocate_trap", //"kern_invalid",
/* 17 */ "_kernelrpc_mach_port_destroy_trap" ,//"kern_invalid",
/* 18 */ "_kernelrpc_mach_port_deallocate_trap", // "kern_invalid",
/* 19 */ "_kernelrpc_mach_port_mod_refs_trap", //"kern_invalid",
/* 20 */ "_kernelrpc_mach_port_move_member_trap", //"kern_invalid",
/* 21 */ "_kernelrpc_mach_port_insert_right_trap", //"kern_invalid",
/* 22 */ "_kernelrpc_mach_port_insert_member_trap", // "kern_invalid",
/* 23 */ "_kernelrpc_mach_port_extract_member_trap", // "kern_invalid",
/* 24 */ "kern_invalid",
/* 25 */ "kern_invalid",
/* 26 */ "mach_reply_port",
/* 27 */ "thread_self_trap",
/* 28 */ "task_self_trap",
/* 29 */ "host_self_trap",
/* 30 */ "kern_invalid",
/* 31 */ "mach_msg_trap",
/* 32 */ "mach_msg_overwrite_trap",
/* 33 */ "semaphore_signal_trap",
/* 34 */ "semaphore_signal_all_trap",
/* 35 */ "semaphore_signal_thread_trap",
/* 36 */ "semaphore_wait_trap",
/* 37 */ "semaphore_wait_signal_trap",
/* 38 */ "semaphore_timedwait_trap",
/* 39 */ "semaphore_timedwait_signal_trap",
/* 40 */ "kern_invalid",
/* 41 */ "kern_invalid",
/* 42 */ "kern_invalid",
/* 43 */ "map_fd",
/* 44 */ "task_name_for_pid",
/* 45 */ "task_for_pid",
/* 46 */ "pid_for_task",
/* 47 */ "kern_invalid",
/* 48 */ "macx_swapon",
/* 49 */ "macx_swapoff",
/* 50 */ "kern_invalid",
/* 51 */ "macx_triggers",
/* 52 */ "macx_backing_store_suspend",
/* 53 */ "macx_backing_store_recovery",
/* 54 */ "kern_invalid",
/* 55 */ "kern_invalid",
/* 56 */ "kern_invalid",
/* 57 */ "kern_invalid",
/* 58 */ "pfz_exit",
/* 59 */ "swtch_pri",
/* 60 */ "swtch",
/* 61 */ "thread_switch",
/* 62 */ "clock_sleep_trap",
/* 63 */ "kern_invalid",
/* traps 64 - 95 reserved (debo) */
/* 64 */ "kern_invalid",
/* 65 */ "kern_invalid",
/* 66 */ "kern_invalid",
/* 67 */ "kern_invalid",
/* 68 */ "kern_invalid",
/* 69 */ "kern_invalid",
/* 70 */ "kern_invalid",
/* 71 */ "kern_invalid",
/* 72 */ "kern_invalid",
/* 73 */ "kern_invalid",
/* 74 */ "kern_invalid",
/* 75 */ "kern_invalid",
/* 76 */ "kern_invalid",
/* 77 */ "kern_invalid",
/* 78 */ "kern_invalid",
/* 79 */ "kern_invalid",
/* 80 */ "kern_invalid",
/* 81 */ "kern_invalid",
/* 82 */ "kern_invalid",
/* 83 */ "kern_invalid",
/* 84 */ "kern_invalid",
/* 85 */ "kern_invalid",
/* 86 */ "kern_invalid",
/* 87 */ "kern_invalid",
/* 88 */ "kern_invalid",
/* 89 */ "mach_timebase_info_trap",
/* 90 */ "mach_wait_until_trap",
/* 91 */ "mk_timer_create_trap",
/* 92 */ "mk_timer_destroy_trap",
/* 93 */ "mk_timer_arm_trap",
/* 94 */ "mk_timer_cancel_trap",
/* 95 */ "kern_invalid",
/* traps 64 - 95 reserved (debo) */
/* 96 */ "kern_invalid",
/* 97 */ "kern_invalid",
/* 98 */ "kern_invalid",
/* 99 */ "kern_invalid",
/* traps 100-107 reserved for iokit (esb) */
/* 100 */ "kern_invalid",
/* 100 */ //"iokit_user_client_trap",
/* 101 */ "kern_invalid",
/* 102 */ "kern_invalid",
/* 103 */ "kern_invalid",
/* 104 */ "kern_invalid",
/* 105 */ "kern_invalid",
/* 106 */ "kern_invalid",
/* 107 */ "kern_invalid",
/* traps 108-127 unused */
/* 108 */ "kern_invalid",
/* 109 */ "kern_invalid",
/* 110 */ "kern_invalid",
/* 111 */ "kern_invalid",
/* 112 */ "kern_invalid",
/* 113 */ "kern_invalid",
/* 114 */ "kern_invalid",
/* 115 */ "kern_invalid",
/* 116 */ "kern_invalid",
/* 117 */ "kern_invalid",
/* 118 */ "kern_invalid",
/* 119 */ "kern_invalid",
/* 120 */ "kern_invalid",
/* 121 */ "kern_invalid",
/* 122 */ "kern_invalid",
/* 123 */ "kern_invalid",
/* 124 */ "kern_invalid",
/* 125 */ "kern_invalid",
/* 126 */ "kern_invalid",
/* 127 */ "kern_invalid",
};
#define XNUSIG "SourceCache/xnu/xnu-"
#define SYS_MAXSYSCALL 443
#define SYS_MAXSYSCALL_7 454
#define SIG1 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00"
#define SIG1_SUF "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x04\x00\x00\x00"
#define SIG2 "\x00\x00\x00\x00" \
"\x00\x00\x00\x00" \
"\x01\x00\x00\x00" \
"\x1C\x00\x00\x00" \
"\x00\x00\x00\x00"
#define SIG1_2423_ONWARDS "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00"
#define SIG2_2423_ONWARDS "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x04\x00"
void dumpMachTraps(char *mach)
{
if (mach) printf ("Kern invalid should be %p. Ignoring those\n", *((int *) &mach[4]));
int i;
for (i = 0; i < 128; i++)
{
int thumb = 0;
int addr = * ((int *) (mach + 4 + 8*i));
if (addr == *((int *) (mach + 4))) continue;
if ((addr % 4) == 1) { addr--; thumb++; }
if ((addr % 4) == -3) { addr--; thumb++; }
if (addr % 4) { thumb = "?"; }
printf ("%3d %-40s %x %s\n", i, mach_syscall_name_table[i], addr, (thumb? "T": "-"));
} // end for < 128 ..
} // dumpMachTraps
int g_Verbose = 0;
char *MachOLookupSymbolAtAddress(uint64_t, unsigned char *File);
int doKext (char *mmapped)
{
return 1;
} // doKext
void
printDictionaryAsXML(CFMutableDictionaryRef dict)
{
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)dict);
if (xml) {
write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml));
printf("done\n");
CFRelease(xml);
}
printf("..\n");
}
void doKexts(char *mmapped)
{
int kexts = 0;
// To do the kexts, we load the dictionary of PRELINK_INFO
char *kextPrelinkInfo = (char *) malloc(1000000);
CFDictionaryRef dict;
char *kextNamePtr;
char *kextLoadAddr;
char kextName[256];
char loadAddr[16];
char *temp = kextPrelinkInfo;
char *loadAddrPtr;
char *prelinkAddr;
extern char *g_SegName;
g_SegName = "__PRELINK_INFO";
void *seg = MachOGetSection("__PRELINK_INFO");
kextPrelinkInfo = (char *) (mmapped + MachOGetSegmentOffset(seg));
temp = kextPrelinkInfo;
kextNamePtr = strstr(temp,"CFBundleName</key>");
// This is EXTREMELY quick and dirty, but I can't find a way to load a CFDictionary
// directly from XML data, so it will do for now..
while (kextNamePtr) {
temp = strstr(kextNamePtr, "</string>");
prelinkAddr = strstr(kextNamePtr, "_PrelinkExecutableLoadAddr");
loadAddrPtr = strstr(prelinkAddr, "0x");
// overflow, etc..
memset(kextName, '\0', 256);
strncpy (kextName, kextNamePtr + 26, temp - kextNamePtr - 26);
// temp = strstr(loadAddrPtr, "</integer>");
strncpy (loadAddr, loadAddrPtr, 10);
loadAddr[9]='\0';
printf("%s: %s ", loadAddr, kextName);
temp += 10;
kextNamePtr = strstr(temp, "CFBundleIdentifier");
if (kextNamePtr)
{
temp = strstr(kextNamePtr,"</string>");
memset(kextName,'\0',256);
strncpy(kextName, kextNamePtr + 32, temp - kextNamePtr - 32);
printf ("(%s)\n", kextName);
}
kextNamePtr = strstr(temp,"CFBundleName</key>");
kexts++;
}
printf("Got %d kexts. done\n", kexts);
}
struct sysctl_oid {
uint32_t ptr_oid_parent;
uint32_t ptr_oid_link;
int oid_number;
int oid_kind;
uint32_t oid_arg1;
int oid_arg2;
uint32_t ptr_oid_name;
uint32_t ptr_oid_handler;
uint32_t ptr_oid_fmt;
uint32_t ptr_oid_descr; /* offsetof() field / long description */
int oid_version;
int oid_refcnt;
};
char *sysctlName (char *mmapped, uint32_t sysctlPtr)
{
char *name = malloc(1024);
name[0] = '\0';
uint32_t sysCtlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr);
if (sysCtlOffsetInFile == -1) { strcat (name, "?"); return (name); }
struct sysctl_oid *sysctl = (mmapped + sysCtlOffsetInFile);
char *parent = MachOLookupSymbolAtAddress(sysctl->ptr_oid_parent, mmapped);
if (parent)
{
if (strncmp(parent, "_sysctl__",9) ==0)
{
strcpy(name,parent+9);
int i =0;
while (i < strlen(name))
{
if (name[i] == '_') name[i] = '.';
i++;
if (strncmp(name +i, "children",7) == 0) name[i-1] = '\0'; //will fall out
}
}
else
strcpy(name, parent);
strcat(name, ".");
}
else
{
char parentAddr[16];
sprintf (parentAddr,"0x%x", sysctl->ptr_oid_parent);
strcpy(name, parentAddr);
strcat(name,".");
}
uint32_t sysctlNameOffsetInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_name);
if (sysctlNameOffsetInFile == -1) {strcat (name,"?"); return (name);}
strcat (name, mmapped + sysctlNameOffsetInFile);
return (name);
} //sysctlName
void doSysctls(char *mmapped)
{
// assume section 32 for now..
struct section *sec = MachOGetSection ("__DATA.__sysctl_set");
if (sec) {
int numsysctls = sec->size /sizeof(uint32_t);
int s = 0;
printf ("Dumping sysctl_set from 0x%x (offset in file: 0x%x), %x sysctls follow:\n", sec->addr,sec->offset, numsysctls);
for (s = 0 ; s < numsysctls; s++)
{
uint32_t sysctlPtr = *((uint32_t *)(mmapped + sec->offset+ s * sizeof(uint32_t)));
uint32_t sysctlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr);
printf ("0x%x: ", sysctlPtr , sysctlOffsetInFile);
// sanity check, anyone?
if (sysctlOffsetInFile > sec->offset + sec->size) { printf("(outside __sysctl_set)\n"); continue;};
struct sysctl_oid *sysctl = (mmapped + sysctlOffsetInFile);
uint32_t sysctlDescInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_descr);
uint32_t sysctlFormatInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_fmt);
char *sysctlFormat = "?";
if (sysctlFormatInFile != -1) { sysctlFormat = mmapped + sysctlFormatInFile;}
printf ("%s\tDescription: %s\n\t\tHandler: 0x%x\n\t\tFormat: %s\n\t\tParent: %x\n\t\tArg1: %x\n\t\tArg2: %x\n",
sysctlName(mmapped,sysctlPtr),
mmapped + sysctlDescInFile,
sysctl->ptr_oid_handler,
sysctlFormat,
sysctl->ptr_oid_parent, sysctl->oid_arg1, sysctl->oid_arg2);
}
}
} // doSysctls
int main (int argc, char **argv)
{
int ios7 = 0;
int fd;
char *mmapped;
int rc;
struct stat stbuf;
int filesize;
char *filename = argv[1];
struct mach_header *mh;
int i,j ;
int magic;
char *sysent = NULL;
char *mach = NULL;
char *xnuSig = NULL;
int showUNIX = 0, showMach = 0;
int suppressEnosys = 1;
int showVersion = 0;
int showKexts = 0;
int showSysctls = 0;
if (!filename) { fprintf (stderr,"Usage: joker [-ask] _filename_\n", argv[0]);
fprintf (stderr," _filename_ should be a decrypted iOS kernelcache. Tested on 3.x-4.x-5.x-7.0\n");
fprintf (stderr," -m: dump UNIX Syscalls and Mach Traps\n");
fprintf (stderr," -a: dump everything\n");
fprintf (stderr," -k: dump kexts\n");
fprintf (stderr," -s: dump sysctls\n");
fprintf (stderr, "Stable version (no symbolification/etc here yet)\n"); exit(0);}
if (filename[0] == '-') { showVersion = (filename[1] == 'v' ? 1 : 0 ) ; filename = argv[2]; };
if (strcmp (argv[1], "-k") ==0 ) { showKexts = 1; filename = argv[2]; showUNIX =0; showMach = 0;};
if (strcmp (argv[1], "-s") ==0 ) { showSysctls = 1; filename = argv[2]; showUNIX =0; showMach = 0;};
if (strcmp (argv[1], "-a") ==0 ) { showSysctls = 1; showKexts=1;filename = argv[2]; showUNIX =showMach = 1;};
if (strcmp (argv[1], "-m") ==0 ) { showMach = showUNIX = 1; filename = argv[2];};
rc = stat(filename, &stbuf);
if (rc == -1) { perror (filename); exit (1); }
filesize = stbuf.st_size;
fd = open (filename, O_RDONLY);
if (fd < 0) { perror ("open"); exit(2);}
mmapped = mmap(NULL,
filesize, // size_t len,
PROT_READ, // int prot,
MAP_SHARED | MAP_FILE, // int flags,
fd, // int fd,
0); // off_t offset);
if (!mmapped) { perror ("mmap"); exit(3);}
processFile(mmapped,filesize, CPU_TYPE_ARM, 0, 0);
struct source_version_command *svc = (struct source_version_command *) findLoadCommand (mmapped, LC_SOURCE_VERSION);
if (svc)
fprintf (stdout, "%-25s%ld.%d.%d.%d.%d\n",
"Source Version:",
(long) ((svc->version) >> 40),
(int) (svc->version >> 30) & 0x000003FF ,
(int) (svc->version >> 20) & 0x000003FF,
(int) (svc->version >> 10) & 0x000003FF,
(int) (svc->version) & 0x000003FF);
if (svc && (svc->version >> 40) >= 2423)
{
fprintf(stdout, "This is iOS 7.x, or later\n");
ios7 = 1;
}
mh = (struct mach_header *) (mmapped);
switch (mh->magic)
{
case 0xFEEDFACE:
/* Good, this is a Mach-O */
if (mh->cputype == 12) /* ARM */
{
// This is an ARM binary. Good.
}
break;
case 0xbebafeca:
fprintf (stderr, "This is an Intel FAT binary, but I can't handle these yet\n");
exit(5);
default:
fprintf(stderr, "I have no idea how to handle a file with a magic of %p\n", magic); exit(6);
}
//printf ("Entry point is 0x%llx..", getEntryPoint());
for (i = 0;
i < filesize-50;
i++)
{
if (!xnuSig && memcmp(&mmapped[i], XNUSIG, strlen(XNUSIG)) == 0)
{
/* Could actually get the version from LC_SOURCE_VERSION... */
char buf[80];
xnuSig = mmapped + i + strlen(XNUSIG);
memset(buf, '\0', 80);
strncpy (buf, xnuSig, 40);
// The signature we get is from a panic, with the full path to the
// xnu sources. Remove the "/" following the XNU version. Because the
// memory is mmap(2)ed read only, we have to copy this first.
char *temp = strstr(buf, "/");
if (temp) {
*temp = '\0';
}
xnuSig = buf;
if (showVersion) {
printf ("This is XNU %s\n", xnuSig);
exit(0);
}
}
if (memcmp(&mmapped[i], ARMExcVector, 8) == 0)
{
if (showUNIX) printf("ARM Exception Vector is at file offset @0x%x (Addr: 0x%x)\n", i-28, findAddressOfOffset(i-28));
}
if (memcmp(&mmapped[i], SIG1, 20) == 0)
{
if (memcmp(&mmapped[i+24], SIG1_SUF, 16) == 0)
{
if (showUNIX) printf ("Sysent offset in file (for patching purposes): %p\n",i-8,0x80041000+(i -8));
sysent = mmapped + i - 24 ;
// if (xnuSig) break;
}
}
if ( (memcmp(&mmapped[i], SIG1_2423_ONWARDS, 16) == 0) &&
(memcmp(&mmapped[i+20], SIG2_2423_ONWARDS, 16) ==0) &&
(memcmp(&mmapped[i+40], SIG1_2423_ONWARDS, 16) ==0))
{
if (showUNIX)
printf ("Sysent offset in file (for patching purposes): %p\n",i-8,0x80041000+(i -8));
sysent = mmapped + i - 24 ;
// if (xnuSig) break;
}
if (showMach)
{
if (! mach &&
(memcmp(&mmapped[i], &mmapped[i+40], 40 ) == 0) &&
(memcmp(&mmapped[i], &mmapped[i+32], 32 ) == 0) &&
(memcmp(&mmapped[i], &mmapped[i+24], 24 ) == 0) &&
(memcmp(&mmapped[i], &mmapped[i+16], 16) == 0) &&
(memcmp(&mmapped[i], &mmapped[i+24], 24) == 0) &&
(memcmp(&mmapped[i], &mmapped[i+8], 8 ) == 0) &&
( (!*((int *) &mmapped[i])) && *((int *) &mmapped[i+4]))
)
{
printf ("mach_trap_table offset in file/memory (for patching purposes): 0x%x/%p\n", i,findAddressOfOffset(i));
mach = &mmapped[i];
dumpMachTraps (mach);
}
} // end showMach
} // end for i..
if (!xnuSig) { fprintf (stderr, "This doesn't seem to be a kernel!\n"); exit (7);}
if (showUNIX && sysent)
{
if (memcmp(&mmapped[i], "syscall\0exit", 12) == 0)
{
// syscall_names = &mmapped[i];
printf ("Syscall names are @%x\n", i);
}
if (suppressEnosys)
{
int enosys = * ((int *) (sysent + 20 + 24*4));
printf ("Suppressing enosys (%p)\n", enosys);
}
for (i = 0; i< (ios7 ? SYS_MAXSYSCALL_7 : SYS_MAXSYSCALL); i++)
{
int suppress = 0;
int thumb = 0;
int jump = (ios7? 20 : 24);
int addr = * ((int *) (sysent + 20 + jump*i));
if (addr == *((int *)(sysent + 20 + jump * 8)))
suppress =1;
if ((addr % 4) == 1) { addr--; thumb++; }
if ((addr % 4) == -3) { addr--; thumb++; }
if (!suppress)
printf ("%d. %-20s %x %s\n", i,syscall_names[i], addr, (thumb? "T": "-"));
// skip to next post null byte - unfortunately wont work due to optimizations
// putting some of the system call name strings elsewhere (in their first appearance
// in the binary)
// for (; *syscall_names; syscall_names++);
// syscall_names++;
}
} // showUNIX
// Do KEXTs
void *seg = MachOGetSection("__DATA.__const");
if (!seg)
{
fprintf(stderr,"Unable to find const section. This shouldn't be happening.. continuting anyway, but can't look for sysent/mach_trap_table\n");
}
else
{
}
_kexts:
if (showKexts) doKexts(mmapped);
_sysctls:
if (showSysctls) doSysctls(mmapped);
}
#include <stdlib.h>
#include <unistd.h>
#define NULL ((void *) 0ULL)
#include <stdio.h>
// simple program that links two functions,
// calls one of them twice. <==
// - Understand the concepts behind dynamic linking
// - Understand "lazy binding"
// - Understand the specific benefit in lazy binding
int main (int argc, char **argv) {
printf("HELLO\n");
sleep(1);
printf("Goodbyte\n");
}
#include <mach/mach.h>
#include <mach/task.h>
#include <sys/signal.h>
#include <libproc.h>
#include <dlfcn.h>
#include <stdio.h>
#include <pthread.h>
#include <CoreFoundation/CoreFoundation.h>
extern void NSLog(CFStringRef, ...);
#include <unistd.h>
#include <stdlib.h> // exit
#include <mach-o/loader.h> // for Mach-O handling
#include <mach-o/fat.h>
uint64_t amfidTEXTaddress = 0;
#ifdef _11
uint32_t MVSACI_offset = 0x100004150 - 0x100000000;
#else
uint32_t MVSACI_offset = 0x100004140 - 0x100000000;
#endif
//
// Missing headers
//
kern_return_t mach_vm_write
(
vm_map_t target_task,
mach_vm_address_t address,
vm_offset_t data,
mach_msg_type_number_t dataCnt
);
kern_return_t mach_vm_read_overwrite
(
vm_map_t target_task,
mach_vm_address_t address,
mach_vm_size_t size,
vm_offset_t data,
mach_msg_type_number_t *dataCnt
);
FILE *OUT = NULL;
void debug (char *Msg, ...)
{
static char buffer[2048];
va_list args;
va_start (args, Msg);
vsprintf (buffer, Msg, args);
va_end (args);
NSLog(CFSTR("DEBUG: %s\n"), buffer);
fprintf(OUT, "DEBUG: %s\n", buffer);
}
void status (char *Msg, ...)
{
static char buffer[2048];
va_list args;
va_start (args, Msg);
vsprintf (buffer, Msg, args);
va_end (args);
NSLog(CFSTR("%s\n"), buffer);
fprintf(OUT, "%s\n", buffer);
}
void error (char *Msg, ...)
{
static char buffer[2048];
va_list args;
va_start (args, Msg);
vsprintf (buffer, Msg, args);
va_end (args);
NSLog(CFSTR("ERROR: %s\n"), buffer);
fprintf(OUT, "Error: %s\n", buffer);
//exit(strlen(Msg)); // :-)
}
pid_t findPidOfProcess (char *ProcName) {
char buffer[256];
int rc = 0 ;
int pid = 0;
// Brute force-ish. Better would be to get list of pids first..
for (pid = 1; pid < 65536; pid++)
{
// Interesting behavior: proc_name only works for processes with same uid.
rc = proc_name(pid, // int pid,
buffer, // void * buffer,
256); // uint32_t buffersize)
//if (rc){ printf("PID %d: %s\n", pid, buffer);}
if (rc && strcmp(buffer,ProcName) == 0)
{
return pid;
}
}
return 0;
}
// Code signing support
#define ALGORITHM_SHA256 256
#define ALGORITHM_SHA1 1
int algorithm = ALGORITHM_SHA256;
const struct ccdigest_info *ccsha256_di(void);
void cchmac(const struct ccdigest_info *di, unsigned long key_len,
const void *key, unsigned long data_len, const void *data,
unsigned char *mac);
void ccdigest(const struct ccdigest_info *di, unsigned long len,
const void *data, void *digest);
static inline unsigned char *mysha1 (void *Data, int Len)
{
static unsigned char sha256[32] = {0};
ccdigest(ccsha256_di(),
0x1000, // data_len
Data, // const void *data
sha256); // unsigned char *mac);
return (sha256);
}
int MISdoNotValidateSignatureButCopyInfo(char *Path, void *Options, char *outDict)
{
// Not yet
return 0;
} //doNotValidateCodeSignatureButCopyInfo
const struct ccdigest_info *ccsha256_di(void);
const struct ccdigest_info *ccsha1_di(void);
void cchmac(const struct ccdigest_info *di, unsigned long key_len,
const void *key, unsigned long data_len, const void *data,
unsigned char *mac);
void ccdigest(const struct ccdigest_info *di, unsigned long len,
const void *data, void *digest);
static inline unsigned char doSHA256 (void *Data, int Len, unsigned char *Buf)
{
static unsigned char sha256[32] = {0};
ccdigest(ccsha256_di(),
Len, // data_len
Data, // const void *data
Buf); // unsigned char *mac);
return (Buf);
}
static inline unsigned char doSHA1 (void *Data, int Len, unsigned char *Buf)
{
static unsigned char sha1[32] = {0};
ccdigest(ccsha1_di(),
Len, // data_len
Data, // const void *data
Buf); // unsigned char *mac);
return (Buf);
}
#define FAULTING_ADDRESS 0x454d41524542494c // De profondu lacu
unsigned char *cdHashOfFile(char *Path, int Algorithm )
{
struct stat stBuf ;
if (access (Path, F_OK)) {
fprintf(stderr,"File %s apparently not found!\n", Path); return NULL;
}
int fd = open (Path, O_RDONLY);
if (fd == -1) { fprintf(stderr,"Unable to open File %s - %s\n", Path ,strerror(errno)); return NULL; }
int rc = fstat (fd,&stBuf);
// NSLog(CFSTR("Processing file %s\n"), Path);
char *fileContents = malloc(stBuf.st_size);
read (fd, fileContents, stBuf.st_size);
close(fd);
struct mach_header_64 *mh = (struct mach_header_64 *) fileContents;
if (mh->magic != MH_MAGIC_64) {
// Give FAT a change...
if (mh->magic == FAT_CIGAM) {
struct fat_header *fh = (struct fat_header *) mh;
fprintf(OUT, "# fat archs: %d\n", ntohl(fh->nfat_arch));
struct fat_arch *fa = (struct fat_arch *) (fh +1);
int arch = 0;
for (arch = 0; arch < ntohl(fh->nfat_arch); arch++)
{
fprintf(OUT, "ARCH: 0x%x\n", fa->cputype);
if (fa->cputype == 0xc000001) { // CPU_TYPE_ARM64)
fprintf(OUT, "Adjusting header to 0x%x\n", ntohl(fa->offset));
mh = (struct mach_header_64 *) (fileContents + ntohl(fa->offset));
}
fa++; // maybe next arch?:
} // end for
#if 0
struct fat_header {
uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
uint32_t nfat_arch; /* number of structs that follow */
};
struct fat_arch {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
uint32_t offset; /* file offset to this object file */
uint32_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
};
#endif
printf ("MH MAGIC IS NOW %x\n", mh->magic);
}
else { error ("Found magic 0x%x at off file - this is not an MH_MAGIC_64 nor a FAT_MAGIC..\n", mh->magic); return(NULL);}
}
fprintf (OUT,"Got Header with %d Load commands\n", mh->ncmds);
int lcNum = 0;
struct load_command *lc = (struct load_command *) ((char *)mh + sizeof(struct mach_header_64));
while (lcNum < mh->ncmds -1)
{
lc = (struct load_command *)((char *)lc + lc->cmdsize);
lcNum++;
}
// printf("Load command %d - 0x%x (0x%x)\n", lcNum, lc->cmd, LC_CODE_SIGNATURE);
if (lc->cmd != LC_CODE_SIGNATURE) {
debug( "Last load command is not an LC_CODE_SIGNATURE...\n");
free(fileContents);
return NULL;;
}
// Want to get the code signature blob:
struct linkedit_data_command *ldc = (struct linkedit_data_command *) lc;
int csBlobOffset = ldc->dataoff;
int csBlobSize = ldc->datasize;
struct blobDesc {
uint32_t blobType;
uint32_t blobOffset;
};
struct superblob {
uint32_t magic;
uint32_t size;
uint32_t numBlobs;
struct blobDesc blobDesc[0];
} *b = (struct superblob *) ((char *)mh + ldc->dataoff);
if (memmem(b, ntohl(b->size), "Apple Worldwide Developer Relations",
strlen("Apple Worldwide Developer Relations"))) {
debug("Request for a dev signed party - allowing this\n");
}
else
if (memmem(b,ntohl(b->size), "Apple Certi", 10)) {
free(fileContents);
debug("Request for an App store binary - not touching this\n");
return NULL;
}
struct __CodeDirectory {
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
uint32_t length; /* total length of CodeDirectory blob */
uint32_t version; /* compatibility version */
uint32_t flags; /* setup and mode flags */
uint32_t hashOffset; /* offset of hash slot element at index zero */
uint32_t identOffset; /* offset of identifier string */
uint32_t nSpecialSlots; /* number of special hash slots */
uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
uint32_t codeLimit; /* limit to main image signature range */
uint8_t hashSize; /* size of each hash in bytes */
uint8_t hashType; /* type of hash (cdHashType* constants) */
uint8_t platform; /* unused (must be zero) */
uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
uint32_t spare2; /* unused (must be zero) */
} *cdb;
int kSecCodeMagicCodeDirectory = 0xfade0c02; /* CodeDirectory */
cdb = (struct __CodeDirectory *) ((char *) b + ntohl(b->blobDesc[0].blobOffset));
int numBlob = 0;
status("GOT BLOB, MAGIC: 0x%x, offset: %x, type: %x\n",
ntohl(cdb->magic), ntohl(b->blobDesc[0].blobOffset),
ntohl(b->blobDesc[0].blobType));
int matchingBlob = 0;
int match = 0;
while (numBlob < ntohl(b->numBlobs))
{
if (cdb->magic != htonl (kSecCodeMagicCodeDirectory))
{
fprintf(OUT, "Blob Magic: 0x%x - not a code directory (!= 0x%x)\n",
ntohl(cdb->magic), htonl (kSecCodeMagicCodeDirectory));
}
else // is a code directory.
if
(cdb->hashSize != (Algorithm == ALGORITHM_SHA256 ? 32 : 20))
{
fprintf(OUT,"Blob %d hash size: %d (need %d)\n",
cdb->hashSize,
(Algorithm == ALGORITHM_SHA256 ? 32 : 20));
}
else
{
// MATCH
match++;
break;
}
numBlob++;
cdb = (struct __CodeDirectory *) ((char *) b + ntohl(b->blobDesc[numBlob].blobOffset));
/*
status("GOT BLOB, MAGIC: 0x%x, offset: %x, type: %x\n",
ntohl(cdb->magic), ntohl(b->blobDesc[numBlob].blobOffset),
ntohl(b->blobDesc[numBlob].blobType));
*/
} // while
if (!match) { fprintf(stderr,"Can't find a CD Blob match\n"); return (NULL); }
printf("CD Blob magic: 0x%x (CodeDir: 0x%lx)\n", ntohl(cdb->magic),kSecCodeMagicCodeDirectory);
uint32_t cdSize = ntohl(cdb->length);
static unsigned char cdHash[32];
switch (Algorithm)
{
case ALGORITHM_SHA256:
doSHA256(cdb, cdSize, cdHash);
break;
case ALGORITHM_SHA1:
status("Doing SHA1\n");
doSHA1(cdb, cdSize, cdHash);
break;
}
free(fileContents);
return (char *)cdHash;
}
// End code signing support
uint64_t MVSACI_addr = 0;
mach_port_t g_AmfidPort = MACH_PORT_NULL;
int exceptionHandler (mach_port_t ExceptionPort)
{
#define BUFSIZE 0x1000
mach_msg_header_t* msg = (mach_msg_header_t *) alloca(BUFSIZE);;
for(;;){
kern_return_t kr;
kr = mach_msg(msg,
MACH_RCV_MSG | MACH_MSG_TIMEOUT_NONE, // no timeout
0,
BUFSIZE,
ExceptionPort,
0,
0);
// We get this from mach_exc.defs, with an application of mig
// Note that packing the structure is essential since it is
// not aligned on any boundaries..
#pragma pack(1)
struct mach_exc_msg {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[614];
} ;
#pragma pack()
struct mach_exc_msg *excMsg = (struct mach_exc_msg *)msg;
// Ian Beer uses thread_get_state() - which he would need, since he uses exception_raise,
// but if you use raise_state_identity, you get everything.
if ((excMsg->Head.msgh_id !=2405) && (excMsg->Head.msgh_id != 2407))
{
fprintf(stderr, "Message isn't 2407.. this is weird\n");
}
#if 0
// from osfmk/mach/arm/_structs.h:
_STRUCT_ARM_THREAD_STATE64
{
__uint64_t __x[29]; /* General purpose registers x0-x28 */
__uint64_t __fp; /* Frame pointer x29 */
__uint64_t __lr; /* Link register x30 */
__uint64_t __sp; /* Stack pointer x31 */
__uint64_t __pc; /* Program counter */
__uint32_t __cpsr; /* Current program status register */
__uint32_t __pad; /* Same size for 32-bit or 64-bit clients */
};
#endif
// hexDump(excMsg, 0x300, 0);
printf("TASK: 0x%x, Thread: 0x%x - CODE: 0x%llx/0x%llx, flavor: %x\n",
excMsg->task, excMsg->thread, excMsg->code[0], excMsg->code[1], excMsg->flavor);
mach_port_t thread_port = excMsg->thread.name;
mach_port_t task_port = excMsg->task.name;
#ifndef TEST
_STRUCT_ARM_THREAD_STATE64 * old_state = (_STRUCT_ARM_THREAD_STATE64 *) malloc (614*4); // = (_STRUCT_ARM_THREAD_STATE64 *)excMsg->old_state;
mach_msg_type_number_t cnt = 68; //sizeof(ARM_THREAD_STATE64)/4;;
kr = thread_get_state(thread_port, //
ARM_THREAD_STATE64, // thread_state_flavor_t flavor
(thread_state_t)old_state,
&cnt);
// dumpARMThreadState64(old_state);
uint64_t fileNameAddr = old_state->__x[25];
uint64_t optionsAddr = old_state->__x[1];
char *fileName = malloc(0x200);
uint64_t fileNameSize = 0x200 ;
// kr = task_get_special_port (mach_task_self(), TASK_DEBUG_CONTROL_PORT, &g_AmfidPort);
kr = mach_vm_read_overwrite(task_port, // target_task
fileNameAddr, // address
fileNameSize, // mach_vm_size_t size
(mach_vm_address_t )fileName,
&fileNameSize);
debug("Got request - kr: %d - FileName (@0x%llx): %s (fileNameSize : %d)\n", kr, fileNameAddr, fileName, fileNameSize);
unsigned char *cdh;
cdh = cdHashOfFile(fileName, algorithm);
if (!cdh) { mach_vm_write (task_port,
old_state->__pc, MVSACI_addr,sizeof(uint64_t));
old_state->__pc =MVSACI_addr;
debug("File error or not self signed... redirected to original MVSACI @0x%llx\n", MVSACI_addr);
}
else
{
kr = mach_vm_write(task_port,
old_state->__x[24],
(mach_vm_address_t) cdh,
20); // yep, 20, not 32..
if (kr ==0 ) {
debug("written cdhash for algorithm %d (0x%x 0x%x 0x%x...0x%x) to 0x%llx - kr %d\n",
algorithm,
cdh[0], cdh[1], cdh[2], cdh[19], old_state->__x[24] , kr);
}
else {
error ("Error %d writing CDHash back into AMFI at 0x%llx\n",
kr, old_state->__x[24]);
}
uint32_t one = 1;
kr = mach_vm_write(task_port, old_state->__x[20],
(mach_vm_address_t) &one,sizeof(one));
// Legacy VPN plugins :
// uint32_t five = 5;
// kr = mach_vm_write (AmfidPort, old_state->__x[28], &five, sizeof(five));
// recover
#ifdef _11
old_state->__pc = (old_state->__lr & 0xfffffffffffff000) + 0x1000; // resume
#else
old_state->__pc = (old_state->__lr & 0xfffffffffffff000) + 0x0ef4; // resume
#endif
}
printf("will resume at 0x%llx\n",old_state->__pc);
// -------------------------------
#pragma pack(1)
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
/* int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[614];*/
} excReplyMsg;
#pragma pack(0)
//#if 0
kr = thread_set_state(thread_port, //
ARM_THREAD_STATE64, // thread_state_flavor_t flavor
(thread_state_t)old_state,
cnt);
printf("set state %d - Cnt: %d\n",kr, cnt);
//#endif
excReplyMsg excReply = {0};
// memcpy(excReply.new_state, old_state, sizeof (*old_state));
// excReply.new_stateCnt= excMsg->old_stateCnt ;;
excReply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(excMsg->Head.msgh_bits), 0);
excReply.Head.msgh_size = sizeof(excReply);
excReply.Head.msgh_remote_port = excMsg->Head.msgh_remote_port;
excReply.Head.msgh_local_port = MACH_PORT_NULL;
excReply.Head.msgh_id = excMsg->Head.msgh_id + 100;
// excReply.flavor= excMsg->flavor;
excReply.NDR = excMsg->NDR;
excReply.RetCode = KERN_SUCCESS;
kr = mach_msg(&excReply.Head,
MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
(mach_msg_size_t)sizeof(excReply),
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
// mach_port_deallocate(mach_task_self(), thread_port);
// mach_port_deallocate(mach_task_self(), task_port);
// printf("sent reply - %d - Flavor %d, %d bytes, %x\n", excReply.Head.msgh_id , excReply.flavor,
// excReply.new_stateCnt ,kr);
// printf("REPLY KR: %d\n", kr);
//dumpARMThreadState64(excReply.new_state);
fflush(NULL);
#endif // TEST
}
return 0;
}
void setExceptionHandlerForTask(mach_port_t Victim, void *Handler)
{
mach_port_t exc_port;
// Chapter 11 of the old MOXiI book, if anyone's interested..
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exc_port);
mach_port_insert_right(mach_task_self(), exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND);
#ifndef TEST
kern_return_t kr = task_set_exception_ports(Victim,
EXC_MASK_ALL,
exc_port,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
ARM_THREAD_STATE64);
#endif
pthread_t exception_handling_thread;
pthread_create(&exception_handling_thread, NULL, Handler, (void *) (mach_port_t) exc_port);
printf("SET EXCEPTION HANDLER\n");
} // setExceptionHandlerForTask
#define FAULTING_ADDRESS 0x454d41524542494c // De profondu lacu
int castrateAmfid (mach_port_t AmfidPort)
{
status("Got AMFId's port (0x%x) - Let's castrate this bastard\n", AmfidPort);
// The quick and dirty way is to do it with Ian's method. This also has the upside of enabling
// an effective hook on all third party application startup (or library validations)
// A better way is to patch up AMFId's code because there's plenty of space to fit in
// a SHA-256 CDHash calculator and just jump to it instead of MISValidateSignatureAndCopyInfo
// Anyway:
// (TvOS 11.1, BEE6...CC3 , 270.20.2.0.0)
//
// __DATA __la_symbol_ptr 0x100004150 0x02D1 libmis.dylib _MISValidateSignatureAndCopyInfo
pid_t amfidPid = 0;
kern_return_t kr = pid_for_task (AmfidPort, &amfidPid);
while (amfidPid <1 )
{
if (amfidPid = -1) { debug("Error getting PID from task port. Was I handed an invalid task port?\n"); }
status("HERE - 0x%x, %d\n", AmfidPort, amfidPid);
printf("Sleeping\n");
fflush(NULL);
sleep(1);
pid_t amfidPid = findPidOfProcess("amfid");
mach_port_deallocate (mach_task_self(), AmfidPort);
kern_return_t kr = task_for_pid(mach_task_self(), amfidPid, &AmfidPort);
printf("TFP ON %d - KR %d port %x\n", amfidPid, kr, AmfidPort);
printf("KR %d on port %x\n", kr, AmfidPort);
amfidPid = 0 ;
kr = pid_for_task (AmfidPort, &amfidPid);
}
struct proc_regionwithpathinfo regionsWithPaths;
int reg = 0 ;
uint64_t addr = 0;
int size = 0 ;
status("Getting region info:\n");
fflush(NULL);
int rc = proc_pidinfo( amfidPid,
PROC_PIDREGIONPATHINFO,
addr, // uint64_t arg,
&regionsWithPaths,
sizeof (struct proc_regionwithpathinfo));;
status("Set exception handler:\n");
setExceptionHandlerForTask(AmfidPort, exceptionHandler);
amfidTEXTaddress = regionsWithPaths.prp_prinfo.pri_address;
uint64_t len = sizeof(void *);
uint64_t faultingAddr ;
retry:
kr = mach_vm_read_overwrite (AmfidPort,
amfidTEXTaddress + MVSACI_offset, sizeof(void *) ,
&MVSACI_addr,
&len);
if (kr == KERN_SUCCESS) {
void *h = dlopen("libmis.dylib", 0);
void *MISValidateSignatureAndCopyInfo = (void *) dlsym(h, "MISValidateSignatureAndCopyInfo");
debug("Original address of MVSACI: 0x%llx\n", MVSACI_addr);
debug("NOW SET TO %llx\n", MISValidateSignatureAndCopyInfo);
MVSACI_addr = (uint64_t) MISValidateSignatureAndCopyInfo;
}
else
{
error("Unable to read amfid's memory\n");
return -1;
sleep(2);
}
faultingAddr = FAULTING_ADDRESS;
status("HERE STILL\n");
fflush(NULL);
kr = mach_vm_write(AmfidPort,
amfidTEXTaddress + MVSACI_offset,
&faultingAddr, sizeof(void *));
if (kr ==0 ) { status("patched AMFI through port 0x%x @0x%llx to Faulting addr: 0x%llx\n",AmfidPort,amfidTEXTaddress + MVSACI_offset, faultingAddr );}
else
{
printf("KR: %d\n", kr);
error("Failed to patch AMFI @0x%llx\n",amfidTEXTaddress + MVSACI_offset );
}
uint64_t tryAgain;
kr = mach_vm_read_overwrite (AmfidPort,
amfidTEXTaddress + MVSACI_offset, sizeof(void *) ,
&tryAgain,
&len);
status("TRY AGAIN : 0x%llx\n", tryAgain);
return 0;
}
struct kevent ke;
int getKqueueForPid (pid_t pid)
{
// This is a direct rip from Listing 3-1 in the first edition of MOXiI:
int kq = kqueue();
if (kq == -1) { perror("kqueue"); printf("UNABLE TO CREATE KQUEUE\n"); return -1;}
// Set process fork/exec notifications
else {
EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT_DETAIL , 0, NULL);
// Register event
int rc = kevent(kq, &ke, 1, NULL, 0, NULL);
if (rc < 0) { perror ("kevent"); printf("UNABLE TO GET KEVENT\n"); return -2;}
}
return kq;
}
#ifndef TEST
int main (int argc, char **argv)
{
// Ain't no terminating us
// First find amfid
OUT = fopen ("/tmp/amfidebilitate.out", "w");
if (!OUT) { OUT = stdout;}
if (argc > 1)
{
if (strcmp(argv[1], "sha1") == 0) {
printf("WILL USE SHA-1\n");
algorithm = ALGORITHM_SHA1;
}
else {
printf("WILL USE SHA-256\n");
algorithm = ALGORITHM_SHA256;
}
}
status ("THIS IS AMFIDEBILITATE - Compiled on " __DATE__ "/" __TIME__);
sleep(3);
mach_port_t tsp = MACH_PORT_NULL;
kern_return_t kr = task_get_special_port (mach_task_self(), TASK_DEBUG_CONTROL_PORT, &tsp);
kr = KERN_SUCCESS;
tsp = 0xbb07;
tsp= MACH_PORT_NULL;
#define CS_OPS_ENTITLEMENTS_BLOB 7
struct blob {
uint32_t type;
uint32_t len;
char data[0];
};
struct blob *entBlob = alloca(1024); int entBlobLen = 1024;
bzero (entBlob, entBlobLen);
extern int csops (pid_t, int, char * ,int *);
int rc = csops (getpid(), CS_OPS_ENTITLEMENTS_BLOB, entBlob, &entBlobLen);
extern int errno;
if (rc) printf("CSOPS RC: %d, %s\n", rc,strerror(errno));
else {
printf("RETRIEVED BLOB: %s\n", entBlob->data);
}
#if 0
if (kr == KERN_SUCCESS)
{
pid_t amfidPid = 0 ;
kern_return_t kr = pid_for_task(tsp, &amfidPid);
while (kr == 5) {
status("retrying -- amfid - Pid: %d (KR %d)\n", amfidPid, kr);
// kr = task_get_special_port (mach_task_self(), TASK_DEBUG_CONTROL_PORT, &tsp);
kr = pid_for_task(tsp, &amfidPid);
sleep(1);
}
g_AmfidPort = tsp;
}
#endif
// Got it. Don't want no signals
signal(1, SIG_IGN);
signal(2, SIG_IGN);
signal(15, SIG_IGN);
pid_t amfidPid;
if (tsp == MACH_PORT_NULL)
{
debug("Using task_for_pid. Please make sure you've platformized me..\n");
if (getuid()) {
error ("I have to run as root\n");
}
amfidPid = findPidOfProcess("amfid");
if (!amfidPid) { error ("I can't find amfid!\n"); }
g_AmfidPort = MACH_PORT_NULL;
kern_return_t kr = task_for_pid (mach_task_self(),
amfidPid,
&g_AmfidPort);
if (kr != KERN_SUCCESS) { error("Can't get amfid's task port\n"); exit(12); }
else { status("GOT AMFID (PID %d)'s PORT %d\n", amfidPid, g_AmfidPort); }
}
castrateAmfid(g_AmfidPort);
// Main thread continues to listen for the off chance that amfid will be killed -
// yes, people - it can happen - either due to a bug of mine, but more likely
// because launchd will be fed up with it being idle.
// Anyway, in either case we need to redo this.
pid_t pid = amfidPid; // PID to monitor
int kq; // The kqueue file descriptor int rc; // collecting return values int done;
getKqueueForPid (amfidPid);
for (;;) {
kq = getKqueueForPid(amfidPid);
struct kevent ke;
memset(&ke, '\0', sizeof(struct kevent));
// This blocks until an event matching the filter occurs
rc = kevent(kq, NULL, 0, &ke, 1, NULL);
if (rc >= 0) {
// Don't really care about the kevent - we know it's only because AMFI's dead
close (kq);
status ("AMFI has died!\n");
// TODO: Hook launchd, because it will respawn amfid. Though that's a pain
pid_t new_amfidPid = findPidOfProcess("amfid");
while (! new_amfidPid) {
sleep(1);
new_amfidPid = findPidOfProcess("amfid");
}
amfidPid = new_amfidPid;
kern_return_t kr = task_for_pid (mach_task_self(),
amfidPid,
&g_AmfidPort);
castrateAmfid (g_AmfidPort);
status("*Sigh* Long live amfi - %d... ZZzzz\n", amfidPid);
}
} // end for
}
#else
int main (int argc, char **argv) {
OUT =stderr;
unsigned char *h = cdHashOfFile(argv[1], ALGORITHM_SHA256);
printf("HERE\n");
if (h) {
printf("Hash : 0x%x 0x%x...0x%x\n", h[0], h[1], h[31]);
}
return 0;
}
#endif
#include <CoreFoundation/CoreFoundation.h>
// Simple example to read battery details
// Compile: gcc bat.c -o bat -framework IOKit -framework CoreFoundation
/// Power Mgmt Stuff
// from IOKitUser-755.18.10/ps.subproj/IOPowerSources.h
CFTypeRef IOPSCopyPowerSourcesInfo(void);
CFArrayRef IOPSCopyPowerSourcesList(CFTypeRef blob);
CFDictionaryRef IOPSGetPowerSourceDescription(CFTypeRef blob, CFTypeRef ps);
void
dumpDict (CFDictionaryRef Dict)
{
// Helper function to just dump a CFDictioary as XML
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)Dict);
if (xml) { write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml)); CFRelease(xml); }
}
char *
getPowerDetails(int Debug)
{
CFTypeRef powerInfo;
CFArrayRef powerSourcesList;
CFDictionaryRef powerSourceInformation;
static char returned[80];
powerInfo = IOPSCopyPowerSourcesInfo();
if(! powerInfo) return ("Error: IOPsCopyPowerSourcesInfo()");
powerSourcesList = IOPSCopyPowerSourcesList(powerInfo);
if(!powerSourcesList) {
CFRelease(powerInfo);
return ("Error: IOPSCopyPowerSourcesList()");
}
// Should only get one source. But in practice, check for > 0 sources
if (CFArrayGetCount(powerSourcesList))
{
powerSourceInformation = IOPSGetPowerSourceDescription(powerInfo, CFArrayGetValueAtIndex(powerSourcesList, 0));
if (Debug) dumpDict (powerSourceInformation);
returned[0] = '\0';
CFNumberRef capacityRef = (CFNumberRef) CFDictionaryGetValue(powerSourceInformation, CFSTR("Current Capacity"));
uint32_t capacity;
if ( ! CFNumberGetValue(capacityRef, // CFNumberRef number,
kCFNumberSInt32Type, // CFNumberType theType,
&capacity)) // void *valuePtr);
strcat (returned , "Battery: Unknown");
else
sprintf(returned +strlen(returned), "Battery: %d%%",capacity);
CFStringRef psStateRef = (CFStringRef) CFDictionaryGetValue(powerSourceInformation, CFSTR("Power Source State"));
const char *psState = CFStringGetCStringPtr(psStateRef, // CFStringRef theString,
kCFStringEncodingMacRoman); //CFStringEncoding encoding);
if (!psState) sprintf (returned + strlen(returned), " <unknown> ");
else sprintf (returned + strlen(returned), " (on %s,", psState);
CFBooleanRef isCharging = (CFBooleanRef) CFDictionaryGetValue(powerSourceInformation, CFSTR("Is Charging"));
sprintf(returned +strlen(returned), "%sCharging)", (CFBooleanGetValue(isCharging) ? "": " Not "));
}
CFRelease(powerInfo);
CFRelease(powerSourcesList);
// Ignore the potential memory leak here - this is a demo
return (returned);
}
/// End Power stuff
int
main (int argc, char **argv)
{
char *powerInfo = getPowerDetails(1);
if (powerInfo) printf ("%s\n", powerInfo);
free(powerInfo);
return (0);
}
fffffff006ba7f20:_IOFree.stub
fffffff006ba7f2c:_IOMalloc.stub
fffffff006ba7f38:_OSCompareAndSwap.stub
fffffff006ba7f44:_OSCompareAndSwapPtr.stub
fffffff006ba7f50:_OSDecrementAtomic.stub
fffffff006ba7f5c:_OSIncrementAtomic.stub
fffffff006ba7f68:_PE_i_can_has_debugger.stub
fffffff006ba7f74:_SHA1Final.stub
fffffff006ba7f80:_SHA1Init.stub
fffffff006ba7f8c:_SHA1Update.stub
fffffff006ba7f98:__ZN11OSMetaClass20getMetaClassWithNameEPK8OSSymbol.stub
fffffff006ba7fa4:__ZN12OSDictionary12withCapacityEj.stub
fffffff006ba7fb0:__ZN15OSMetaClassBase12safeMetaCastEPKS_PK11OSMetaClass.stub
fffffff006ba7fbc:__ZN20OSCollectionIterator14withCollectionEPK12OSCollection.stub
fffffff006ba7fc8:__ZN24AppleMobileFileIntegrity16copyEntitlementsEP5ucred
fffffff006ba7fd4:__ZN24AppleMobileFileIntegrity21copySigningIdentifierEP5ucred
fffffff006ba7fe0:__ZN8OSNumber10withNumberEyj.stub
fffffff006ba7fec:__ZN8OSString11withCStringEPKc.stub
fffffff006ba7ff8:__ZN8OSSymbol11withCStringEPKc.stub
fffffff006ba8004:__ZN8OSSymbol17withCStringNoCopyEPKc.stub
fffffff006ba8010:__ZN9IOService12nameMatchingEPKcP12OSDictionary.stub
fffffff006ba801c:__ZN9IOService15serviceMatchingEPKcP12OSDictionary.stub
fffffff006ba8028:__ZN9IOService19getMatchingServicesEP12OSDictionary.stub
fffffff006ba8034:__ZN9IOService22waitForMatchingServiceEP12OSDictionaryy.stub
fffffff006ba8040:__ZNK11OSMetaClass12getClassNameEv.stub
fffffff006ba804c:__ZNK11OSMetaClass13getSuperClassEv.stub
fffffff006ba8058:___stack_chk_fail.stub
fffffff006ba8064:_amfi_copy_seatbelt_profile_names.stub
fffffff006ba8070:_amfi_free_seatbelt_profile_names.stub
fffffff006ba807c:_amfi_register_mac_policy.stub
fffffff006ba8088:_bcopy.stub
fffffff006ba8094:___bzero.stub
fffffff006ba80a0:_convert_port_to_task_suspension_token.stub
fffffff006ba80ac:_convert_task_suspension_token_to_port.stub
fffffff006ba80b8:_convert_task_to_port.stub
fffffff006ba80c4:_copyin.stub
fffffff006ba80d0:_copyinstr.stub
fffffff006ba80dc:_copyout.stub
fffffff006ba80e8:_copyoutstr.stub
fffffff006ba80f4:_cs_entitlement_flags.stub
fffffff006ba8100:_cs_restricted.stub
fffffff006ba810c:_csproc_get_platform_binary.stub
fffffff006ba8118:_ctl_name_by_id.stub
fffffff006ba8124:_current_task.stub
fffffff006ba8130:_current_thread.stub
fffffff006ba813c:_file_drop.stub
fffffff006ba8148:_file_vnode_withvid.stub
fffffff006ba8154:_host_get_special_port.stub
fffffff006ba8160:_host_priv_self.stub
fffffff006ba816c:_ifaddr_address.stub
fffffff006ba8178:_ifnet_free_address_list.stub
fffffff006ba8184:_ifnet_get_address_list_family.stub
fffffff006ba8190:_ifnet_list_free.stub
fffffff006ba819c:_ifnet_list_get.stub
fffffff006ba81a8:_ipc_port_release_send.stub
fffffff006ba81b4:_kauth_cred_getuid.stub
fffffff006ba81c0:_kauth_cred_issuser.stub
fffffff006ba81cc:_kauth_cred_proc_ref.stub
fffffff006ba81d8:_kauth_cred_unref.stub
fffffff006ba81e4:_kauth_proc_label_update.stub
fffffff006ba81f0:_kern_config_is_development.stub
fffffff006ba81fc:_lck_attr_alloc_init.stub
fffffff006ba8208:_lck_grp_alloc_init.stub
fffffff006ba8214:_lck_grp_attr_alloc_init.stub
fffffff006ba8220:_lck_grp_attr_setstat.stub
fffffff006ba822c:_lck_mtx_destroy.stub
fffffff006ba8238:_lck_mtx_init.stub
fffffff006ba8244:_lck_mtx_lock.stub
fffffff006ba8250:_lck_mtx_unlock.stub
fffffff006ba825c:_lck_rw_destroy.stub
fffffff006ba8268:_lck_rw_init.stub
fffffff006ba8274:_lck_rw_lock_exclusive.stub
fffffff006ba8280:_lck_rw_lock_shared.stub
fffffff006ba828c:_lck_rw_unlock_exclusive.stub
fffffff006ba8298:_lck_rw_unlock_shared.stub
fffffff006ba82a4:_mac_label_get.stub
fffffff006ba82b0:_mac_label_set.stub
fffffff006ba82bc:_mac_policy_register.stub
fffffff006ba82c8:_mac_vnop_getxattr.stub
fffffff006ba82d4:_mach_absolute_time.stub
fffffff006ba82e0:_mach_msg_rpc_from_kernel_proper.stub
fffffff006ba82ec:_mach_msg_send_from_kernel_proper.stub
fffffff006ba82f8:_mach_vm_region.stub
fffffff006ba8304:_matchExec
fffffff006ba8310:_matchFree
fffffff006ba831c:_matchInit
fffffff006ba8328:_matchUnpack
fffffff006ba8334:_memcmp.stub
fffffff006ba8340:_memcpy.stub
fffffff006ba834c:_memset.stub
fffffff006ba8358:_mig_dealloc_reply_port.stub
fffffff006ba8364:_mig_get_reply_port.stub
fffffff006ba8370:_mig_put_reply_port.stub
fffffff006ba837c:_mig_strncpy.stub
fffffff006ba8388:_mig_strncpy_zerofill.stub
fffffff006ba8394:_panic.stub
fffffff006ba83a0:_printf.stub
fffffff006ba83ac:_proc_find.stub
fffffff006ba83b8:_proc_getexecutablevnode.stub
fffffff006ba83c4:_proc_name.stub
fffffff006ba83d0:_proc_pgrpid.stub
fffffff006ba83dc:_proc_pid.stub
fffffff006ba83e8:_proc_pidversion.stub
fffffff006ba83f4:_proc_ppid.stub
fffffff006ba8400:_proc_rele.stub
fffffff006ba840c:_proc_self.stub
fffffff006ba8418:_proc_selfname.stub
fffffff006ba8424:_proc_selfpgrpid.stub
fffffff006ba8430:_proc_selfpid.stub
fffffff006ba843c:_proc_signal.stub
fffffff006ba8448:_proc_suser.stub
fffffff006ba8454:_proc_task.stub
fffffff006ba8460:_proc_uniqueid.stub
fffffff006ba846c:_read_random.stub
fffffff006ba8478:_sbuf_data.stub
fffffff006ba8484:_sbuf_delete.stub
fffffff006ba8490:_sbuf_finish.stub
fffffff006ba849c:_sbuf_new.stub
fffffff006ba84a8:_sbuf_printf.stub
fffffff006ba84b4:_snprintf.stub
fffffff006ba84c0:_sock_getsockname.stub
fffffff006ba84cc:_sock_gettype.stub
fffffff006ba84d8:_sock_iskernel.stub
fffffff006ba84e4:_strcasecmp.stub
fffffff006ba84f0:_strchr.stub
fffffff006ba84fc:_strcmp.stub
fffffff006ba8508:_strlcat.stub
fffffff006ba8514:_strlcpy.stub
fffffff006ba8520:_strlen.stub
fffffff006ba852c:_strncmp.stub
fffffff006ba8538:_sysctl_register_oid.stub
fffffff006ba8544:_task_reference.stub
fffffff006ba8550:_task_resume2.stub
fffffff006ba855c:_task_suspend2.stub
fffffff006ba8568:_thread_tid.stub
fffffff006ba8574:_vfs_context_create.stub
fffffff006ba8580:_vfs_context_get_special_port.stub
fffffff006ba858c:_vfs_context_rele.stub
fffffff006ba8598:_vfs_context_set_special_port.stub
fffffff006ba85a4:_vfs_flags.stub
fffffff006ba85b0:_vfs_getbyid.stub
fffffff006ba85bc:_vfs_name.stub
fffffff006ba85c8:_vfs_rootvnode.stub
fffffff006ba85d4:_vfs_statfs.stub
fffffff006ba85e0:_vfs_vnodecovered.stub
fffffff006ba85ec:_vm_allocate.stub
fffffff006ba85f8:_vm_deallocate.stub
fffffff006ba8604:_vm_map_copy_discard.stub
fffffff006ba8610:_vm_map_copyin.stub
fffffff006ba861c:_vm_map_copyout.stub
fffffff006ba8628:_vm_map_page_mask.stub
fffffff006ba8634:_vm_map_round_page_mask.stub
fffffff006ba8640:_vm_map_trunc_page_mask.stub
fffffff006ba864c:_vm_map_unwire.stub
fffffff006ba8658:_vm_map_wire_external.stub
fffffff006ba8664:_vn_path_package_check.stub
fffffff006ba8670:_vnode_authorize.stub
fffffff006ba867c:_vnode_get.stub
fffffff006ba8688:_vnode_getattr.stub
fffffff006ba8694:_vnode_getfromfd.stub
fffffff006ba86a0:_vnode_getparent.stub
fffffff006ba86ac:_vnode_getwithvid.stub
fffffff006ba86b8:_vnode_isblk.stub
fffffff006ba86c4:_vnode_ischr.stub
fffffff006ba86d0:_vnode_isdir.stub
fffffff006ba86dc:_vnode_isdyldsharedcache.stub
fffffff006ba86e8:_vnode_istty.stub
fffffff006ba86f4:_vnode_isvroot.stub
fffffff006ba8700:_vnode_lookup.stub
fffffff006ba870c:_vnode_mount.stub
fffffff006ba8718:_vnode_put.stub
fffffff006ba8724:_vnode_tag.stub
0xfffffff006ba2398:_mpo_cred_check_label_update_execve
0xfffffff006b96cc8:_mpo_cred_check_label_update
0xfffffff006b96cf8:_mpo_cred_label_associate
0xfffffff006b96d24:_mpo_cred_label_destroy
0xfffffff006ba23a0:_mpo_cred_label_update_execve
0xfffffff006b96d2c:_mpo_cred_label_update
0xfffffff006b96d6c:_mpo_file_check_fcntl
0xfffffff006b96dc0:_mpo_file_check_mmap
0xfffffff006b96e44:_mpo_file_check_set
0xfffffff006b96e88:_mpo_mount_check_fsctl
0xfffffff006b96ee4:_mpo_mount_check_mount
0xfffffff006b96f84:_mpo_mount_check_remount
0xfffffff006b97020:_mpo_mount_check_umount
0xfffffff006b970dc:_mpo_policy_init
0xfffffff006b97238:_mpo_policy_initbsd
0xfffffff006b972d0:_mpo_policy_syscall
0xfffffff006b97410:_mpo_system_check_sysctlbyname
0xfffffff006b975c4:_mpo_vnode_check_rename
0xfffffff006b977d8:_mpo_kext_check_query
0xfffffff006b97824:_mpo_iokit_check_nvram_get
0xfffffff006b97880:_mpo_iokit_check_nvram_set
0xfffffff006b97a88:_mpo_iokit_check_nvram_delete
0xfffffff006b97ae4:_mpo_proc_check_expose
0xfffffff006b97b4c:_mpo_proc_check_set_host_special_port
0xfffffff006b97ba8:_mpo_proc_check_set_host_exception_port
0xfffffff006b97bec:_mpo_posixsem_check_create
0xfffffff006b97c74:_mpo_posixsem_check_open
0xfffffff006b97c7c:_mpo_posixsem_check_post
0xfffffff006b97cec:_mpo_posixsem_check_unlink
0xfffffff006b97cf4:_mpo_posixsem_check_wait
0xfffffff006b97d64:_mpo_posixshm_check_create
0xfffffff006b97dc4:_mpo_posixshm_check_open
0xfffffff006b97e90:_mpo_posixshm_check_stat
0xfffffff006b97ef0:_mpo_posixshm_check
0xfffffff006b97f50:_mpo_posixshm_check_unlink
0xfffffff006b97fb0:_mpo_proc_check_debug
0xfffffff006b9800c:_mpo_proc_check_fork
0xfffffff006b98050:_mpo_proc_check_get_task_name
0xfffffff006b980ac:_mpo_proc_check_get_task
0xfffffff006b98168:_mpo_proc_check_sched
0xfffffff006b981ac:_mpo_proc_check_setaudit
0xfffffff006b981f0:_mpo_proc_check_setauid
0xfffffff006b98234:_mpo_proc_check_signal
0xfffffff006b982a0:_mpo_socket_check_bind
0xfffffff006b982b4:_mpo_socket_check_connect
0xfffffff006b982f0:_mpo_socket_check_create
0xfffffff006b98394:_mpo_socket_check_listen
0xfffffff006b983ac:_mpo_socket_check_receive
0xfffffff006b983c4:_mpo_socket_check_send
0xfffffff006b983e0:_mpo_system_check_acct
0xfffffff006b98424:_mpo_system_check_audit
0xfffffff006b98468:_mpo_system_check_auditctl
0xfffffff006b984ac:_mpo_system_check_auditon
0xfffffff006b984f0:_mpo_system_check_host_priv
0xfffffff006b9853c:_mpo_system_check_nfsd
0xfffffff006b98580:_mpo_system_check_reboot
0xfffffff006b985c4:_mpo_system_check_settime
0xfffffff006b98608:_mpo_system_check_swapoff
0xfffffff006b9864c:_mpo_system_check_swapon
0xfffffff006b98690:_mpo_sysvmsq_check_enqueue
0xfffffff006b986d4:_mpo_sysvmsq_check_msgrcv
0xfffffff006b98718:_mpo_sysvmsq_check_msgrmid
0xfffffff006b9875c:_mpo_sysvmsq_check_msqctl
0xfffffff006b987a0:_mpo_sysvmsq_check_msqget
0xfffffff006b987e4:_mpo_sysvmsq_check_msqrcv
0xfffffff006b98828:_mpo_sysvmsq_check_msqsnd
0xfffffff006b9886c:_mpo_sysvsem_check_semctl
0xfffffff006b988b0:_mpo_sysvsem_check_semget
0xfffffff006b988f4:_mpo_sysvsem_check_semop
0xfffffff006b98938:_mpo_sysvshm_check_shmat
0xfffffff006b9897c:_mpo_sysvshm_check_shmctl
0xfffffff006b989c0:_mpo_sysvshm_check_shmdt
0xfffffff006b98a04:_mpo_sysvshm_check_shmget
0xfffffff006b98a48:_mpo_mount_check_snapshot_create
0xfffffff006b98ac0:_mpo_check_snapshot_delete
0xfffffff006b98b38:_mpo_vnode_check_clone
0xfffffff006b98ce4:_mpo_proc_check_get_cs_info
0xfffffff006b98d70:_mpo_proc_check_set_cs_info
0xfffffff006b98dcc:_mpo_iokit_check_hid_control
0xfffffff006b98e10:_mpo_vnode_check_access
0xfffffff006b98f14:_mpo_vnode_check_chroot
0xfffffff006b98f78:_mpo_vnode_check_create
0xfffffff006b990f0:_mpo_vnode_check_deleteextattr
0xfffffff006b99170:_mpo_vnode_check_exchangedata
0xfffffff006b99280:_mpo_vnode_check_exec
0xfffffff006b99404:_mpo_vnode_check_getattrlist
0xfffffff006b99468:_mpo_vnode_check_getextattr
0xfffffff006b994cc:_mpo_vnode_check_ioctl
0xfffffff006b995a4:_mpo_vnode_check_link
0xfffffff006b99788:_mpo_vnode_check_listextattr
0xfffffff006b997ec:_mpo_vnode_check_open
0xfffffff006b998b0:_mpo_vnode_check_readlink
0xfffffff006b99914:_mpo_vnode_check_revoke
0xfffffff006b99978:_mpo_vnode_check_setattrlist
0xfffffff006b999dc:_mpo_vnode_check_setextattr
0xfffffff006b99a5c:_mpo_vnode_check_setflags
0xfffffff006b99af0:_mpo_vnode_check_setmode
0xfffffff006b99c38:_mpo_vnode_check_setowner
0xfffffff006b99c9c:_mpo_vnode_check_setutimes
0xfffffff006b99cfc:_mpo_vnode_check_stat
0xfffffff006b99d60:_mpo_vnode_check
0xfffffff006b99dc4:_mpo_vnode_check_unlink
0xfffffff006b99ec8:_mpo_vnode_notify_create
0xfffffff006b9a0e0:_mpo_vnode_check_uipc_bind
0xfffffff006b9a144:_mpo_vnode_check_uipc_connect
0xfffffff006b9a1bc:_mpo_proc_check_suspend_resume
0xfffffff006b9a200:_mpo_iokit_check_set_properties
0xfffffff006b9a25c:_mpo_system_check_chud
0xfffffff006b9a2a0:_mpo_vnode_check_searchfs
0xfffffff006b9a304:_mpo_priv_check
0xfffffff006b9a360:_mpo_priv_grant
0xfffffff006b9a3d4:_mpo_vnode_check_fsgetpath
0xfffffff006b9a438:_mpo_iokit_check_open
0xfffffff006b9a494:_mpo_vnode_notify_rename
0xfffffff006b9a4f4:_mpo_vnode_check_setacl
0xfffffff006b9a558:_mpo_proc_check_cpumon
0xfffffff006b9a5c8:_mpo_pty_notify_grant
0xfffffff006b9a624:_mpo_pty_notify_close
0xfffffff006b9a704:_mpo_vnode_find_sigs
0xfffffff006b9a7e4:_mpo_kext_check_unload
0xfffffff006b9a840:_mpo_proc_check_proc_info
0xfffffff006b9a89c:_mpo_vnode_notify_link
0xfffffff006b9a93c:_mpo_iokit_check_get_property
0xfffffff006b95000:_sb_fsa_evaluate
0xfffffff006b95408:_match_sequence
0xfffffff006b954a0:_match_variable
0xfffffff006b95544:_hash
0xfffffff006b95570:_getpath
0xfffffff006b956cc:_address_in_static_profile
0xfffffff006b9571c:_gets_boolean_entitlement
0xfffffff006b9577c:_locks_unlocks| likely something from MacOS, which has been #ifdef'ed out
0xfffffff006b957b0:_extension_create_file
0xfffffff006b9584c:_extension_create
0xfffffff006b958a8:_extension_create_mach
0xfffffff006b95934:_extension_create_iokit_registry_class
0xfffffff006b959c0:_extension_create_posix_ipc
0xfffffff006b95a4c:_extension_create_user_preference
0xfffffff006b95ad8:_extension_create_sysctl
0xfffffff006b95b64:_extension_destroy
0xfffffff006b95bd8:_extension_add
0xfffffff006b95d0c:_sandbox_allow_file
0xfffffff006b95e94:_deconstruct_path
0xfffffff006b96044:_sandbox_revoke_file
0xfffffff006b96160:_profile_create
0xfffffff006b96380:_profile_release
0xfffffff006b9657c:_cred_get_sandbox
0xfffffff006b96684:_builtin_register
0xfffffff006b966fc:_builtin_sandbox_create
0xfffffff006b968ec:__toggles_builtin_profiles_on_or_off
0xfffffff006b969c0:_sandbox_create_likely
0xfffffff006b96ad4:_revoke_privileged_ports
0xfffffff006b96c70:_cred_sb_evaluate
0xfffffff006b9cb88:_func_fffffff006b9cb88
0xfffffff006b9d6d8:_sandbox_set_container_copyin
0xfffffff006b9d764:_proc_apply_sandbox:
0xfffffff006b9d884:_func_fffffff006b9d884
0xfffffff006b9d8e8:_hmac_sha1
0xfffffff006b9da14:_func_fffffff006b9da14
0xfffffff006b9dbb4:_rootless_forbid_xattr
0xfffffff006b9dc80:_sandbox_report
0xfffffff006b9dd14:_sandbox_trace
0xfffffff006b9dd88:_sandbox_trace_init_kernel
0xfffffff006b9dec4:_sandbox_builtin_likely
0xfffffff006b9e06c:_smalloc
0xfffffff006b9e0c4:_smalloc_track
0xfffffff006b9e14c:_smalloc_set_description
0xfffffff006b9e150:_sstrdup
0xfffffff006b9e1a0:_sstrdup_track
0xfffffff006b9e1fc:_srealloc
0xfffffff006b9e28c:_sfree
0xfffffff006b9e338:_sreallocf
0xfffffff006b9e370:_scopyinstr
0xfffffff006b9e410:_free_filter_context
0xfffffff006b9e558:_memchr
0xfffffff006b9e590:_derive_vnode_path
0xfffffff006b9e640:_derive_vnode_type
0xfffffff006b9e70c:_derive_vnode_rdev
0xfffffff006b9e7fc:_set_sandbox_flags
0xfffffff006b9e898:_re_cache_init_likely
0xfffffff006b9eac8:_re_cache_uninit_likely
0xfffffff006b9eb1c:_var_ordinals_populate
0xfffffff006b9ed18:_collection_load_profiles
0xfffffff006b9eec0:_func_fffffff006b9eec0
0xfffffff006b9f0fc:_derive_cred
0xfffffff006b9f164:_eval
0xfffffff006b9f450:switch_case_0?
0xfffffff006b9f450:switch_case_1?
0xfffffff006ba0108:switch_case_2?
0xfffffff006b9f5f4:switch_case_3?
0xfffffff006b9f678:switch_case_4?
0xfffffff006b9f684:switch_case_5?
0xfffffff006b9f6a0:switch_case_6?
0xfffffff006b9f55c:switch_case_7?
0xfffffff006b9f55c:switch_case_8?
0xfffffff006b9f6c4:switch_case_9?
0xfffffff006b9f74c:switch_case_10?
0xfffffff006b9f758:switch_case_11?
0xfffffff006b9f764:switch_case_12?
0xfffffff006b9f774:switch_case_13?
0xfffffff006b9f784:switch_case_14?
0xfffffff006b9f794:switch_case_15?
0xfffffff006b9f7a0:switch_case_16?
0xfffffff006b9f7f8:switch_case_17?
0xfffffff006b9f808:switch_case_18?
0xfffffff006b9f34c:switch_case_19?
0xfffffff006b9f34c:switch_case_20?
0xfffffff006b9f34c:switch_case_21?
0xfffffff006b9f860:switch_case_22?
0xfffffff006b9f944:switch_case_23?
0xfffffff006b9f950:switch_case_24?
0xfffffff006b9f95c:switch_case_25?
0xfffffff006b9f9a0:switch_case_26?
0xfffffff006b9f9ac:switch_case_27?
0xfffffff006b9f9b8:switch_case_28?
0xfffffff006b9fa5c:switch_case_29?
0xfffffff006b9fb28:switch_case_30?
0xfffffff006b9fb54:switch_case_31?
0xfffffff006b9fb78:switch_case_32?
0xfffffff006b9fb84:switch_case_33?
0xfffffff006b9fb90:switch_case_34?
0xfffffff006b9fb9c:switch_case_35?
0xfffffff006b9fbb8:switch_case_36?
0xfffffff006b9fc1c:switch_case_37?
0xfffffff006b9fc28:switch_case_38?
0xfffffff006ba0cb8:switch_case_39?
0xfffffff006b9fc3c:switch_case_40?
0xfffffff006b9fe00:switch_case_41?
0xfffffff006b9fed4:switch_case_42?
0xfffffff006b9ffbc:switch_case_43?
0xfffffff006b9ffd4:switch_case_44?
0xfffffff006ba005c:switch_case_45?
0xfffffff006ba00a0:switch_case_46?
0xfffffff006ba0cb8:switch_case_47?
0xfffffff006ba00ac:switch_case_48?
0xfffffff006ba00c8:switch_case_49?
0xfffffff006ba00f8:switch_case_50?
0xfffffff006b9ff7c:switch_case_51?
0xfffffff006ba0514:switch_case_52?
0xfffffff006ba0528:switch_case_53?
0xfffffff006ba0720:switch_case_54?
0xfffffff006ba073c:switch_case_55?
0xfffffff006ba0100:switch_case_56?
0xfffffff006ba0680:switch_case_57?
0xfffffff006ba0698:switch_case_58?
0xfffffff006ba06bc:switch_case_59?
0xfffffff006ba06cc:switch_case_60?
0xfffffff006ba0708:switch_case_61?
0xfffffff006ba0724:switch_case_62?
0xfffffff006ba1030:_derive_socket_info
0xfffffff006ba10c8:_match_regex
0xfffffff006ba1170:_match_pattern
0xfffffff006ba1250:_is_localaddr
0xfffffff006ba161c:_sb_report
0xfffffff006ba1a48:_kernel_report
0xfffffff006ba1bd4:_get_report_context
0xfffffff006ba1f18:_sandbox_trace_init
0xfffffff006ba1fcc:_sb_trace
0xfffffff006ba2174:_sb_builtin
0xfffffff006ba22f8:_platform_set_container
0xfffffff006ba2fc0:_platform_backtraces
0xfffffff006ba2ff8:_platform_apple_internal
0xfffffff006ba3020:_platform_start
0xfffffff006ba319c:_func_fffffff006ba319c
0xfffffff006ba3258:_ustate_home_dir
0xfffffff006ba32c8:_lock_and_increment_2
0xfffffff006ba3314:_decrement_and_free_and_destroy_if_needed
0xfffffff006ba3354:_lock_and_increment
0xfffffff006ba3414:_func_fffffff006ba3414
0xfffffff006ba34a0:_sb_ustate_manager_inspect
0xfffffff006ba3564:_calls_packs_collections
0xfffffff006ba3658:_free_and_destroy
fffffff006ba3520:no_user_state
0xfffffff006ba36d0:_packs_collections
0xfffffff006ba37e8:_func_fffffff006ba37e8
0xfffffff006ba37f8:___will_set_container_entry
0xfffffff006ba3810:___sb_state_set_container_entry
0xfffffff006ba3918:___sb_state_set_container_entry_wrapper
0xfffffff006ba3930:___sb_state_remove_container_entry_wrapper
0xfffffff006ba3940:___sb_state_remove_container_entry
0xfffffff006ba39b8:_removes_ustate_container_by_x0+40
0xfffffff006ba39c8:_gets_ustate_container_path_by_x0+32
0xfffffff006ba3a70:_gets_ustate_container_path_by_x0+40
0xfffffff006ba3a80:_ustate_sets_removes_container_todo
0xfffffff006ba3ac0:_ustate_container_path_for_uid_472_480_todo
0xfffffff006ba3adc:___sb_state_container_entry_related_maybe_temporary
0xfffffff006ba3b1c:_func_fffffff006ba3b1c
0xfffffff006ba3b38:_func_fffffff006ba3b38
0xfffffff006ba3bb0:_func_fffffff006ba3bb0
0xfffffff006ba3bcc:_func_fffffff006ba3bcc
0xfffffff006ba42f0:_sandbox_normalize_string
0xfffffff006ba4334:_func_fffffff006ba4334
0xfffffff006ba43a8:_entitlements_init
0xfffffff006ba43c4:_func_fffffff006ba43c4
0xfffffff006ba43f8:___ZL7getAMFIv
0xfffffff006ba44dc:_get_entitlement_value_by_cred
0xfffffff006ba4858:_copy_signing_identifier
0xfffffff006ba488c:_sb_packbuff_new_with_tag:
0xfffffff006ba4930:_sb_packbuff_init_with_buffer_and_tag
0xfffffff006ba4a04:_sb_packbuff_init_with_buffer
0xfffffff006ba3be8:_userstate_set
0xfffffff006ba4aac:_sb_packbuff_free
0xfffffff006ba4b28:_sb_packbuff_get_bytes
0xfffffff006ba4b30:_sb_packbuff_get_size
0xfffffff006ba4b38:_sb_packbuff_set_size
0xfffffff006ba4b48:_sb_packbuff_get_item_value_type
0xfffffff006ba4b70:_sb_packbuff_unpack_uint32
0xfffffff006ba4bb8:_sb_packbuff_unpack_item
0xfffffff006ba4c8c:_sb_packbuff_unpack_string
0xfffffff006ba4d84:_sb_packbuff_pack_item
0xfffffff006ba4d0c:_sb_packbuff_unpack_bytes
0xfffffff006ba4d64:_sb_packbuff_pack_string
0xfffffff006ba4ea8:_sb_packbuff_pack_uint32
0xfffffff006ba4ee8:_sb_packbuff_pack_bytes
0xfffffff006ba4f1c:_sb_packbuff_pack_key_with_string_value
0xfffffff006ba4fa4:_sb_packbuff_unpack_key_with_string_value
0xfffffff006ba5060:_sb_packbuff_alloc_vm_buffer
0xfffffff006ba5258:_sb_container_manager_request_new
0xfffffff006ba53bc:_sb_container_manager_request_append_app_group_id
0xfffffff006ba5420:_sb_container_manager_request_append_system_container_id
0xfffffff006ba5484:_sb_container_manager_request_append_system_group_container_id
0xfffffff006ba54e8:_sb_container_manager_request_send
0xfffffff006ba56c8:_get_signing_identifier
0xfffffff006ba5788:_get_container_required_entitlement
0xfffffff006ba2590:i_kill_u
0xfffffff006ba6024:_will_issue_extensions
0xfffffff006ba617c:_processes_entitlements_from_user_state
0xfffffff006ba635c:_processes_user_state_for_uid
0xfffffff006ba6438:no_user_state_for_this_uid
0xfffffff006ba6568:_func_fffffff006ba6568
0xfffffff006ba692c:_upcall_to_containermanagerd
0xfffffff006ba6dbc:_platform_start
0xfffffff006ba70d8:_func_fffffff006ba70d8
0xfffffff006ba7770:_sb_container_manager_get_process_containers
0xfffffff006ba78cc:_sb_evaluate_properties
0xfffffff006b9dc14:_iokit_property_callback
0xfffffff006ba79b8:_func_fffffff006ba79b8
0xfffffff006ba7a48:_func_fffffff006ba7a48
0xfffffff006ba7aec:_func_fffffff006ba7aec
0xfffffff006ba7b94:_func_fffffff006ba7b94
0xfffffff006ba7c9c:_func_fffffff006ba7c9c
0xfffffff006ba7dbc:_client_class_name
0xfffffff006ba7de4:_io_object_retain
0xfffffff006ba7e18:_io_object_release
0xfffffff006ba7e2c:_io_object_get_string
0xfffffff006ba7e64:_io_object_get_boolean
0xfffffff006fba870:_operation_names
0xfffffff006fbacc0:_operation_names_2
0xfffffff006fbb0d8:_minimize_reporting.sandboxd_dependencies
0xfffffff006ba5390:common_printf_and_fail
0xfffffff006b9b2fc:likely_syscall_check_sandbox_bulk
fffffff006ba49fc:_sb_packbuff_new
0xfffffff006b96584:_label_get_sandbox
0xfffffff006b9eec0:_sb_evaluate
0xfffffff006b96254:_sandbox_release
0xfffffff006b965f4:_cred_set_sandbox
0xfffffff006b965fc:_label_set_sandbox
0xfffffff006ba2a50:container_with_no_code_signing_identity
0xfffffff006ba2914:values_missing_from_seatbelt_entitlement
0xfffffff006ba2a64:outside_of_container_and_not_i_can_haz_debugger
0xfffffff006ba29cc:outside_of_container
0xfffffff006ba252c:common_failure
0xfffffff0076b5208:__ZZL7getAMFIvE4amfi
0xfffffff006b9db2c:_cred_check_socket
0xfffffff006b9da70:_cred_check_posix_semaphore
0xfffffff006b9d884:_operation_is_forbidden
0xfffffff006b9b4a0:x24_will_be_3
0xfffffff006b9b4b0:x24_will_be_12
0xfffffff006b9ac54:_likely_syscall_check_sandbox
0xfffffff006b9b8a0:_syscall_extension_issue:
0xfffffff00630e630:the_real_platform_profile_data
0xfffffff006fbaca8:platform_profile
0xfffffff00630fe80:the_real_collection_data
0xfffffff0076b5100:sandbox_lock_grp
0xfffffff0076b5158:sandbox_lock_attr
0xfffffff0076b51f8:__ZL17symbol_cache_lock
0xfffffff006b9a9dc:_syscall_set_profile
0xfffffff006b9ab40:_syscall_set_profile_builtin
0xfffffff006b9be74:_syscall_extension_consume
0xfffffff006b9bb30:case_0
0xfffffff006b9bc2c:case_1
0xfffffff006b9bc60:case_2
0xfffffff006b9bc48:case_3
0xfffffff006b9bc50:case_4
0xfffffff0076b50b8:debug_mode
0xfffffff0076b4cc0:sentinel
0xfffffff0076b5170:secret
0xfffffff006ba0cb0:sentinel_related
0xfffffff006b9da14:_unhex
0xfffffff006b96b9c:_sandbox_check_vnode
0xfffffff0076b4fd8:kmod_info
0xfffffff006ba7ed4:_OSKextGetCurrentIdentifier
0xfffffff006ba7ee4:_OSKextGetCurrentVersionString
0xfffffff006ba7ef4:_OSKextGetCurrentLoadTag
0xfffffff006ba7f04:__stop
0xfffffff006ba7eb8:__start
0xfffffff006b96c2c:__kmod_start
0xfffffff006b96c68:__kmod_stop
0xfffffff0076b5130:policy_handle
0xfffffff006fb9da8:policy_conf
0xfffffff006ba6ed8:_extension_issue_application_group
0xfffffff006ba7060:_extension_issue_mach_lookup_and_register
0xfffffff006ba70d8:_extension_issue_security_exception_absolute_path
0xfffffff006ba731c:_extension_issue_security_exception_home_relative_path
0xfffffff006ba7408:_extension_issue_iokit_user_client
0xfffffff006ba74f8:_extension_issue_sysctl
0xfffffff006ba7480:_extension_issue_preferences
0xfffffff006ba7570:_extension_issue_system_containers
0xfffffff006ba7664:_extension_issue_system_group_containers
0xfffffff0076b4e28:_extension_issuer_table
0xfffffff006ba5d30:a_case_0
0xfffffff006ba5dbc:a_case_1_data_container_path
0xfffffff006ba5de4:a_case_2_temp_container_path
0xfffffff006ba5e0c:a_case_3
0xfffffff006b9c0c8:ext_case_0_file
0xfffffff006b9c170:ext_case_1_mach
0xfffffff006b9c140:ext_case_2_ioregistry
0xfffffff006b9c1f8:ext_case_3_generic
0xfffffff006b9c158:ext_case_4_posix_ipc
0xfffffff006b9b6c0:_syscall_note
0xfffffff006b9b788:_syscall_container
0xfffffff006b9c238:_syscall_extension_release
0xfffffff006b9c498:_syscall_extension_update_file
0xfffffff006b9c770:_syscall_extension_twiddle
0xfffffff006b9ca64:_syscall_suspend
0xfffffff006b9730c:syscall__note
0xfffffff006b9731c:syscall__extension_issue
fffffff006b9732c:syscall__extesion_release
fffffff006b97334:syscall__extension_update_file
fffffff006b9733c:syscall__extension_twiddle
fffffff006b97378:return_0x4E
fffffff006b97344:syscall__suspend
fffffff006b9734c:syscall__unsuspend
0xfffffff006b97314:syscall__container
0xfffffff006b97324:syscall__extension_consume
0xfffffff006b9cb88:_syscall_unsuspend
0xfffffff006b9737c:syscall__fail
0xfffffff006b97364:syscall__passthrough
0xfffffff006b9736c:syscall__platform_policy_syscall
0xfffffff006b97370:syscall__inspect
0xfffffff006b97384:syscall__neuter_builtin
fffffff006b9738c:syscall_sandbox_check_bulk
fffffff006b97394:syscall__check_task
fffffff006b9739c:syscall__rootless_whitelist_check
fffffff006b972f4:syscall__set_profile
fffffff006b972fc:syscall__set_profile_builtin
fffffff006b97304:syscall__check_sandbox
0xfffffff006b9cbe0:_syscall_passthrough_access
0xfffffff006b9d638:_syscall_rootless_whitelist_check (idle)
0xfffffff006b9cd3c:_syscall_inspect
0xfffffff006ba5860:_syscall_container_map
0xfffffff006b9d478:_syscall_neuter_builtin
0xfffffff006ba6d24:_printf_fail_common
0xfffffff006ba6c54:was_d_CM_KERN_REPLY_HOME_DIRECTORY_PATH
0xfffffff006ba6c78:was_e_CM_KERN_REPLY_SYSTEM_CONTAINER_PATH
0xfffffff006ba6ca0:unknown_value
0xfffffff006ba6b90:was_f
0xfffffff006ba6b6c:failed_to_unpack_KERN_REPLY_STATUS
0xfffffff006ba6c68:failed_to_unpack_CM_KERN_REPLY_HOME_DIRECTORY_PATH
0xfffffff006ba6b78:do_type
0xfffffff006ba6cf4:failed_to_unpack_CM_KERN_REPLY_SYSTEM_CONTAINER_PATH
0xfffffff006ba6b54:type_0
0xfffffff006ba6bcc:type_1_CM_KERN_REPLY_DATA_CONTAINER_PATH
0xfffffff006ba6c00:type_2_CM_KERN_REPLY_TEMP_CONTAINER_PATH
0xfffffff006ba6c28:type_3
0xfffffff006ba6d04:invalid_for_CM_KERN_REPLY_SYSTEM_GROUP_CONTAINER_PATH
0xfffffff006ba6d14:invalid_for_CM_KERN_REPLY_APP_GROUP_CONTAINER_PATH
0xfffffff006ba6cec:success
0xfffffff006ba6cc0:failed_to_unpack_CM_KERN_REPLY_DATA_CONTAINER_PATH
0xfffffff006ba6c48:failed_to_unpack
0xfffffff006ba5f38:invalid_value_type_for_SYSTEM_GROUP_CONTAINER_PATH
0xfffffff006ba5f4c:invalid_value_type_for_APP_GROUP_CONTAINER_PATH
0xfffffff006ba5ec8:failed_to_unpack_CM_KERN_REPLY_TEMP_CONTAINER_PATH
0xfffffff006ba5e3c:was_d_home_directory
0xfffffff006ba5e64:was_e_system_container
fffffff006ba5e9c:do_status
fffffff006b9cb60:can_suspend
fffffff006b9cb24:cannot_suspend
0xfffffff006ba7870:incorrect_reply
0xfffffff006ba55d4:get_process_containers_successful
fffffff006ba5304:failed_to_create_packbuffer
fffffff006ba30b4:failed_to_create_default_front_user
0xfffffff006ba319c:sb_ustate_create|creates_user_state:default_front_user
0xfffffff006b96754:loop
0xfffffff0076b4e18:label_names
0xfffffff0076b50c0:sysctl__security_mac_sandbox_children
0xfffffff0076b4cd0:sentinel_len
0xfffffff0076b4d78:_sysctl__security_mac_sandbox_debug_mode
0xfffffff0076b4d28:_sysctl__security_mac_sandbox_sentinel
0xfffffff0076b4dc8:_sysctl__security_mac_sandbox_debug_mode
0xfffffff0076b50a0:__realmain
0xfffffff0076b50a8:__antimain
0xfffffff0076b5118:builtin_list_probably
0xfffffff006b96930:profile_loop
0xfffffff006b9d814:can_apply_sandbox|Otherwise, go ahead and apply sandbox on this process
0xfffffff0076b50c8:apply_lock
fffffff006b9d7cc:|if a sandbox already exists for this process, forbid attempt
fffffff006ba23f0:|call on AMFI to get the seatbelt-profiles (builtin-names to apply) from entitlement, if any
fffffff006ba2440:amfi_has_failed_us
fffffff006ba2454:so_far_so_good
0xfffffff006ba1554:_pattern_variable_resolver_maybe
fffffff006ba2508:failure_when_getting_signing_identifier
fffffff006ba251c:failure_when_getting_container_required
fffffff006ba2928:ignore_builtin_for_platform
fffffff006ba2900:stacked_profiles_not_supported
fffffff006ba2838:check_stacked_profiles
fffffff006ba27e8:check_no_container_entitlement
0xfffffff006ba29a0:container_is_required
fffffff006ba24f0:bogus_sandbox_spawnattrs
fffffff006ba2634:still_here
fffffff006ba26f8:in_containers_bundle
0xfffffff006ba2a78:check_container_path
0xfffffff006ba2aac:container_path_ok
fffffff006ba2c74:system_container_without_codesigning_id
fffffff006ba2c60:failed_to_find_container
fffffff006ba2c88:check_exec_matches_bundle
fffffff006ba2da0:unable_to_allocate_container_name
fffffff006ba2dcc:unable_to_resolve_bundle
fffffff006ba2e30:container_failure
fffffff006ba2db4:mismatch
fffffff006ba2e54:enter_builtin_sandbox
fffffff006ba2f48:unable_to_set_builtin
fffffff006ba2f58:still_still_here
fffffff006ba2f40:unable_to_set_container
0xfffffff006ba2efc:extension_added
0xfffffff006ba25ac:common_exit_from_exec
fffffff006ba2604:|do the stack_chk
fffffff0076b4e20:ios_specific_uid_501
0xfffffff006ba3408:_returns_ios_specific_uid_501
fffffff006ba39d8:ustate_container_path_for_uid
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int csops(pid_t pid, uint32_t ops, void *useraddr, size_t usersize) {
// In this part. case we dont need this since csops is in libsystem_kernel.. .but....
return (syscall(169, pid, ops, useraddr, usersize));
}
int main (int argc, char **argv) {
// simple csops command-line driver.
if (argc < 3) { fprintf(stderr,"Usage: %s _op_ _pid_\n", argv[0]); exit(1);}
uint32_t op = atoi(argv[1]);
pid_t pid = atoi(argv[2]);
char buf[4096] = { 0 };
uint32_t bufsize = 4096;
int rc = csops (pid, op, buf, bufsize);
if ( rc == -1) { return 1; }
// if here then ok:
write (1, buf, bufsize);
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define CSOPS_NUM 169
int csops (pid_t pid, uint32_t ops, user_addr_t useraddr, user_size_t usersize)
{
int rc = syscall (CSOPS_NUM, pid, ops, useraddr, usersize);
return rc;
}
int main (int argc, char **argv) {
int pid = atoi(argv[1]);
int op = atoi (argv[2]);
char buf[4096];
int bufSize = 4096;
int rc = csops(pid, op, buf, bufSize);
if (rc < 0 ) // -1
{
perror("csops");
exit(1);
}
write(2, buf, bufSize);
}
#!/usr/bin/env dtrace -s
#pragma D option quiet
provider*::: // Using "*" allows for matching per-pid providers
{
// Convenience naming of probe variables
method = (string)&probefunc[1];
type = (string)&probefunc[0];
class = probemod;
name = probename;
printf("%s(%d) %s %s %s %s ==> %x %x %x\n",
execname, pid, method, type, name, class,
arg1, arg2, arg3);
}
#define kPropNameLength 32
typedef struct DeviceTreeNodeProperty {
char name[kPropNameLength]; // NUL terminated property name
uint32_t length; // Length (bytes) of folloing prop value
// unsigned long value[1]; // Variable length value of property
// Padded to a multiple of a longword?
} DeviceTreeNodeProperty;
typedef struct OpaqueDTEntry {
uint32_t nProperties; // Number of props[] elements (0 => end)
uint32_t nChildren; // Number of children[] elements
// DeviceTreeNodeProperty props[];// array size == nProperties
// DeviceTreeNode children[]; // array size == nChildren
} DeviceTreeNode;
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#define DEV_TREE_PROP_NAME_LEN 32
#define DEV_TREE_PROP_VALUE_LEN 8192
#define DEV_TREE_PROP_FLAGS_MASK (0xf0000000)
#define PAD32(x) (((x) + 3) & ~3) // Macro to make sure values get padded on 32-bit boundary
/**
* dtize.c - Another http://NewOSXBook.com/ source example
*
* A simple device tree dumper. Similar to what imagine (for img3) had, but works on new
* Img4s as well. Doesn't actually do any DER processing (hence quick)
*
* License: ABSE. Rest of the world - Use and abuse. The day of open iOS on generic ARM64
* architectures drawns (infinitessimally) closer..
*
*/
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
int indent = 0;
void getDTNode (int fd) {
int rc = 0;
char devTreePropName[DEV_TREE_PROP_NAME_LEN];
unsigned char devTreePropValue[DEV_TREE_PROP_VALUE_LEN];
uint32_t devTreePropLenAndFlags = 0;
uint32_t devTreePropFlags = 0;
uint32_t numProperties = 0;
uint32_t numChildren = 0 ;
rc = read (fd, &numProperties, sizeof(uint32_t));
rc = read (fd, &numChildren, sizeof(uint32_t));
printf("NUM PROPERTIES: %d, NUM CHILDREN: %d\n", numProperties, numChildren);
int p = 0;
while (p < numProperties ) {
rc = read (fd, devTreePropName, DEV_TREE_PROP_NAME_LEN);
rc = read (fd, &devTreePropLenAndFlags, sizeof(uint32_t));
devTreePropFlags = devTreePropLenAndFlags & DEV_TREE_PROP_FLAGS_MASK;
devTreePropLenAndFlags &= ~DEV_TREE_PROP_FLAGS_MASK;
if (devTreePropLenAndFlags > DEV_TREE_PROP_VALUE_LEN)
{
off_t pos = lseek (fd, 0, SEEK_CUR);
fprintf(stderr,"Error: Excessive device tree property len Pos %x (%d > %d)\n",
pos,
devTreePropLenAndFlags , DEV_TREE_PROP_VALUE_LEN);
exit(0);
}
rc = read (fd, devTreePropValue, PAD32(devTreePropLenAndFlags));
int i = 0;
while (i < indent) { printf("---"); i++;};
printf("%s (%d bytes): ", devTreePropName, devTreePropLenAndFlags);
static char allZeros[DEV_TREE_PROP_VALUE_LEN] = { 0 };
if (devTreePropLenAndFlags == 4) {
// Print as integer
printf("%d (*)\n", *(uint32_t *) devTreePropValue);
}
else if (memcmp(devTreePropValue, allZeros, devTreePropLenAndFlags) ==0) {
printf("(NULL)\n");
}
else { // safe string
int j = 0;
for (j = 0 ; j < devTreePropLenAndFlags; j++)
{
if (isprint(devTreePropValue[j])) putc(devTreePropValue[j], stdout);
else if ((j == devTreePropLenAndFlags - 1) && !devTreePropValue[j]) { /* skip terminating NULL */}
else printf("\\x%02x", devTreePropValue[j]);
}
printf("\n");
}
p++;
}
indent++;
int c = 0;
while (c< numChildren ) {
printf("Child %d:", c);
getDTNode(fd);
c++;
}
indent--;
} // end getDTNode;
int main (int argc, char **argv) {
if (!argv[1]) {
fprintf(stderr,"Usage: %s DeviceTree....im4p\n", argv[0]);
exit(1);
}
int fd = open (argv[1],O_RDONLY);
if (fd < 0){ perror (argv[1]); exit(2);}
// The IMG4p header is a DER header of 48 bytes:
//
// 0:d=0 hl=5 l=168296 cons: SEQUENCE
// 5:d=1 hl=2 l= 4 prim: IA5STRING :IM4P
// 11:d=1 hl=2 l= 4 prim: IA5STRING :dtre
// 17:d=1 hl=2 l= 29 prim: IA5STRING :EmbeddedDeviceTrees-3480.40.4
// @TODO: Handle DER. For now, just skip to offset 19, which is where EmbeddedDevice
// in IA5String starts
//
//00000000 30 83 02 91 68 16 04 49 4d 34 50 16 04 64 74 72 |0...h..IM4P..dtr|
//00000010 65 16 1d 45 6d 62 65 64 64 65 64 44 65 76 69 63 |e..EmbeddedDevic|
//00000020 65 54 72 65 65 73
//
char buf[256];
int rc = read (fd, buf, 19);
if (buf[17] != 0x16) { fprintf(stderr,"Error: Expected IA5STRING for EmbeddedDevice...\n"); exit(5); }
// Otherwise the length of the Embedded DeviceTree.... string is in buf[18]
int len = buf[18];
rc = read (fd,buf, len);
// At this offset we expect the last two bytes
// At offset 48 we expect an OCTET STRING (4)
// 48:d=1 hl=5 l=168248 prim
char dtBegins[] = { '\x04' , '\x83' };
rc = read (fd, buf, 2);
if (memcmp (buf, dtBegins,2)) {
fprintf(stderr,"Error: Expected OCTET STRING with 3 byte length at offet 48\n"); exit(3);
}
int totalLenLen = 3;
// Otherwise get total length, assuming 3 bytes
uint32_t totalLen = 0;
rc = read (fd, &totalLen, totalLenLen);
totalLen <<=8;
totalLen = ntohl(totalLen);
printf("Device Tree of %ld bytes\n", totalLen);
// @TODO: Sanity check for size (vs. fstat)
//
//
uint32_t numProperties = 0;
uint32_t numChildren = 0 ;
// Read two uint32_t values. One is 12, the other is 10. Then start the properties
uint32_t pos = 48 + 5 ;
// Now for the properties!
while (pos < totalLen) {
getDTNode(fd);
pos = lseek(fd, 0, SEEK_CUR);
} // end while
}
#!/usr/sbin/dtrace -s
#pragma D option flowindent
syscall::open:entry { self->tracing = 1; }
syscall::open:return { self->tracing = 0; exit(0); }
fbt::: /self->tracing/ { printf("%x %x %x", arg0, arg1,arg2); /* Dump arguments */ }
export SDK=iPhoneOS.sdk
export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
#XCODE_DEVELOPER_USR_PATH=/Developer # don't really need this..
gcc -DARM -arch arm64 -framework IOKit -framework CoreFoundation \
-F /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks -F /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/PrivateFrameworks -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/10.0/Symbols/usr/include -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib $*
#include <stdio.h>
#include <dispatch/dispatch.h>
#include <pthread.h>
int main (int arg,char **argv)
{
// Using pthread_self() inside a block will show you the thread it
// is being run in. The interested reader might want to dispatch
// this block several times, and note that the # of threads can
// change according to GCD's internal decisions..
void (^myblock1) (void) = ^ { printf(" Blocks are cool - \n");};
dispatch_queue_t q =
dispatch_queue_create("com.technologeeks.demoq", // Our name
DISPATCH_QUEUE_CONCURRENT); // DISPATCH_QUEUE_SERIAL or CONCURRENT
dispatch_group_t g = dispatch_group_create();
dispatch_group_async(g, q, myblock1);
dispatch_group_async(g, q, myblock1);
int rc= dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#ifdef __arm64__
//#include "mach/arm/thread_status.h"
// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported
// And I say, bullshit.
kern_return_t mach_vm_allocate
(
vm_map_t target,
mach_vm_address_t *address,
mach_vm_size_t size,
int flags
);
kern_return_t mach_vm_write
(
vm_map_t target_task,
mach_vm_address_t address,
vm_offset_t data,
mach_msg_type_number_t dataCnt
);
#else
#include <mach/mach_vm.h>
#endif
#define STACK_SIZE 65536
#define CODE_SIZE 128
// Due to popular request:
//
// Simple injector example (and basis of coreruption tool).
//
// If you've looked into research on injection techniques in OS X, you
// probably know about mach_inject. This tool, part of Dino Dai Zovi's
// excellent "Mac Hacker's Handbook" (a must read - kudos, DDZ) was
// created to inject code in PPC and i386. Since I couldn't find anything
// for x86_64 or ARM, I ended up writing my own tool.
// Since, this tool has exploded in functionality - with many other features,
// including scriptable debugging, fault injection, function hooking, code
// decryption, and what not - which comes in *really* handy on iOS.
//
// coreruption is still closed source, due its highly.. uhm.. useful
// nature. But I'm making this sample free, and I have fully annotated this.
// The rest of the stuff you need is in Chapters 11 and 12 MOXiI 1, with more
// to come in the 2nd Ed (..in time for iOS 9 :-)
//
// Go forth and spread your code :-)
//
// J ([email protected]) 02/05/2014
//
// v2: With ARM64 - 06/02/2015 NOTE - ONLY FOR **ARM64**, NOT ARM32!
// Get the full bundle at - http://NewOSXBook.com/files/injarm64.tar
// with sample dylib and with script to compile this neatly.
//
//**********************************************************************
// Note ARM code IS messy, and I left the addresses wide apart. That's
// intentional. Basic ARM64 assembly will enable you to tidy this up and
// make the code more compact.
//
// This is *not* meant to be neat - I'm just preparing this for TG's
// upcoming OS X/iOS RE course (http://technologeeks.com/OSXRE) and thought
// this would be interesting to share. See you all in MOXiI 2nd Ed!
//**********************************************************************
// Update (7/16/2019):
// You'll need to change pthread_set_self to from ..from_mach_thread,
// which is required as a workaround for behavior change in Mojave (10.14) and later iOS 12
// q.v. https://knight.sc/malware/2019/03/15/code-injection-on-macos.html
// This sample code calls pthread_set_self to promote the injected thread
// to a pthread first - otherwise dlopen and many other calls (which rely
// on pthread_self()) will crash.
// It then calls dlopen() to load the library specified - which will trigger
// the library's constructor (q.e.d as far as code injection is concerned)
// and sleep for a long time. You can of course replace the sleep with
// another function, such as pthread_exit(), etc.
//
// (For the constructor, use:
//
// static void whicheverfunc() __attribute__((constructor));
//
// in the library you inject)
//
// Note that the functions are shown here as "_PTHRDSS", "DLOPEN__" and "SLEEP___".
// Reason being, that the above are merely placeholders which will be patched with
// the runtime addresses when code is actually injected.
//
char injectedCode[] =
#ifdef X86_64
//"\xcc" // int3
"\x90" // nop..
"\x55" // pushq %rbp
"\x48\x89\xe5" // movq %rsp, %rbp
"\x48\x83\xec\x20" // subq $32, %rsp
"\x89\x7d\xfc" // movl %edi, -4(%rbp)
"\x48\x89\x75\xf0" // movq %rsi, -16(%rbp)
"\xb0\x00" // movb $0, %al
// call pthread_set_self
"\x48\xbf\x00\x00\x00\x00\x00\x00\x00\x00" // movabsq $0, %rdi
"\x48\xb8" "_PTHRDSS" // movabsq $140735540045793, %rax
"\xff\xd0" // callq *%rax
"\x48\xbe\x00\x00\x00\x00\x00\x00\x00\x00" // movabsq $0, %rsi
"\x48\x8d\x3d\x2c\x00\x00\x00" // leaq 44(%rip), %rdi
// DLOpen...
"\x48\xb8" "DLOPEN__" // movabsq $140735516395848, %rax
"\x48\xbe\x00\x00\x00\x00\x00\x00\x00\x00" // movabsq $0, %rsi
"\xff\xd0" // callq *%rax
// Sleep(1000000)...
"\x48\xbf\x00\xe4\x0b\x54\x02\x00\x00\x00" // movabsq $10000000000, %rdi
"\x48\xb8" "SLEEP___" // movabsq $140735516630165, %rax
"\xff\xd0" // callq *%rax
// plenty of space for a full path name here
"LIBLIBLIBLIB" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
;
#else
// That's the ARM64 "shellcode"
"\x08\x03\x00\x58" // LDR X8, #3 ; load PTHREADSS
"\x00\x01\x3f\xd6" // BLR X8 ; do pthread_set_self
"\x00\x01\x00\x10" // ADR X0, #32
"\x00\x40\x01\x91" // ADD x0, x0, #0x50 ; X0 => "LIBLIBLIB...";
"\x08\x03\x00\x58" // LDR X8, #3 ; load DLOPEN
"\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 = 0;
"\x29\x01\x00\x91" // ADD x9, x9, 0 - I left this as a nop
// dlopen("LIBLIBLIB", 0);
"\x00\x01\x3f\xd6" // BLR X8 ; do dlopen()
"\xa8\x00\x00\x58" // LDR X8, #12 ; load PTHREADEXT
"\x00\x00\x80\xd2" // MOVZ X0, 0 ; X1 = 0;
"\x00\x01\x3f\xd6" // BLR X8 ; do pthread_exit
"\x00\x00\x20\xd4" // BRK X0 ; // useful if you need a break :)
"XXXX"
"PTHRDEXT" // <-
"AAAA"
"BCDEFGHI"
"JKLMNOPR"
"STUVWXYZ"
"!!!!!!!!"
"_PTHRDSS" // <-
"PTHRDEXT" //
"DLOPEN__" // <-
"LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB"
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" ;
#endif
int inject(pid_t pid, const char *lib) {
task_t remoteTask;
struct stat buf;
/**
* First, check we have the library. Otherwise, we won't be able to inject..
*/
int rc = stat (lib, &buf);
if (rc != 0)
{
fprintf (stderr, "Unable to open library file %s (%s) - Cannot inject\n", lib,strerror (errno));
//return (-9);
}
mach_error_t kr = 0;
/**
* Second - the critical part - we need task_for_pid in order to get the task port of the target
* pid. This is our do-or-die: If we get the port, we can do *ANYTHING* we want. If we don't, we're
* #$%#$%.
*
* In iOS, this will require the task_for_pid-allow entitlement. In OS X, this will require getting past
* taskgated, but root access suffices for that.
*
*/
kr = task_for_pid(mach_task_self(), pid, &remoteTask);
if (kr != KERN_SUCCESS) {
fprintf (stderr, "Unable to call task_for_pid on pid %d: %s. Cannot continue!\n",pid, mach_error_string(kr));
return (-1);
}
/**
* From here on, it's pretty much straightforward -
* Allocate stack and code. We don't really care *where* they get allocated. Just that they get allocated.
* So, first, stack:
*/
mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
mach_vm_address_t remoteCode64 = (vm_address_t) NULL;
kr = mach_vm_allocate( remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
if (kr != KERN_SUCCESS)
{
fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
return (-2);
}
else
{
fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64);
}
/**
* Then we allocate the memory for the thread
*/
remoteCode64 = (vm_address_t) NULL;
kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );
if (kr != KERN_SUCCESS)
{
fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
return (-2);
}
/**
* Patch code before injecting: That is, insert correct function addresses (and lib name) into placeholders
*
* Since we use the same shared library cache as our victim, meaning we can use memory addresses from
* OUR address space when we inject..
*/
int i = 0;
char *possiblePatchLocation = (injectedCode );
for (i = 0 ; i < 0x100; i++)
{
// Patching is crude, but works.
//
extern void *_pthread_set_self;
possiblePatchLocation++;
uint64_t addrOfPthreadSetSelf = dlsym ( RTLD_DEFAULT, "_pthread_set_self"); //(uint64_t) _pthread_set_self;
uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, "pthread_exit"); //(uint64_t) _pthread_set_self;
uint64_t addrOfDlopen = (uint64_t) dlopen;
uint64_t addrOfSleep = (uint64_t) sleep; // pthread_exit;
if (memcmp (possiblePatchLocation, "PTHRDEXT", 8) == 0)
{
memcpy(possiblePatchLocation, &addrOfPthreadExit,8);
printf ("Pthread exit @%llx, %llx\n", addrOfPthreadExit, pthread_exit);
}
if (memcmp (possiblePatchLocation, "_PTHRDSS", 8) == 0)
{
memcpy(possiblePatchLocation, &addrOfPthreadSetSelf,8);
printf ("Pthread set self @%llx\n", addrOfPthreadSetSelf);
}
if (memcmp(possiblePatchLocation, "DLOPEN__", 6) == 0)
{
printf ("DLOpen @%llx\n", addrOfDlopen);
memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t));
}
if (memcmp(possiblePatchLocation, "SLEEP___", 6) == 0)
{
printf ("Sleep @%llx\n", addrOfSleep);
memcpy(possiblePatchLocation, &addrOfSleep, sizeof(uint64_t));
}
if (memcmp(possiblePatchLocation, "LIBLIBLIB", 9) == 0)
{
strcpy(possiblePatchLocation, lib );
}
}
/**
* Write the (now patched) code
*/
kr = mach_vm_write(remoteTask, // Task port
remoteCode64, // Virtual Address (Destination)
(vm_address_t) injectedCode, // Source
0xa9); // Length of the source
if (kr != KERN_SUCCESS)
{
fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
return (-3);
}
/*
* Mark code as executable - This also requires a workaround on iOS, btw.
*/
kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
/*
* Mark stack as writable - not really necessary
*/
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
if (kr != KERN_SUCCESS)
{
fprintf(stderr,"Unable to set memory permissions for remote thread: Error %s\n", mach_error_string(kr));
return (-4);
}
/**
*
* Create thread - This is obviously hardware specific.
*
*/
#ifdef X86_64
x86_thread_state64_t remoteThreadState64;
#else
// Using unified thread state for backporting to ARMv7, if anyone's interested..
struct arm_unified_thread_state remoteThreadState64;
#endif
thread_act_t remoteThread;
memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) );
remoteStack64 += (STACK_SIZE / 2); // this is the real stack
//remoteStack64 -= 8; // need alignment of 16
const char* p = (const char*) remoteCode64;
#ifdef X86_64
remoteThreadState64.__rip = (u_int64_t) (vm_address_t) remoteCode64;
// set remote Stack Pointer
remoteThreadState64.__rsp = (u_int64_t) remoteStack64;
remoteThreadState64.__rbp = (u_int64_t) remoteStack64;
#else
// Note the similarity - all we change are a couple of regs.
remoteThreadState64.ash.flavor = ARM_THREAD_STATE64;
remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64;
remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;
// __uint64_t __x[29]; /* General purpose registers x0-x28 */
#endif
printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p );
/*
* create thread and launch it in one go
*/
#ifdef X86_64
kr = thread_create_running( remoteTask, x86_THREAD_STATE64,
(thread_state_t) &remoteThreadState64, x86_THREAD_STATE64_COUNT, &remoteThread );
#else // __arm64__
kr = thread_create_running( remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64,
(thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );
#endif
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr));
return (-3); }
return (0);
} // end injection code
int main(int argc, const char * argv[])
{
if (argc < 3)
{
fprintf (stderr, "Usage: %s _pid_ _action_\n", argv[0]);
fprintf (stderr, " _action_: path to a dylib on disk\n");
exit(0);
}
pid_t pid = atoi(argv[1]);
const char *action = argv[2];
struct stat buf;
int rc = stat (action, &buf);
if (rc == 0) inject(pid,action);
else
{
fprintf(stderr,"Dylib not found\n");
}
}
#if 0
tatic void con() __attribute__((constructor));
void con() {
printf("I'm a constructor\n");
}
#endif
/*
* Copyright (c) 2000-2013 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/**
* J's addition:
*
* No claims made as per (c). This adds zero logic to iokit, and just improves
* the already existing ncurses support (limited to bold class names) by adding
* some real colors! (well, as real as xterm colors can get...) and a couple of
* other features I find myself using a lot (see below)
*
*
* If my readers at 17.x.x.x would like to use any of these simple mods, power to them.
* No acknowledgment, etc. needed
*
* To compile:
*
* - Get IOKitUser from Apple's opensource site
* - mkdir IOKit and move all the headers (esp. IOKitLibPrivate.h) to it.
* - type the following, replacing _path_to_IOKit_User_ with where you made
* that IOKit directory
*
* gcc ioreg.c -o ioreg -framework IOKit -I_path_to_IOKitUser_ -framework CoreFoundation -lcurses
*
* Or, if you trust me, get it from the ios Binary Pack
*
* (http://NewOSXBook.com/tools/iOSBinaries.html)
*
* To get the original (colorless, meh) ioreg(8) back, compile with -DNO_J
*
*
* New options: (as of 03/04/2016)
* -------------------------------
*
* - Colors.
* - reduce verbiage ("r/m/a" instead of "registered/matched/active")
* - Busy time in uSec, not mSec.
* - Tests IOUserClient Reachability (brute force IOServiceOpen)
* Useful for these cases where you find yourself in a Sandbox and
* don't want to code from scratch
*
*/
#include <CoreFoundation/CoreFoundation.h> // (CFDictionary, ...)
#include <IOKit/IOCFSerialize.h> // (IOCFSerialize, ...)
#include <IOKit/IOKitLib.h> // (IOMasterPort, ...)
#include <IOKit/IOKitLibPrivate.h> // (IOServiceGetState, ...)
#include <sys/ioctl.h> // (TIOCGWINSZ, ...)
#include <term.h> // (tputs, ...)
#include <unistd.h> // (getopt, ...)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define assertion(e, message) ((void) (__builtin_expect(!(e), 0) ? fprintf(stderr, "ioreg: error: %s.\n", message), exit(1) : 0))
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#ifndef NO_J
#define BOLD "\033[1;1m"
#define RED "\033[0;31m"
#define M0 "\e[0;30m"
#define CYAN "\e[0;36m"
#define M1 "\e[0;31m"
#define GREY "\e[0;37m"
#define M8 "\e[0;38m"
#define M9 "\e[0;39m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[0;33m"
#define BLUE "\e[0;34m"
#define PINK "\e[0;35m"
#define NORMAL "\e[0;0m"
#endif
struct options
{
UInt32 archive:1; // (-a option)
UInt32 bold:1; // (-b option)
UInt32 format:1; // (-f option)
UInt32 hex:1; // (-x option)
UInt32 inheritance:1; // (-i option)
UInt32 list:1; // (-l option)
UInt32 root:1; // (-r option)
UInt32 tree:1; // (-t option)
char * class; // (-c option)
UInt32 depth; // (-d option)
char * key; // (-k option)
char * name; // (-n option)
char * plane; // (-p option)
UInt32 width; // (-w option)
#ifndef NO_J
UInt32 open:1; // (-o option)
#endif
};
char *ver[] = { "@(#) PROGRAM: ioreg\tPROJECT: IOKitTools-89.1.1\n",
"@(#) Compiled (with slight mods) by Jonathan Levin, http://www.NewOSXBook.com/" };
struct context
{
io_registry_entry_t service;
UInt32 serviceDepth;
UInt64 stackOfBits;
struct options options;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void boldinit();
static void boldon();
static void boldoff();
static void printinit(int width);
static void print(const char * format, ...);
static void println(const char * format, ...);
static void cfshowinit(Boolean hex);
static void cfshow(CFTypeRef object);
static void cfarrayshow(CFArrayRef object);
static void cfbooleanshow(CFBooleanRef object);
static void cfdatashow(CFDataRef object);
static void cfdictionaryshow(CFDictionaryRef object);
static void cfnumbershow(CFNumberRef object);
static void cfsetshow(CFSetRef object);
static void cfstringshow(CFStringRef object);
static CFStringRef createInheritanceStringForIORegistryClassName(CFStringRef name);
static void printProp(CFStringRef key, CFTypeRef value, struct context * context);
static void printPhysAddr(CFTypeRef value, struct context * context);
static void printSlotNames(CFTypeRef value, struct context * context);
static void printPCIRanges(CFTypeRef value, struct context * context);
static void printInterruptMap(CFTypeRef value, struct context * context);
static void printInterrupts(CFTypeRef value, struct context * context);
static void printInterruptParent( CFTypeRef value, struct context * context );
static void printData(CFTypeRef value, struct context * context);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static CFMutableDictionaryRef archive( io_registry_entry_t service,
struct options options ) CF_RETURNS_RETAINED;
static CFMutableDictionaryRef archive_scan( io_registry_entry_t service,
UInt32 serviceDepth,
struct options options ) CF_RETURNS_RETAINED;
static CFMutableArrayRef archive_search( io_registry_entry_t service,
UInt32 serviceHasMatchedDepth,
UInt32 serviceDepth,
io_registry_entry_t stackOfObjects[],
struct options options ) CF_RETURNS_RETAINED;
static Boolean compare( io_registry_entry_t service,
struct options options );
static void indent( Boolean isNode,
UInt32 serviceDepth,
UInt64 stackOfBits );
static void scan( io_registry_entry_t service,
Boolean serviceHasMoreSiblings,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options );
static void search( io_registry_entry_t service,
UInt32 serviceHasMatchedDepth,
UInt32 serviceDepth,
io_registry_entry_t stackOfObjects[],
struct options options );
static void show( io_registry_entry_t service,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options );
static void showitem( const void * key,
const void * value,
void * parameter );
static void usage();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int
main(int argc, char ** argv)
{
int argument = 0;
CFWriteStreamRef file = 0; // (needs release)
CFTypeRef object = 0; // (needs release)
struct options options;
CFURLRef path = 0; // (needs release)
io_registry_entry_t service = 0; // (needs release)
io_registry_entry_t stackOfObjects[64];
Boolean success = FALSE;
struct winsize winsize;
// Initialize our minimal state.
options.archive = FALSE;
options.bold = FALSE;
options.format = FALSE;
options.hex = FALSE;
options.inheritance = FALSE;
options.list = FALSE;
options.root = FALSE;
options.tree = FALSE;
options.class = 0;
options.depth = 0;
options.key = 0;
options.name = 0;
options.plane = kIOServicePlane;
options.root = 0;
options.width = 0;
// Obtain the screen width.
if (isatty(fileno(stdout)))
{
if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0)
{
options.width = winsize.ws_col;
}
}
#ifndef NO_J
// Get defaults from IOREG_DEFS environment variable
char *defs = getenv("IOREG_DEFS");
if (defs)
{
}
#endif
// Obtain the command-line arguments.
#ifndef NO_J
// Would have liked to have the opts in a #define here, but I don't want
// my changes to modify the original Apple source in any way.
while ( (argument = getopt(argc, argv, ":abc:d:fik:ln:p:orsStw:x")) != -1 )
#else
while ( (argument = getopt(argc, argv, ":abc:d:fik:ln:p:rsStw:x")) != -1 )
#endif
{
switch (argument)
{
case 'a':
options.archive = TRUE;
break;
case 'b':
options.bold = TRUE;
break;
case 'c':
options.class = optarg;
break;
case 'd':
options.depth = atoi(optarg);
break;
case 'f':
options.format = TRUE;
break;
case 'i':
options.inheritance = TRUE;
break;
case 'k':
options.key = optarg;
break;
case 'l':
options.list = TRUE;
break;
case 'n':
options.name = optarg;
break;
case 'p':
options.plane = optarg;
break;
case 'r':
options.root = TRUE;
break;
case 's':
break;
case 'S':
break;
#ifndef NO_J
case 'o':
options.open = TRUE;
break;
#endif
case 't':
options.tree = TRUE;
break;
case 'w':
options.width = atoi(optarg);
break;
case 'x':
options.hex = TRUE;
break;
default:
usage();
break;
}
}
// Initialize text output functions.
cfshowinit(options.hex);
printinit(options.width);
if (options.bold) boldinit();
// Obtain the I/O Kit root service.
service = IORegistryGetRootEntry(kIOMasterPortDefault);
assertion(service, "can't obtain I/O Kit's root service");
// Traverse over all the I/O Kit services.
if (options.archive)
{
if (options.root)
{
object = archive_search( /* service */ service,
/* serviceHasMatchedDepth */ 0,
/* serviceDepth */ 0,
/* stackOfObjects */ stackOfObjects,
/* options */ options );
}
else
{
object = archive_scan( /* service */ service,
/* serviceDepth */ 0,
/* options */ options );
}
if (object)
{
path = CFURLCreateWithFileSystemPath( /* allocator */ kCFAllocatorDefault,
/* filePath */ CFSTR("/dev/stdout"),
/* pathStyle */ kCFURLPOSIXPathStyle,
/* isDirectory */ FALSE );
assertion(path != NULL, "can't create path");
file = CFWriteStreamCreateWithFile(kCFAllocatorDefault, path);
assertion(file != NULL, "can't create file");
success = CFWriteStreamOpen(file);
assertion(success, "can't open file");
CFPropertyListWrite( /* propertyList */ object,
/* stream */ file,
/* format */ kCFPropertyListXMLFormat_v1_0,
/* options */ 0,
/* error */ NULL );
CFWriteStreamClose(file);
CFRelease(file);
CFRelease(path);
CFRelease(object);
}
}
else
{
if (options.root)
{
search( /* service */ service,
/* serviceHasMatchedDepth */ 0,
/* serviceDepth */ 0,
/* stackOfObjects */ stackOfObjects,
/* options */ options );
}
else
{
scan( /* service */ service,
/* serviceHasMoreSiblings */ FALSE,
/* serviceDepth */ 0,
/* stackOfBits */ 0,
/* options */ options );
}
}
// Release resources.
IOObjectRelease(service);
// Quit.
exit(0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static CFMutableDictionaryRef
archive( io_registry_entry_t service,
struct options options )
{
io_name_t class; // (don't release)
uint32_t count = 0;
CFMutableDictionaryRef dictionary = 0; // (needs release)
uint64_t identifier = 0;
io_name_t location; // (don't release)
io_name_t name; // (don't release)
CFTypeRef object = 0; // (needs release)
uint64_t state = 0;
kern_return_t status = KERN_SUCCESS;
uint64_t time = 0;
// Determine whether the service is a match.
if (options.list || compare(service, options))
{
// Obtain the service's properties.
status = IORegistryEntryCreateCFProperties( service,
&dictionary,
kCFAllocatorDefault,
kNilOptions );
assertion(status == KERN_SUCCESS, "can't obtain properties");
}
else
{
dictionary = CFDictionaryCreateMutable( kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
assertion(dictionary != NULL, "can't create dictionary");
}
// Obtain the name of the service.
status = IORegistryEntryGetNameInPlane(service, options.plane, name);
assertion(status == KERN_SUCCESS, "can't obtain name");
object = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
assertion(object != NULL, "can't create name");
CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryName"), object);
CFRelease(object);
// Obtain the location of the service.
status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
if (status == KERN_SUCCESS)
{
object = CFStringCreateWithCString(kCFAllocatorDefault, location, kCFStringEncodingUTF8);
assertion(object != NULL, "can't create location");
CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryLocation"), object);
CFRelease(object);
}
// Obtain the ID of the service.
status = IORegistryEntryGetRegistryEntryID(service, &identifier);
assertion(status == KERN_SUCCESS, "can't obtain identifier");
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &identifier);
assertion(object != NULL, "can't create identifier");
CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryID"), object);
CFRelease(object);
// Obtain the class of the service.
status = IOObjectGetClass(service, class);
assertion(status == KERN_SUCCESS, "can't obtain class");
object = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
assertion(object != NULL, "can't create class");
CFDictionarySetValue(dictionary, CFSTR("IOObjectClass"), object);
CFRelease(object);
// Obtain the retain count of the service.
count = IOObjectGetKernelRetainCount(service);
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &count);
assertion(object != NULL, "can't create retain count");
CFDictionarySetValue(dictionary, CFSTR("IOObjectRetainCount"), object);
CFRelease(object);
// Obtain the busy state of the service (for IOService objects).
if (IOObjectConformsTo(service, "IOService"))
{
status = IOServiceGetBusyStateAndTime(service, &state, &count, &time);
assertion(status == KERN_SUCCESS, "can't obtain state");
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &state);
assertion(object != NULL, "can't create state");
CFDictionarySetValue(dictionary, CFSTR("IOServiceState"), object);
CFRelease(object);
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &count);
assertion(object != NULL, "can't create busy state");
CFDictionarySetValue(dictionary, CFSTR("IOServiceBusyState"), object);
CFRelease(object);
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &time);
assertion(object != NULL, "can't create busy time");
CFDictionarySetValue(dictionary, CFSTR("IOServiceBusyTime"), object);
CFRelease(object);
}
return dictionary;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static CFMutableDictionaryRef
archive_scan( io_registry_entry_t service,
UInt32 serviceDepth,
struct options options )
{
CFMutableArrayRef array = 0; // (needs release)
io_registry_entry_t child = 0; // (needs release)
io_registry_entry_t childUpNext = 0; // (don't release)
io_iterator_t children = 0; // (needs release)
CFMutableDictionaryRef dictionary = 0; // (needs release)
CFTypeRef object = 0; // (needs release)
kern_return_t status = KERN_SUCCESS;
// Obtain the service's children.
status = IORegistryEntryGetChildIterator(service, options.plane, &children);
if (status == KERN_SUCCESS)
{
childUpNext = IOIteratorNext(children);
// Obtain the relevant service information.
dictionary = archive(service, options);
// Traverse over the children of this service.
if (options.depth == 0 || options.depth > serviceDepth + 1)
{
if (childUpNext)
{
array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
assertion(array != NULL, "can't create array");
while (childUpNext)
{
child = childUpNext;
childUpNext = IOIteratorNext(children);
object = archive_scan( /* service */ child,
/* serviceDepth */ serviceDepth + 1,
/* options */ options );
assertion(object != NULL, "can't obtain child");
CFArrayAppendValue(array, object);
CFRelease(object);
IOObjectRelease(child);
}
CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryChildren"), array);
CFRelease(array);
}
}
IOObjectRelease(children);
}
return dictionary;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static CFMutableArrayRef
archive_search( io_registry_entry_t service,
UInt32 serviceHasMatchedDepth,
UInt32 serviceDepth,
io_registry_entry_t stackOfObjects[],
struct options options )
{
CFMutableArrayRef array = 0; // (needs release)
CFMutableArrayRef array2 = 0; // (needs release)
io_registry_entry_t child = 0; // (needs release)
io_registry_entry_t childUpNext = 0; // (don't release)
io_iterator_t children = 0; // (needs release)
CFMutableDictionaryRef dictionary = 0; // (needs release)
CFMutableDictionaryRef dictionary2 = 0; // (needs release)
UInt32 index = 0;
kern_return_t status = KERN_SUCCESS;
// Determine whether the service is a match.
if (serviceHasMatchedDepth < serviceDepth + 1 && compare(service, options))
{
if (options.depth)
{
serviceHasMatchedDepth = serviceDepth + options.depth;
}
else
{
serviceHasMatchedDepth = UINT32_MAX;
}
if (options.tree)
{
if (options.depth) options.depth += serviceDepth;
dictionary = archive_scan( /* service */ service,
/* serviceDepth */ serviceDepth,
/* options */ options );
if (options.depth) options.depth -= serviceDepth;
for (index = serviceDepth; index > 0; index--)
{
dictionary2 = archive(stackOfObjects[index - 1], options);
assertion(dictionary2 != NULL, "can't obtain parent");
CFDictionarySetValue(dictionary2, CFSTR("IORegistryEntryChildren"), dictionary);
CFRelease(dictionary);
dictionary = dictionary2;
}
}
else
{
dictionary = archive_scan( /* service */ service,
/* serviceDepth */ 0,
/* options */ options );
}
array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
assertion(array != NULL, "can't create array");
CFArrayAppendValue(array, dictionary);
CFRelease(dictionary);
}
// Save service into stackOfObjects for this depth.
stackOfObjects[serviceDepth] = service;
// Obtain the service's children.
status = IORegistryEntryGetChildIterator(service, options.plane, &children);
if (status == KERN_SUCCESS)
{
childUpNext = IOIteratorNext(children);
// Traverse over the children of this service.
while (childUpNext)
{
child = childUpNext;
childUpNext = IOIteratorNext(children);
array2 = archive_search( /* service */ child,
/* serviceHasMatchedDepth */ serviceHasMatchedDepth,
/* serviceDepth */ serviceDepth + 1,
/* stackOfObjects */ stackOfObjects,
/* options */ options );
if (array2)
{
if (array)
{
CFArrayAppendArray(array, array2, CFRangeMake(0, CFArrayGetCount(array2)));
CFRelease(array2);
}
else
{
array = array2;
}
}
IOObjectRelease(child);
}
IOObjectRelease(children);
}
return array;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static Boolean
compare( io_registry_entry_t service,
struct options options )
{
CFStringRef key = 0; // (needs release)
io_name_t location; // (don't release)
Boolean match = FALSE;
io_name_t name; // (don't release)
kern_return_t status = KERN_SUCCESS;
CFTypeRef value = 0; // (needs release)
// Determine whether the class of the service is a match.
if (options.class)
{
if (IOObjectConformsTo(service, options.class) == FALSE)
{
return FALSE;
}
match = TRUE;
}
// Determine whether the key of the service is a match.
if (options.key)
{
key = CFStringCreateWithCString( kCFAllocatorDefault,
options.key,
kCFStringEncodingUTF8 );
assertion(key != NULL, "can't create key");
value = IORegistryEntryCreateCFProperty( service,
key,
kCFAllocatorDefault,
kNilOptions );
CFRelease(key);
if (value == NULL)
{
return FALSE;
}
CFRelease(value);
match = TRUE;
}
// Determine whether the name of the service is a match.
if (options.name)
{
// Obtain the name of the service.
status = IORegistryEntryGetNameInPlane(service, options.plane, name);
assertion(status == KERN_SUCCESS, "can't obtain name");
if (strchr(options.name, '@'))
{
strlcat(name, "@", sizeof(name));
// Obtain the location of the service.
status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
if (status == KERN_SUCCESS) strlcat(name, location, sizeof(name));
}
if (strcmp(options.name, name))
{
return FALSE;
}
match = TRUE;
}
return match;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
scan( io_registry_entry_t service,
Boolean serviceHasMoreSiblings,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options )
{
io_registry_entry_t child = 0; // (needs release)
io_registry_entry_t childUpNext = 0; // (don't release)
io_iterator_t children = 0; // (needs release)
kern_return_t status = KERN_SUCCESS;
// Obtain the service's children.
status = IORegistryEntryGetChildIterator(service, options.plane, &children);
if (status == KERN_SUCCESS)
{
childUpNext = IOIteratorNext(children);
// Save has-more-siblings state into stackOfBits for this depth.
if (serviceHasMoreSiblings)
stackOfBits |= (1 << serviceDepth);
else
stackOfBits &= ~(1 << serviceDepth);
// Save has-children state into stackOfBits for this depth.
if (options.depth == 0 || options.depth > serviceDepth + 1)
{
if (childUpNext)
stackOfBits |= (2 << serviceDepth);
else
stackOfBits &= ~(2 << serviceDepth);
}
// Print out the relevant service information.
show(service, serviceDepth, stackOfBits, options);
// Traverse over the children of this service.
if (options.depth == 0 || options.depth > serviceDepth + 1)
{
while (childUpNext)
{
child = childUpNext;
childUpNext = IOIteratorNext(children);
scan( /* service */ child,
/* serviceHasMoreSiblings */ (childUpNext) ? TRUE : FALSE,
/* serviceDepth */ serviceDepth + 1,
/* stackOfBits */ stackOfBits,
/* options */ options );
IOObjectRelease(child);
}
}
IOObjectRelease(children);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
search( io_registry_entry_t service,
UInt32 serviceHasMatchedDepth,
UInt32 serviceDepth,
io_registry_entry_t stackOfObjects[],
struct options options )
{
io_registry_entry_t child = 0; // (needs release)
io_registry_entry_t childUpNext = 0; // (don't release)
io_iterator_t children = 0; // (needs release)
UInt32 index = 0;
kern_return_t status = KERN_SUCCESS;
// Determine whether the service is a match.
if (serviceHasMatchedDepth < serviceDepth + 1 && compare(service, options))
{
if (options.depth)
{
serviceHasMatchedDepth = serviceDepth + options.depth;
}
else
{
serviceHasMatchedDepth = UINT32_MAX;
}
if (options.tree)
{
for (index = 0; index < serviceDepth; index++)
{
show(stackOfObjects[index], index, (2 << index), options);
}
if (options.depth) options.depth += serviceDepth;
scan( /* service */ service,
/* serviceHasMoreSiblings */ FALSE,
/* serviceDepth */ serviceDepth,
/* stackOfBits */ 0,
/* options */ options );
if (options.depth) options.depth -= serviceDepth;
}
else
{
scan( /* service */ service,
/* serviceHasMoreSiblings */ FALSE,
/* serviceDepth */ 0,
/* stackOfBits */ 0,
/* options */ options );
}
println("");
}
// Save service into stackOfObjects for this depth.
stackOfObjects[serviceDepth] = service;
// Obtain the service's children.
status = IORegistryEntryGetChildIterator(service, options.plane, &children);
if (status == KERN_SUCCESS)
{
childUpNext = IOIteratorNext(children);
// Traverse over the children of this service.
while (childUpNext)
{
child = childUpNext;
childUpNext = IOIteratorNext(children);
search( /* service */ child,
/* serviceHasMatchedDepth */ serviceHasMatchedDepth,
/* serviceDepth */ serviceDepth + 1,
/* stackOfObjects */ stackOfObjects,
/* options */ options );
IOObjectRelease(child);
}
IOObjectRelease(children);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
show( io_registry_entry_t service,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options )
{
io_name_t class; // (don't release)
struct context context = { service, serviceDepth, stackOfBits, options };
uint32_t integer = 0;
uint64_t state = 0;
uint64_t accumulated_busy_time;
io_name_t location; // (don't release)
io_name_t name; // (don't release)
CFMutableDictionaryRef properties = 0; // (needs release)
kern_return_t status = KERN_SUCCESS;
// Print out the name of the service.
status = IORegistryEntryGetNameInPlane(service, options.plane, name);
assertion(status == KERN_SUCCESS, "can't obtain name");
indent(TRUE, serviceDepth, stackOfBits);
#ifndef NO_J
if (options.bold) {
boldon();
}
printf ("%s", CYAN);
#else
if (options.bold) boldon();
#endif
print("%s", name);
#ifndef NO_J
printf ("%s",NORMAL);
if (options.bold) {
boldoff();
}
#else
if (options.bold) boldoff();
#endif
// Print out the location of the service.
status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
if (status == KERN_SUCCESS) print("@%s", location);
// Print out the class of the service.
#ifndef NO_J
// Dont need "<class" every single time!
printf(" <");
#else
print(" <class ");
#endif
if (options.inheritance)
{
CFStringRef classCFStr;
CFStringRef ancestryCFStr;
char * aCStr;
classCFStr = IOObjectCopyClass (service);
ancestryCFStr = createInheritanceStringForIORegistryClassName (classCFStr);
aCStr = (char *) CFStringGetCStringPtr (ancestryCFStr, kCFStringEncodingMacRoman);
if (NULL != aCStr)
{
print(aCStr);
}
CFRelease (classCFStr);
CFRelease (ancestryCFStr);
}
else
{
status = IOObjectGetClass(service, class);
assertion(status == KERN_SUCCESS, "can't obtain class");
#ifndef NO_J
if (strcmp(class,name))
#endif
print("%s ", class);
}
// Prepare to print out the service's useful debug information.
uint64_t entryID;
status = IORegistryEntryGetRegistryEntryID(service, &entryID);
if (status == KERN_SUCCESS)
{
print("id 0x%llx", entryID);
}
// Print out the busy state of the service (for IOService objects).
if (IOObjectConformsTo(service, "IOService"))
{
status = IOServiceGetBusyStateAndTime(service, &state, &integer, &accumulated_busy_time);
assertion(status == KERN_SUCCESS, "can't obtain state");
#ifndef NO_J
print( ", %sr/%sm/%sa",
#else
print( ", %sregistered, %smatched, %sactive",
#endif
state & kIOServiceRegisteredState ? "" : "!",
state & kIOServiceMatchedState ? "" : "!",
state & kIOServiceInactiveState ? "in" : "" );
#ifndef NO_J
if (options.open && !strstr(name, "UserClient"))
{
// Attempt a brute force IOServiceOpen here. Note a better option would have
// been to first figure out if there's an IOUserClient here, at all - by enumerating
// class instances - but let's keep it simple..
CFMutableDictionaryRef matchingDictionary = IOServiceMatching(name);
io_iterator_t iterator;
kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);
io_service_t device = IOIteratorNext(iterator);
io_connect_t conn = MACH_PORT_NULL;
result = IOServiceOpen (device, mach_task_self(), 0, &conn);
IOObjectRelease(iterator);
if (conn == MACH_PORT_NULL) {
// Just don't say anything.
//printf (",!reachable");
}
else printf (",%sreachable%s", BOLD, NORMAL);
}
#endif
print(", busy %ld",
(unsigned long)integer);
if (accumulated_busy_time)
{
#ifndef NO_J
// Why not do Microseconds, guys? Most delay times are < 1ms
print(" (%lld us)",
accumulated_busy_time / kMicrosecondScale);
#else
print(" (%lld ms)",
accumulated_busy_time / kMillisecondScale);
#endif
}
}
// Print out the retain count of the service.
integer = IOObjectGetKernelRetainCount(service);
print(", retain %ld", (unsigned long)integer);
println(">");
// Determine whether the service is a match.
if (options.list || compare(service, options))
{
indent(FALSE, serviceDepth, stackOfBits);
println("{");
// Obtain the service's properties.
status = IORegistryEntryCreateCFProperties( service,
&properties,
kCFAllocatorDefault,
kNilOptions );
assertion(status == KERN_SUCCESS, "can't obtain properties");
// Print out the service's properties.
CFDictionaryApplyFunction(properties, showitem, &context);
indent(FALSE, serviceDepth, stackOfBits);
println("}");
indent(FALSE, serviceDepth, stackOfBits);
println("");
// Release resources.
CFRelease(properties);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
showitem(const void * key, const void * value, void * parameter)
{
struct context * context = parameter; // (don't release)
// Print out one of the service's properties.
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" ");
cfshow(key);
print(" = ");
#ifndef NO_J
printf ("%s", RED);
#endif
if (context->options.format)
{
printProp(key, value, context);
}
else
{
cfshow(value);
println("");
}
#ifndef NO_J
printf ("%s", NORMAL);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
indent(Boolean isNode, UInt32 serviceDepth, UInt64 stackOfBits)
{
// stackOfBits representation, given current zero-based depth is n:
// bit n+1 = does depth n have children? 1=yes, 0=no
// bit [n, .. i .., 0] = does depth i have more siblings? 1=yes, 0=no
UInt32 index;
if (isNode)
{
for (index = 0; index < serviceDepth; index++)
print( (stackOfBits & (1 << index)) ? "| " : " " );
print("+-o ");
}
else // if (!isNode)
{
for (index = 0; index <= serviceDepth + 1; index++)
print( (stackOfBits & (1 << index)) ? "| " : " " );
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
usage()
{
fprintf( stderr,
"usage: ioreg [-abfilrtx] [-c class] [-d depth] [-k key] [-n name] [-p plane] [-w width]\n"
"where options are:\n"
"\t-a archive output\n"
"\t-b show object name in bold\n"
"\t-c list properties of objects with the given class\n"
"\t-d limit tree to the given depth\n"
"\t-f enable smart formatting\n"
"\t-i show object inheritance\n"
"\t-k list properties of objects with the given key\n"
"\t-l list properties of all objects\n"
"\t-n list properties of objects with the given name\n"
"\t-p traverse registry over the given plane (IOService is default)\n"
"\t-r show subtrees rooted by the given criteria\n"
"\t-t show location of each subtree\n"
"\t-w clip output to the given line width (0 is unlimited)\n"
"\t-x show data and numbers as hexadecimal\n"
#ifndef NO_J
"\nJ added the following options:\n"
"\ncolor: by default, to see the output a little bit better, in color\n"
"\t-o Try an IOServiceOpen() to test reachability (userclient via IOServiceOpen).\n\tYou might be pleasantly surprised\n"
#endif
);
exit(1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static char * termcapstr_boldon = 0;
static char * termcapstr_boldoff = 0;
static int termcapstr_outc(int c)
{
return putchar(c);
}
static void boldinit()
{
char * term;
static char termcapbuf[64];
char * termcapbufptr = termcapbuf;
term = getenv("TERM");
if (term)
{
if (tgetent(NULL, term) > 0)
{
termcapstr_boldon = tgetstr("md", &termcapbufptr);
termcapstr_boldoff = tgetstr("me", &termcapbufptr);
assertion(termcapbufptr - termcapbuf <= sizeof(termcapbuf), "can't obtain terminfo");
}
}
if (termcapstr_boldon == 0) termcapstr_boldon = "";
if (termcapstr_boldoff == 0) termcapstr_boldoff = "";
}
static void boldon()
{
tputs(termcapstr_boldon, 1, termcapstr_outc);
}
static void boldoff()
{
tputs(termcapstr_boldoff, 1, termcapstr_outc);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static char * printbuf = 0;
static int printbufclip = FALSE;
static int printbufleft = 0;
static int printbufsize = 0;
static void printinit(int width)
{
if (width)
{
printbuf = malloc(width);
printbufleft = width;
printbufsize = width;
assertion(printbuf != NULL, "can't allocate buffer");
}
}
static void printva(const char * format, va_list arguments)
{
if (printbufsize)
{
char * c;
int count = vsnprintf(printbuf, printbufleft, format, arguments);
while ( (c = strchr(printbuf, '\n')) ) *c = ' '; // (strip newlines)
printf("%s", printbuf);
if (count >= printbufleft)
{
count = printbufleft - 1;
printbufclip = TRUE;
}
printbufleft -= count; // (printbufleft never hits zero, stops at one)
}
else
{
vprintf(format, arguments);
}
}
static void print(const char * format, ...)
{
va_list arguments;
va_start(arguments, format);
printva(format, arguments);
va_end(arguments);
}
static void println(const char * format, ...)
{
va_list arguments;
va_start(arguments, format);
printva(format, arguments);
va_end(arguments);
if (printbufclip) printf("$");
printf("\n");
printbufclip = FALSE;
printbufleft = printbufsize;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static Boolean cfshowhex;
static void cfshowinit(Boolean hex)
{
cfshowhex = hex;
}
static void cfshow(CFTypeRef object)
{
CFTypeID type = CFGetTypeID(object);
if ( type == CFArrayGetTypeID() ) cfarrayshow(object);
else if ( type == CFBooleanGetTypeID() ) cfbooleanshow(object);
else if ( type == CFDataGetTypeID() ) cfdatashow(object);
else if ( type == CFDictionaryGetTypeID() ) cfdictionaryshow(object);
else if ( type == CFNumberGetTypeID() ) cfnumbershow(object);
else if ( type == CFSetGetTypeID() ) cfsetshow(object);
else if ( type == CFStringGetTypeID() ) cfstringshow(object);
else print("<unknown object>");
}
static void cfarrayshowapplier(const void * value, void * parameter)
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
cfshow(value);
}
static void cfarrayshow(CFArrayRef object)
{
Boolean first = TRUE;
CFRange range = { 0, CFArrayGetCount(object) };
print("(");
CFArrayApplyFunction(object, range, cfarrayshowapplier, &first);
print(")");
}
static void cfbooleanshow(CFBooleanRef object)
{
print(CFBooleanGetValue(object) ? "Yes" : "No");
}
static void cfdatashow(CFDataRef object)
{
UInt32 asciiNormalCount = 0;
UInt32 asciiSymbolCount = 0;
const UInt8 * bytes;
CFIndex index;
CFIndex length;
print("<");
length = CFDataGetLength(object);
bytes = CFDataGetBytePtr(object);
//
// This algorithm detects ascii strings, or a set of ascii strings, inside a
// stream of bytes. The string, or last string if in a set, needn't be null
// terminated. High-order symbol characters are accepted, unless they occur
// too often (80% of characters must be normal). Zero padding at the end of
// the string(s) is valid. If the data stream is only one byte, it is never
// considered to be a string.
//
for (index = 0; index < length; index++) // (scan for ascii string/strings)
{
if (bytes[index] == 0) // (detected null in place of a new string,
{ // ensure remainder of the string is null)
break; // (either end of data or a non-null byte in stream)
}
else // (scan along this potential ascii string)
{
for (; index < length; index++)
{
if (isprint(bytes[index]))
asciiNormalCount++;
else if (bytes[index] >= 128 && bytes[index] <= 254)
asciiSymbolCount++;
else
break;
}
if (index < length && bytes[index] == 0) // (end of string)
continue;
else // (either end of data or an unprintable character)
break;
}
}
if ((asciiNormalCount >> 2) < asciiSymbolCount) // (is 80% normal ascii?)
index = 0;
else if (length == 1) // (is just one byte?)
index = 0;
else if (cfshowhex)
index = 0;
if (index >= length && asciiNormalCount) // (is a string or set of strings?)
{
Boolean quoted = FALSE;
for (index = 0; index < length; index++)
{
if (bytes[index])
{
if (quoted == FALSE)
{
quoted = TRUE;
if (index)
print(",\"");
else
print("\"");
}
print("%c", bytes[index]);
}
else
{
if (quoted == TRUE)
{
quoted = FALSE;
print("\"");
}
else
break;
}
}
if (quoted == TRUE)
print("\"");
}
else // (is not a string or set of strings)
{
for (index = 0; index < length; index++) print("%02x", bytes[index]);
}
print(">");
}
static void cfdictionaryshowapplier( const void * key,
const void * value,
void * parameter )
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
cfshow(key);
print("=");
#ifndef NO_J
printf ("%s", RED);
#endif
cfshow(value);
#ifndef NO_J
printf ("%s", NORMAL);
#endif
}
static void cfdictionaryshow(CFDictionaryRef object)
{
Boolean first = TRUE;
print("{");
CFDictionaryApplyFunction(object, cfdictionaryshowapplier, &first);
print("}");
}
static void cfnumbershow(CFNumberRef object)
{
long long number;
if (CFNumberGetValue(object, kCFNumberLongLongType, &number))
{
if (cfshowhex)
print("0x%qx", (unsigned long long)number);
else
print("%qu", (unsigned long long)number);
}
}
static void cfsetshowapplier(const void * value, void * parameter)
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
cfshow(value);
}
static void cfsetshow(CFSetRef object)
{
Boolean first = TRUE;
print("[");
CFSetApplyFunction(object, cfsetshowapplier, &first);
print("]");
}
static void cfstringshow(CFStringRef object)
{
const char * c = CFStringGetCStringPtr(object, kCFStringEncodingMacRoman);
if (c)
print("\"%s\"", c);
else
{
CFIndex bufferSize = CFStringGetLength(object) + 1;
char * buffer = malloc(bufferSize);
if (buffer)
{
if ( CFStringGetCString(
/* string */ object,
/* buffer */ buffer,
/* bufferSize */ bufferSize,
/* encoding */ kCFStringEncodingMacRoman ) )
print("\"%s\"", buffer);
free(buffer);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static CFStringRef createInheritanceStringForIORegistryClassName(CFStringRef name)
{
CFStringRef curClassCFStr;
CFStringRef oldClassCFStr;
CFMutableStringRef outCFStr;
outCFStr = CFStringCreateMutable (NULL, 512);
CFStringInsert (outCFStr, 0, name);
curClassCFStr = CFStringCreateCopy (NULL, name);
for (;;)
{
oldClassCFStr = curClassCFStr;
curClassCFStr = IOObjectCopySuperclassForClass (curClassCFStr);
CFRelease (oldClassCFStr);
if (FALSE == CFEqual (curClassCFStr, CFSTR ("OSObject")))
{
CFStringInsert (outCFStr, 0, CFSTR (":"));
CFStringInsert (outCFStr, 0, curClassCFStr);
}
else
{
break;
}
}
if (curClassCFStr) CFRelease(curClassCFStr);
// Return the CFMutableStringRef as a CFStringRef because it is derived and compatible:
return (CFStringRef) outCFStr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
printProp(CFStringRef key, CFTypeRef value, struct context * context)
{
kern_return_t status = KERN_SUCCESS;
Boolean valueShown = FALSE; // Flag is set when property is printed
io_registry_entry_t thisObj;
thisObj = context->service;
// Match "reg" property for PCI devices.
if (CFStringCompare(key, CFSTR("reg"), 0 ) == 0)
{
io_registry_entry_t parentObj; // (needs release)
io_name_t parentName;
// If the parent entry in the IODeviceTree plane is "pci",
// then we've found what we're looking for.
status = IORegistryEntryGetParentEntry( thisObj,
kIODeviceTreePlane,
&parentObj );
if (status == KERN_SUCCESS)
{
status = IORegistryEntryGetNameInPlane( parentObj,
kIODeviceTreePlane,
parentName );
assertion(status == KERN_SUCCESS, "could not get name of parent");
IOObjectRelease(parentObj);
if (strncmp(parentName, "pci", 3) == 0)
{
printPhysAddr(value, context);
valueShown = TRUE;
}
}
}
// Match "assigned-addresses" property.
else if (CFStringCompare(key, CFSTR("assigned-addresses"), 0) == 0)
{
printPhysAddr(value, context);
valueShown = TRUE;
}
// Match "slot-names" property.
else if (CFStringCompare(key, CFSTR("slot-names"), 0) == 0)
{
printSlotNames(value, context);
valueShown = TRUE;
}
// Match "ranges" property.
else if (CFStringCompare(key, CFSTR("ranges"), 0) == 0)
{
printPCIRanges(value, context);
valueShown = TRUE;
}
// Match "interrupt-map" property.
else if (CFStringCompare(key, CFSTR("interrupt-map"), 0) == 0)
{
printInterruptMap(value, context);
valueShown = TRUE;
}
// Match "interrupts" property.
else if ( CFStringCompare( key, CFSTR("interrupts"), 0) == 0 )
{
printInterrupts( value, context );
valueShown = TRUE;
}
// Match "interrupt-parent" property.
else if ( CFStringCompare( key, CFSTR("interrupt-parent"), 0) == 0 )
{
printInterruptParent( value, context );
valueShown = TRUE;
}
// Print the value if it doesn't have a formatter.
if (valueShown == FALSE)
{
if (CFGetTypeID(value) == CFDataGetTypeID())
{
printData(value, context);
}
else
{
cfshow(value);
println("");
}
}
}
/* The following data structures, masks and shift values are used to decode
* physical address properties as defined by IEEE 1275-1994. The format is
* used in 'reg' and 'assigned-address' properties.
*
* The format of the physHi word is as follows:
*
* npt000ss bbbbbbbb dddddfff rrrrrrrr
*
* n 1 = Relocatable, 0 = Absolute (1 bit)
* p 1 = Prefetchable (1 bit)
* t 1 = Alias (1 bit)
* ss Space code (Config, I/O, Mem, 64-bit Mem) (2 bits)
* bbbbbbbb Bus number (8 bits)
* ddddd Device number (5 bits)
* fff Function number (3 bits)
* rrrrrrrr Register number (8 bits)
*/
struct physAddrProperty {
UInt32 physHi;
UInt32 physMid;
UInt32 physLo;
UInt32 sizeHi;
UInt32 sizeLo;
};
#define kPhysAbsoluteMask 0x80000000
#define kPhysPrefetchMask 0x40000000
#define kPhysAliasMask 0x20000000
#define kPhysSpaceMask 0x03000000
#define kPhysSpaceShift 24
#define kPhysBusMask 0x00FF0000
#define kPhysBusShift 16
#define kPhysDeviceMask 0x0000F800
#define kPhysDeviceShift 11
#define kPhysFunctionMask 0x00000700
#define kPhysFunctionShift 8
#define kPhysRegisterMask 0x000000FF
#define kPhysRegisterShift 0
static SInt32
getRecursivePropValue( io_registry_entry_t thisRegEntry, CFStringRef propertyNameToLookFor )
{
SInt32 returnValue;
CFTypeRef ptr;
ptr = IORegistryEntrySearchCFProperty(thisRegEntry,
kIODeviceTreePlane,
propertyNameToLookFor,
kCFAllocatorDefault,
kIORegistryIterateParents | kIORegistryIterateRecursively);
assertion( ptr != NULL, "unable to get properties" );
returnValue = *(SInt32 *)CFDataGetBytePtr( (CFDataRef) ptr );
CFRelease( ptr );
return( returnValue );
}
static void printPhysAddr(CFTypeRef value, struct context * context)
{
CFIndex length; // stores total byte count in this prop.
struct physAddrProperty *physAddr; // points to current physAddr property
UInt64 numPhysAddr, // how many physAddr's to decode?
count; // loop counter variable
UInt32 tmpCell; // temp storage for a single word
UInt32 busNumber, // temp storage for decoded values
deviceNumber,
functionNumber,
registerNumber;
const char *addressType,
*isPrefetch,
*isAlias,
*isAbsolute;
// Ensure that the object passed in is in fact a CFData object.
assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid phys addr");
// Make sure there is actually data in the object.
length = CFDataGetLength((CFDataRef)value);
if (length == 0)
{
println("<>");
return;
}
numPhysAddr = length / sizeof(struct physAddrProperty);
physAddr = (struct physAddrProperty *)CFDataGetBytePtr((CFDataRef)value);
println("");
for (count = 0; count < numPhysAddr; count++)
{
tmpCell = physAddr[count].physHi; // copy physHi word to a temp var
// Decode the fields in the physHi word.
busNumber = (tmpCell & kPhysBusMask) >> kPhysBusShift;
deviceNumber = (tmpCell & kPhysDeviceMask) >> kPhysDeviceShift;
functionNumber = (tmpCell & kPhysFunctionMask) >> kPhysFunctionShift;
registerNumber = (tmpCell & kPhysRegisterMask) >> kPhysRegisterShift;
isAbsolute = ((tmpCell & kPhysAbsoluteMask) != 0) ? "abs" : "rel";
isPrefetch = ((tmpCell & kPhysPrefetchMask) != 0) ? ", prefetch" : "";
isAlias = ((tmpCell & kPhysAliasMask) != 0) ? ", alias" : "";
switch ((tmpCell & kPhysSpaceMask) >> kPhysSpaceShift)
{
case 0: addressType = "Config"; break;
case 1: addressType = "I/O"; break;
case 2: addressType = "Mem"; break;
case 3: addressType = "64-bit"; break;
default: addressType = "?"; break;
}
// Format and print the information for this entry.
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" %02lu: phys.hi: %08lx phys.mid: %08lx phys.lo: %08lx",
(unsigned long)count,
(unsigned long)physAddr[count].physHi,
(unsigned long)physAddr[count].physMid,
(unsigned long)physAddr[count].physLo );
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" size.hi: %08lx size.lo: %08lx",
(unsigned long)physAddr[count].sizeHi,
(unsigned long)physAddr[count].sizeLo );
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" bus: %lu dev: %lu func: %lu reg: %lu",
(unsigned long)busNumber,
(unsigned long)deviceNumber,
(unsigned long)functionNumber,
(unsigned long)registerNumber );
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" type: %s flags: %s%s%s",
addressType,
isAbsolute,
isPrefetch,
isAlias );
}
}
static void
printSlotNames(CFTypeRef value, struct context * context)
{
CFIndex length;
char * bytePtr;
UInt32 count;
UInt32 * avail_slots;
// Ensure that the object passed in is in fact a CFData object.
assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid phys addr");
// Make sure there is actually data in the object.
length = CFDataGetLength((CFDataRef)value);
if (length == 0)
{
println("<>");
return;
}
avail_slots = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
bytePtr = (char *)avail_slots + sizeof(UInt32);
// Ignore entries that have no named slots.
if (*avail_slots == 0)
{
println("<>");
return;
}
println("");
// Cycle through all 32 bit positions and print slot names.
for (count = 0; count < 32; count++)
{
if ((*avail_slots & (1 << count)) != 0)
{
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" %02lu: %s", (unsigned long)count, bytePtr);
bytePtr += strlen(bytePtr) + 1; // advance to next string
}
}
}
static void
printPCIRanges(CFTypeRef value, struct context * context)
{
kern_return_t status = KERN_SUCCESS;
CFIndex length;
UInt32 *quadletPtr;
SInt32 parentACells, childACells, childSCells, elemSize;
io_registry_entry_t parentObj; // must be released
UInt64 i,j,nRanges;
SInt32 counts[3];
const char *titles[] = {"-child--", "-parent-", "-size---"};
// Ensure that the object passed in is in fact a CFData object.
assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid ranges");
// Make sure there is actually data in the object.
length = CFDataGetLength((CFDataRef)value);
if (length == 0)
{
println("<>");
return;
}
quadletPtr = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
// Get #address-cells of device-tree parent
status = IORegistryEntryGetParentEntry( context->service, kIODeviceTreePlane, &parentObj );
assertion(status == KERN_SUCCESS, "unable to get device tree parent");
parentACells = getRecursivePropValue( parentObj, CFSTR( "#address-cells" ) );
IOObjectRelease( parentObj );
// Get #address-cells and #size-cells for owner
childACells = getRecursivePropValue( context->service, CFSTR( "#address-cells" ) );
childSCells = getRecursivePropValue( context->service, CFSTR( "#size-cells" ) );
// ranges property is a list of [[child addr][parent addr][size]]
elemSize = childACells + parentACells + childSCells;
// print a title line
println("");
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" ");
// set up array of cell counts (only used to print title)
counts[0] = childACells;
counts[1] = parentACells;
counts[2] = childSCells;
for (j = 0; j < 3; j++)
{
print("%s", titles[j]); // titles is init'ed at start of func.
if (counts[j] > 1)
{
print("-");
for( i = 2; i <= counts[j]; i++)
{
if(i == counts[j])
print("-------- ");
else
print("---------");
}
}
else
print(" ");
}
println("");
nRanges = length/(elemSize * sizeof(UInt32));
for(j = 0; j < nRanges; j++)
{
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" ");
for(i = 0; i < elemSize; i++) print("%08lx ", (unsigned long)*quadletPtr++);
println("");
}
}
// constructs a path string for a node in the device tree
static void
makepath(io_registry_entry_t target, io_string_t path)
{
kern_return_t status = KERN_SUCCESS;
status = IORegistryEntryGetPath(target, kIODeviceTreePlane, path);
assertion(status == KERN_SUCCESS, "unable to get path");
memmove(path, strchr(path, ':') + 1, strlen(strchr(path, ':') + 1) + 1);
}
static Boolean
lookupPHandle(UInt32 phandle, io_registry_entry_t * device)
{
CFDictionaryRef props;
Boolean ret = FALSE; // pre-set to failure
CFStringRef key = CFSTR(kIOPropertyMatchKey);
CFDictionaryRef value;
CFStringRef phandleKey = CFSTR("AAPL,phandle");
CFDataRef data;
data = CFDataCreate(NULL, (void *)&phandle, sizeof(UInt32));
props = CFDictionaryCreate( NULL,
(void *)&phandleKey,
(void *)&data,
1,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
value = CFDictionaryCreate( NULL,
(void *)&key,
(void *)&props,
1,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
/* This call consumes 'value', so do not release it.
*/
*device = IOServiceGetMatchingService(kIOMasterPortDefault, value);
if (*device)
ret = TRUE;
CFRelease(props);
CFRelease(data);
return(ret);
}
static void
printInterruptMap(CFTypeRef value, struct context * context)
{
io_registry_entry_t intParent;
io_string_t path;
SInt32 childCells, parentCells;
UInt32 *position, *end;
CFIndex length, count, index;
// Get #address-cells and #interrupt-cells for owner
childCells = getRecursivePropValue( context->service, CFSTR("#address-cells" ) )
+ getRecursivePropValue( context->service, CFSTR("#interrupt-cells" ) );
// Walk through each table entry.
position = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
length = CFDataGetLength((CFDataRef)value)/sizeof(UInt32);
end = position + length;
count = 0;
println("");
while (position < end)
{
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" %02ld: ", (unsigned long)count);
// Display the child's unit interrupt specifier.
print(" child: ");
for (index = 0; index < childCells; index++) {
print("%08lx ", (unsigned long)*position++);
}
println("");
// Lookup the phandle and retreive needed info.
assertion( lookupPHandle(*position, &intParent), "error looking up phandle" );
parentCells = getRecursivePropValue( intParent, CFSTR( "#address-cells" ) )
+ getRecursivePropValue( intParent, CFSTR( "#interrupt-cells" ) );
*path = '\0';
makepath(intParent, path);
IOObjectRelease(intParent);
// Display the phandle, corresponding device path, and
// the parent interrupt specifier.
indent(FALSE, context->serviceDepth, context->stackOfBits);
println(" phandle: %08lx (%s)", (unsigned long)*position++, path);
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" parent: ");
for (index = 0; index < parentCells; index++) {
print("%08lx ", (unsigned long)*position++);
}
println("");
count++;
}
}
static void
printInterrupts(CFTypeRef value, struct context * context)
{
UInt32 *position, *end;
CFIndex length, count, index;
// Walk through each table entry.
position = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
length = CFDataGetLength((CFDataRef)value) / sizeof(UInt32);
end = position + length;
count = 0;
index = 0;
println("");
while (position < end)
{
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" %02ld: ", (unsigned long)index);
if ( count < (length-1) )
{
print("specifier: %08lx (vector: %02lx) sense: %08lx (",
(unsigned long)*position,
(unsigned long)((*position) & 0x000000FF),
(unsigned long)*(position+1) );
position ++;
count ++;
if ( (*position & 0x00000002 ) ) // HyperTransport
{
print( "HyperTransport vector: %04lx, ",
(unsigned long)((*position >> 16) & 0x0000FFFF));
}
println( "%s)", (*position & 1)? "level" : "edge" );
}
else
{
println("parent interrupt-map entry: %08lx",
(unsigned long)*position );
}
position ++;
count ++;
index ++;
}
}
static void
printInterruptParent( CFTypeRef value, struct context * context )
{
io_registry_entry_t parentRegEntry;
io_string_t path;
UInt32 * pHandleValue = (UInt32 *) CFDataGetBytePtr( (CFDataRef) value );
if ( lookupPHandle( *pHandleValue, &parentRegEntry ) )
{
*path = '\0';
makepath( parentRegEntry, path );
print( "<%08lx>", (unsigned long)*pHandleValue );
if ( *path != '\0' )
print( " (%s)", path );
println( "" );
IOObjectRelease( parentRegEntry );
}
}
static char ToAscii(UInt32 nibble)
{
nibble &= 0x0F;
if (nibble <= 9)
return((char)nibble + '0');
else
return((char)nibble - 10 + 'A');
}
static void
printData(CFTypeRef value, struct context * context)
{
UInt32 asciiNormalCount = 0;
UInt32 asciiSymbolCount = 0;
const UInt8 * bytes;
CFIndex index;
CFIndex length;
length = CFDataGetLength(value);
bytes = CFDataGetBytePtr(value);
//
// This algorithm detects ascii strings, or a set of ascii strings, inside a
// stream of bytes. The string, or last string if in a set, needn't be null
// terminated. High-order symbol characters are accepted, unless they occur
// too often (80% of characters must be normal). Zero padding at the end of
// the string(s) is valid. If the data stream is only one byte, it is never
// considered to be a string.
//
for (index = 0; index < length; index++) // (scan for ascii string/strings)
{
if (bytes[index] == 0) // (detected null in place of a new string,
{ // ensure remainder of the string is null)
for (; index < length && bytes[index] == 0; index++) { }
break; // (either end of data or a non-null byte in stream)
}
else // (scan along this potential ascii string)
{
for (; index < length; index++)
{
if (isprint(bytes[index]))
asciiNormalCount++;
else if (bytes[index] >= 128 && bytes[index] <= 254)
asciiSymbolCount++;
else
break;
}
if (index < length && bytes[index] == 0) // (end of string)
continue;
else // (either end of data or an unprintable character)
break;
}
}
if ((asciiNormalCount >> 2) < asciiSymbolCount) // (is 80% normal ascii?)
index = 0;
else if (length == 1) // (is just one byte?)
index = 0;
else if (cfshowhex)
index = 0;
if (index >= length && asciiNormalCount) // (is a string or set of strings?)
{
Boolean quoted = FALSE;
print("<");
for (index = 0; index < length; index++)
{
if (bytes[index])
{
if (quoted == FALSE)
{
quoted = TRUE;
if (index)
print(",\"");
else
print("\"");
}
print("%c", bytes[index]);
}
else
{
if (quoted == TRUE)
{
quoted = FALSE;
print("\"");
}
else
break;
}
}
if (quoted == TRUE)
print("\"");
print(">");
}
else if (length > 8) // (is not a string or set of strings)
{
SInt8 work[ 256 ];
SInt8* p;
UInt32 i;
UInt32 offset;
CFIndex totalBytes;
CFIndex nBytesToDraw;
UInt32 bytesPerLine;
UInt8 c;
totalBytes = length; // assume length is greater than zero
// Calculate number of bytes per line to print, use as much screen
// as possible. The numbers used are derived by counting the number
// of characters that are always printed (data address offset, white
// space, etc ~= 20), indentation from the tree structure (2*depth)
// and 4 characters printed per byte (two hex digits, one space, and
// one ascii char).
bytesPerLine = (context->options.width - 20 - (2*context->serviceDepth))/4;
// Make sure we don't overflow the work buffer (256 bytes)
bytesPerLine = bytesPerLine > 32 ? 32 : bytesPerLine;
for ( offset = 0; offset < totalBytes; offset += bytesPerLine )
{
UInt32 offsetCopy;
UInt16 text;
println("");
if ( ( offset + bytesPerLine ) <= totalBytes )
nBytesToDraw = bytesPerLine;
else
nBytesToDraw = totalBytes - offset;
offsetCopy = offset;
// Convert offset to ASCII.
work[ 8 ] = ':';
p = &work[ 7 ];
while ( offsetCopy != 0 )
{
*p-- = ToAscii( offsetCopy & 0x0F );
offsetCopy >>= 4;
}
// Insert leading zeros.
while ( p >= work )
*p-- = '0';
// Add kBytesPerLine bytes of data.
p = &work[ 9 ];
for ( i = 0; i < nBytesToDraw; i++ )
{
c = bytes[ offset + i ];
*p++ = ' ';
*p++ = ToAscii( ( c & 0xF0 ) >> 4 );
*p++ = ToAscii( c & 0x0F );
}
// Add padding spaces.
for ( ; i < bytesPerLine; i++ )
{
text = ( ( ' ' << 8 ) | ' ' );
*( UInt16 * ) p = text;
p[ 2 ] = ' ';
p += 3;
}
*p++ = ' ';
// Insert ASCII representation of data.
for ( i = 0; i < nBytesToDraw; i++ )
{
c = bytes[ offset + i ];
if ( ( c < ' ' ) || ( c > '~' ) )
c = '.';
*p++ = c;
}
*p = 0;
// Print this line.
indent(FALSE, context->serviceDepth, context->stackOfBits);
print(" %s", work);
} // for
} // else if (length > 32)
else
{
print("<");
for (index = 0; index < length; index++) print("%02x", bytes[index]);
print(">");
}
println("");
}
/**
*
* A relatively simple program to home in on XNU's system call table.
* Coded specifically for iOS kernels, but works just as well on OS X.
* Seeks XNU version string and signature of beginning of system call table.
* Then dumps all system calls. Can work on the kernel proper, or the kernel cache.
*
* System call names auto-generated from iOS's <sys/syscall.h>
* (/Developer/Platforms/iPhoneOS.platform/DeviceSupport/Latest/Symbols/usr/include/sys)
*
* can also be generated from OS X's <sys/syscall.h>, with minor tweaks (e.g. include
* ledger, pid_shutdown_sockets, etc..)
*
* Note, that just because a syscall is present, doesn't imply it's implemented -
* System calls can either point to nosys, or can be stubs returning an error code,
* as is the case with audit syscalls (350-359), among others.
*
* Tested on iOS 2.0 through 9.3
*
* 03/20/14: Updated to dump sysctls, code cleaned, more messy code added
*
* 01/08/16: v2.0: dumps 64-bit, jtool companion file support
* v2.1: dumps MIG tables
* 01/16/16:
* ---------
* This tool has been around for years, and I'm happy to know it helped people.
* It does not promote piracy in any way, form, or manner.
*
* What it DOES promote, PROUDLY, is jailbreaking, and kernel research, in the
* hands of the many, not the elitist, condescending little idiot that @i0n1c is.
*
* And if you have a problem with that, then tough.
*
* 03/17/16: v2.3b: symbolicates kext callouts, better resilience on bad Mach-O headers
*
* 06/16/16: And now that Apple decrypts its kernelcaches, this tool has become
* even more useful!
*
* 05/03/16: v2.3.1: correctly gets all kext names!
* 05/27/16: Tight machlib integration, symbolicates by auto-disassembly with callbacks :-)
*
* 06/16/16 v3b2 (Hatsune): Allowed method switch, Fixed ID= in kexts, now handles 10b2
* segment split kexts!
*
* 08/06/16: cykey fix (no crash on 32-bit), beta 3 with kpp finding
* v2.2.1: kextracts
*
*
* 08/21/16 v3b6 (Hatsune): Split kexts correctly reassembled. Symbolication needs to be fixed, though..
*
* 09/01/16 v3b8 (Hatsune): Got symbolication working, too
*
* 09/08/16 v3.0.1: Got Sandbox collections kind of working, and can now operate on complzss directly
*
*
*
* Coded by Jonathan Levin (a.k.a @Morpheus______), http://newosxbook.com
*
**/
#include <sys/mman.h> // For mmap(2)
#include <sys/stat.h> // For stat(2)
#include <unistd.h> // For everything else
#include <fcntl.h> // O_RDONLY
#include <stdio.h> // printf!
#include <string.h> // strstr..
#ifndef LINUX
#include <CoreFoundation/CoreFoundation.h>
#else
typedef unsigned char u_int8_t;
typedef unsigned int u_int32_t;
typedef unsigned long uint64_t;
typedef unsigned int uint32_t;
#define strnstr(a,b,c) strstr(a,b)
#endif
static int is64 = 0;
static int wantJToolOut = 0;
#define HAVE_LZSS
#ifdef HAVE_LZSS
#include "lzss.c"
#endif
// Mac Policy support
struct mac_policy_conf_64 {
uint64_t mpc_name; /** policy name */
uint64_t mpc_fullname; /** full name */
uint64_t mpc_labelnames; /** managed label namespaces */
uint64_t mpc_labelname_count; /** number of managed label namespaces */
uint64_t mpc_ops; /** operation vector */
uint64_t mpc_loadtime_flags; /** load time flags */
uint64_t mpc_field_off; /** label slot */
uint64_t mpc_runtime_flags; /** run time flags */
uint64_t mpc_list; /** List reference */
uint64_t mpc_data; /** module data */
}; // _mac_policy_conf_64
struct mac_policy_conf_32 {
uint32_t mpc_name; /** policy name */
uint32_t mpc_fullname; /** full name */
uint32_t mpc_labelnames; /** managed label namespaces */
uint32_t mpc_labelname_count; /** number of managed label namespaces */
uint32_t mpc_ops; /** operation vector */
uint32_t mpc_loadtime_flags; /** load time flags */
uint32_t mpc_field_off; /** label slot */
uint32_t mpc_runtime_flags; /** run time flags */
uint32_t mpc_list; /** List reference */
uint32_t mpc_data; /** module data */
}; // _mac_policy_conf_32
char *mac_policy_ops_names[] = {
//"mpo_audit_check_postselect",
"mpo_audit_check_preselect",
"mpo_audit_check_preselect",
"mpo_bpfdesc_label_associate",
"mpo_bpfdesc_label_destroy",
"mpo_bpfdesc_label_init",
"mpo_bpfdesc_check_receive",
"mpo_cred_check_label_update_execve",
"mpo_cred_check_label_update",
"mpo_cred_check_visible",
"mpo_cred_label_associate_fork",
"mpo_cred_label_associate_kernel",
"mpo_cred_label_associate",
"mpo_cred_label_associate_user",
"mpo_cred_label_destroy",
"mpo_cred_label_externalize_audit",
"mpo_cred_label_externalize",
"mpo_cred_label_init",
"mpo_cred_label_internalize",
"mpo_cred_label_update_execve",
"mpo_cred_label_update",
"mpo_devfs_label_associate_device",
"mpo_devfs_label_associate_directory",
"mpo_devfs_label_copy",
"mpo_devfs_label_destroy",
"mpo_devfs_label_init",
"mpo_devfs_label_update",
"mpo_file_check_change_offset",
"mpo_file_check_create",
"mpo_file_check_dup",
"mpo_file_check_fcntl",
"mpo_file_check_get_offset",
"mpo_file_check_get",
"mpo_file_check_inherit",
"mpo_file_check_ioctl",
"mpo_file_check_lock",
"mpo_file_check_mmap_downgrade",
"mpo_file_check_mmap",
"mpo_file_check_receive",
"mpo_file_check_set",
"mpo_file_label_init",
"mpo_file_label_destroy",
"mpo_file_label_associate",
"mpo_ifnet_check_label_update",
"mpo_ifnet_check",
"mpo_ifnet_label_associate",
"mpo_ifnet_label_copy",
"mpo_ifnet_label_destroy",
"mpo_ifnet_label_externalize",
"mpo_ifnet_label_init",
"mpo_ifnet_label_internalize",
"mpo_ifnet_label_update",
"mpo_ifnet_label_recycle",
"mpo_inpcb_check_deliver",
"mpo_inpcb_label_associate",
"mpo_inpcb_label_destroy",
"mpo_inpcb_label_init",
"mpo_inpcb_label_recycle",
"mpo_inpcb_label_update",
"mpo_iokit_check_device",
"mpo_ipq_label_associate",
"mpo_ipq_label_compare",
"mpo_ipq_label_destroy",
"mpo_ipq_label_init",
"mpo_ipq_label_update",
"mpo_reserved1_hook",
"mpo_reserved2_hook",
"mpo_reserved3_hook",
"mpo_reserved4_hook",
"mpo_reserved5_hook",
"mpo_reserved6_hook",
"mpo_reserved7_hook",
"mpo_reserved8_hook",
"mpo_reserved9_hook",
"mpo_mbuf_label_associate_bpfdesc",
"mpo_mbuf_label_associate_ifnet",
"mpo_mbuf_label_associate_inpcb",
"mpo_mbuf_label_associate_ipq",
"mpo_mbuf_label_associate_linklayer",
"mpo_mbuf_label_associate_multicast_encap",
"mpo_mbuf_label_associate_netlayer",
"mpo_mbuf_label_associate_socket",
"mpo_mbuf_label_copy",
"mpo_mbuf_label_destroy",
"mpo_mbuf_label_init",
"mpo_mount_check_fsctl",
"mpo_mount_check_getattr",
"mpo_mount_check_label_update",
"mpo_mount_check_mount",
"mpo_mount_check_remount",
"mpo_mount_check_setattr",
"mpo_mount_check_stat",
"mpo_mount_check_umount",
"mpo_mount_label_associate",
"mpo_mount_label_destroy",
"mpo_mount_label_externalize",
"mpo_mount_label_init",
"mpo_mount_label_internalize",
"mpo_netinet_fragment",
"mpo_netinet_icmp_reply",
"mpo_netinet",
"mpo_pipe_check_ioctl",
"mpo_pipe_check_kqfilter",
"mpo_pipe_check_label_update",
"mpo_pipe_check_read",
"mpo_pipe_check_select",
"mpo_pipe_check_stat",
"mpo_pipe_check_write",
"mpo_pipe_label_associate",
"mpo_pipe_label_copy",
"mpo_pipe_label_destroy",
"mpo_pipe_label_externalize",
"mpo_pipe_label_init",
"mpo_pipe_label_internalize",
"mpo_pipe_label_update",
"mpo_policy_destroy",
"mpo_policy_init",
"mpo_policy_initbsd",
"mpo_policy_syscall",
"mpo_system_check_sysctlbyname",
"mpo_proc_check_inherit_ipc_ports",
"mpo_vnode_check_rename",
"mpo_kext_check_query",
"mpo_iokit_check_nvram_get",
"mpo_iokit_check_nvram_set",
"mpo_iokit_check_nvram_delete",
"mpo_proc_check_expose",
"mpo_proc_check_set_host_special_port",
"mpo_proc_check_set_host_exception_port",
"mpo_reserved10_hook",
"mpo_reserved11_hook",
"mpo_reserved12_hook",
"mpo_reserved13_hook",
"mpo_reserved14_hook",
"mpo_reserved15_hook",
"mpo_reserved16_hook",
"mpo_reserved17_hook",
"mpo_reserved18_hook",
"mpo_reserved19_hook",
"mpo_reserved20_hook",
"mpo_reserved21_hook",
"mpo_posixsem_check_create",
"mpo_posixsem_check_open",
"mpo_posixsem_check_post",
"mpo_posixsem_check_unlink",
"mpo_posixsem_check_wait",
"mpo_posixsem_label_associate",
"mpo_posixsem_label_destroy",
"mpo_posixsem_label_init",
"mpo_posixshm_check_create",
"mpo_posixshm_check_mmap",
"mpo_posixshm_check_open",
"mpo_posixshm_check_stat",
"mpo_posixshm_check",
"mpo_posixshm_check_unlink",
"mpo_posixshm_label_associate",
"mpo_posixshm_label_destroy",
"mpo_posixshm_label_init",
"mpo_proc_check_debug",
"mpo_proc_check_fork",
"mpo_proc_check_get_task_name",
"mpo_proc_check_get_task",
"mpo_proc_check_getaudit",
"mpo_proc_check_getauid",
"mpo_proc_check_getlcid",
"mpo_proc_check_mprotect",
"mpo_proc_check_sched",
"mpo_proc_check_setaudit",
"mpo_proc_check_setauid",
"mpo_proc_check_setlcid",
"mpo_proc_check_signal",
"mpo_proc_check_wait",
"mpo_proc_label_destroy",
"mpo_proc_label_init",
"mpo_socket_check_accept",
"mpo_socket_check_accepted",
"mpo_socket_check_bind",
"mpo_socket_check_connect",
"mpo_socket_check_create",
"mpo_socket_check_deliver",
"mpo_socket_check_kqfilter",
"mpo_socket_check_label_update",
"mpo_socket_check_listen",
"mpo_socket_check_receive",
"mpo_socket_check_received",
"mpo_socket_check_select",
"mpo_socket_check_send",
"mpo_socket_check_stat",
"mpo_socket_check_setsockopt",
"mpo_socket_check_getsockopt",
"mpo_socket_label_associate_accept",
"mpo_socket_label_associate",
"mpo_socket_label_copy",
"mpo_socket_label_destroy",
"mpo_socket_label_externalize",
"mpo_socket_label_init",
"mpo_socket_label_internalize",
"mpo_socket_label_update",
"mpo_socketpeer_label_associate_mbuf",
"mpo_socketpeer_label_associate_socket",
"mpo_socketpeer_label_destroy",
"mpo_socketpeer_label_externalize",
"mpo_socketpeer_label_init",
"mpo_system_check_acct",
"mpo_system_check_audit",
"mpo_system_check_auditctl",
"mpo_system_check_auditon",
"mpo_system_check_host_priv",
"mpo_system_check_nfsd",
"mpo_system_check_reboot",
"mpo_system_check_settime",
"mpo_system_check_swapoff",
"mpo_system_check_swapon",
"mpo_reserved22_hook",
"mpo_sysvmsg_label_associate",
"mpo_sysvmsg_label_destroy",
"mpo_sysvmsg_label_init",
"mpo_sysvmsg_label_recycle",
"mpo_sysvmsq_check_enqueue",
"mpo_sysvmsq_check_msgrcv",
"mpo_sysvmsq_check_msgrmid",
"mpo_sysvmsq_check_msqctl",
"mpo_sysvmsq_check_msqget",
"mpo_sysvmsq_check_msqrcv",
"mpo_sysvmsq_check_msqsnd",
"mpo_sysvmsq_label_associate",
"mpo_sysvmsq_label_destroy",
"mpo_sysvmsq_label_init",
"mpo_sysvmsq_label_recycle",
"mpo_sysvsem_check_semctl",
"mpo_sysvsem_check_semget",
"mpo_sysvsem_check_semop",
"mpo_sysvsem_label_associate",
"mpo_sysvsem_label_destroy",
"mpo_sysvsem_label_init",
"mpo_sysvsem_label_recycle",
"mpo_sysvshm_check_shmat",
"mpo_sysvshm_check_shmctl",
"mpo_sysvshm_check_shmdt",
"mpo_sysvshm_check_shmget",
"mpo_sysvshm_label_associate",
"mpo_sysvshm_label_destroy",
"mpo_sysvshm_label_init",
"mpo_sysvshm_label_recycle",
"mpo_reserved23_hook",
"mpo_reserved24_hook",
"mpo_reserved25_hook",
"mpo_mount_check_snapshot_create",
"mpo_check_snapshot_delete",
"mpo_vnode_check_clone",
"mpo_proc_check_get_cs_info",
"mpo_proc_check_set_cs_info",
"mpo_iokit_check_hid_control",
"mpo_vnode_check_access",
"mpo_vnode_check_chdir",
"mpo_vnode_check_chroot",
"mpo_vnode_check_create",
"mpo_vnode_check_deleteextattr",
"mpo_vnode_check_exchangedata",
"mpo_vnode_check_exec",
"mpo_vnode_check_getattrlist",
"mpo_vnode_check_getextattr",
"mpo_vnode_check_ioctl",
"mpo_vnode_check_kqfilter",
"mpo_vnode_check_label_update",
"mpo_vnode_check_link",
"mpo_vnode_check_listextattr",
"mpo_vnode_check_lookup",
"mpo_vnode_check_open",
"mpo_vnode_check_read",
"mpo_vnode_check_readdir",
"mpo_vnode_check_readlink",
"mpo_vnode_check_rename_from",
"mpo_vnode_check_rename",
"mpo_vnode_check_revoke",
"mpo_vnode_check_select",
"mpo_vnode_check_setattrlist",
"mpo_vnode_check_setextattr",
"mpo_vnode_check_setflags",
"mpo_vnode_check_setmode",
"mpo_vnode_check_setowner",
"mpo_vnode_check_setutimes",
"mpo_vnode_check_stat",
"mpo_vnode_check",
"mpo_vnode_check_unlink",
"mpo_vnode_check_write",
"mpo_vnode_label_associate_devfs",
"mpo_vnode_label_associate_extattr",
"mpo_vnode_label_associate_file",
"mpo_vnode_label_associate_pipe",
"mpo_vnode_label_associate_posixsem",
"mpo_vnode_label_associate_posixshm",
"mpo_vnode_label_associate_singlelabel",
"mpo_vnode_label_associate_socket",
"mpo_vnode_label_copy",
"mpo_vnode_label_destroy",
"mpo_vnode_label_externalize_audit",
"mpo_vnode_label_externalize",
"mpo_vnode_label_init",
"mpo_vnode_label_internalize",
"mpo_vnode_label_recycle",
"mpo_vnode_label_store",
"mpo_vnode_label_update_extattr",
"mpo_vnode_label_update",
"mpo_vnode_notify_create",
"mpo_vnode_check_signature",
"mpo_vnode_check_uipc_bind",
"mpo_vnode_check_uipc_connect",
"mpo_proc_check_run_cs_invalid",
"mpo_proc_check_suspend_resume",
"mpo_thread_userret",
"mpo_iokit_check_set_properties",
"mpo_system_check_chud",
"mpo_vnode_check_searchfs",
"mpo_priv_check",
"mpo_priv_grant",
"mpo_proc_check_map_anon",
"mpo_vnode_check_fsgetpath",
"mpo_iokit_check_open",
"mpo_proc_check_ledger",
"mpo_vnode_notify_rename",
"mpo_vnode_check_setacl",
"mpo_system_check_kas_info",
"mpo_proc_check_cpumon",
"mpo_vnode_notify_open",
"mpo_system_check_info",
"mpo_pty_notify_grant",
"mpo_pty_notify_close",
"mpo_vnode_find_sigs",
"mpo_kext_check_load",
"mpo_kext_check_unload",
"mpo_proc_check_proc_info",
"mpo_vnode_notify_link",
"mpo_iokit_check_filter_properties",
"mpo_iokit_check_get_property",
NULL
};
#include <mach-o/loader.h> // struct mach_header
#include "machlib.h" // from jtool
#include "common.h" // from jtool
#include "companion.h" // from jtool
#include "jtoolsyms.h"
int jtoolOutFD = 0;
char *mmapped = NULL;
int xnu3757_and_later = 0;
int g_jdebug = 0;
int g_dec = 0;
uint64_t prelink_data_data_addr =0;
uint64_t prelink_data_data_offset = 0;
uint64_t prelink_data_data_size= 0;
struct symtabent *kernelSymTable = NULL;
void register_disassembled_function_call_callback (void *Func);
char *filename = NULL;
void **segments = NULL;
typedef struct {
char *sig ;
char *name;
} kext_sig;
// 2.3 kext sigs
kext_sig KextSigs[] = {
// The Magnificent Seven can be identified by their symbols (supported in all archs :)
// plus by the fact that they have no __TEXT.__cstring
{ "MD5Init" , "Libkern Pseudoextension (com.apple.kpi.libkern)" },
{ "lock_get_calendar_microti", "Mach Kernel Pseudoextension (com.apple.kpi.mach)" },
{ "IOBSDNameMatching", "I/O Kit Pseudoextension" },
{ "VNOP_BWRITE", "BSD Kernel Pseudoextension (com.apple.kpi.bsd)" },
{ "ifnet_poll_params", "Private Pseudoextension (com.apple.kpi.private)" },
{ "KUNCExecute", "Unsupported Pseudoextension (com.apple.kpi.unsupported)" },
{ "mac_iokit_check_nvram", "MAC Framework Pseudoextension (com.apple.kpi.dsep)" },
{ "com.apple.driver.AppleSynopsysMIPIDSI", "(com.apple.driver.AppleSynopsysMIPIDSI)" },
{ "com.apple.nke.pptp", "(com.apple.nke.pptp)"},
{ "com.apple.kec.Libm","com.apple.kec.Libm"},
{ "com.apple.driver.AppleEmbeddedAccelerometer", "(com.apple.driver.AppleEmbeddedAccelerometer)" },
{ "com.apple.driver.AppleSamsungI2S", "(com.apple.driver.AppleSamsungI2S)" },
{ "com.apple.driver.AppleT7000PMGR", "(com.apple.driver.AppleT7000PMGR)" } ,
{ "com.apple.driver.AppleS5L8960XUSBEHCI", "(com.apple.driver.AppleS5L8960XUSBEHCI)" } ,
{ "com.apple.driver.AppleT7000CLPC", "(com.apple.driver.AppleT7000CLPC)" },
{ "com.apple.driver.AppleT7000", "(com.apple.driver.AppleT7000)" },
{ "com.apple.driver.AppleS5L8960XUSBHSIC", "(com.apple.driver.AppleS5L8960XUSBHSIC)" },
{ "com.apple.driver.AppleUSBHSIC", "(com.apple.driver.USBHSIC)" },
{ "com.apple.driver.AppleUSBEHCIARM", "(com.apple.driver.AppleUSBEHCIARM)" },
{"com.apple.driver.AppleS5L8960XGPIOIC", "(com.apple.driver.AppleS5L8960XGPIOIC)" },
{"com.apple.driver.AppleInterruptController", "(com.apple.driver.AppleInterruptController)" },
{"com.apple.driver.DiskImages.ReadWriteDiskImage", "(com.apple.driver.DiskImages.ReadWriteDiskImage)" },
{"com.apple.iokit.IOMikeyBusFamily", "(com.apple.iokit.IOMikeyBusFamily)" },
{"com.apple.iokit.AppleARMIISAudio", "(com.apple.iokit.AppleARMIISAudio)" },
{"com.apple.driver.AppleEmbeddedAudio", "(com.apple.driver.AppleEmbeddedAudio)" },
{"com.apple.driver.AppleCS35L19Amp", "(com.apple.driver.AppleCS35L19Amp)" },
{"com.apple.AppleFSCompression.AppleFSCompressionTypeZlib", "(com.apple.AppleFSCompression.AppleFSCompressionTypeZlib)" },
{"com.apple.nke.l2tp", "(com.apple.nke.l2tp)" },
// Others have very unique strings
{ "bsdthread_terminate", "Pthread (com.apple.kec.pthread)" },
// All the rest can be identified by their IO Objects (can't obfuscate this, AAPL.. ;-)
// or other strings (in cases where they're still chatty)
{"lzvn_decode" "AppleFSCompressionTypeZlib (com.apple.AppleFSCompressionTypeZlib)" },
{"AppleVXD393PriorityQueue", "AppleVXD393 (com.apple.driver.AppleVXD393)"},
{"AppleS5L8940XDWI","AppleS5L8940XDWI (com.apple.driver.AppleS5L8940XDWI)"},
{"AppleD2186PMU","AppleD2186PMU (com.apple.driver.AppleD2186PMU)" },
{"AppleDialogPMU::", "AppleDialogPMU (com.apple.driver.AppleDialogPMU)"},
{ "com_apple_driver_KeyDeliveryIOKitMSE", "LSDIOKitMSE (com.apple.driver.LSDIOKitMSE)" },
{ "com_apple_driver_KeyDeliveryIOKit", "LSDIOKit (com.apple.driver.LSDIOKit)" },
{ "Sandbox extension sentinel", "Seatbelt (com.apple.security.sandbox)" },
{ "IOSlowAdaptiveClockingDomain", "IOSlowAdaptiveClockingFamily (com.apple.iokit.IOSlowAdaptiveClockingFamily)" },
{ "AppleSEPManager::", "AppleSEPManager (com.apple.driver.AppleSEPManager)" },
{ "AppleSEPKeyStore::", "AppleSEPKeyStore (com.apple.driver.AppleSEPKeyStore)" },
{ "AppleOscarAsyncEventSource", "AppleOscar (com.apple.driver.AppleOscar)" },
{ "IOSurface::", "IOSurface (com.apple.iokit.IOSurface)" },
{ "AppleS5L8960XDART::", "AppleS5L8960XDART (com.apple.driver.AppleS5L8960XDART)" }, // 42
{ "IODART::", "IODARTFamily (com.apple.driver.IODARTFamily)"},
{ "IOBlockStorageDriver]:", "I/O Kit Storage Family (com.apple.iokit.IOStorageFamily)" }, // 43
{ "IOHDIXControllerUserClient::", "AppleDiskImageDriver (com.apple.driver.DiskImages)" },
{ "IOHDIXHDDriveInKernel" , "AppleDiskImagesKernelBacked (com.apple.driver.DiskImages.KernelBacked)" }, // 45
{ "KDIRAMBackingStore", "AppleDiskImagesRAMBackingStore (com.apple.driver.DiskImages.RAMBackingStore)" },
{ "AppleAJPEGHal::", "AppleJPEGDriver (com.apple.driver.AppleJPEGDriver)" }, // 47
{ "AppleUSBHostMergePropertie", "I/O Kit Driver for USB Devices (com.apple.driver.AppleUSBHostMergeProperties)" },
{ "IOUSBDeviceConfigurator:" , "IOUSBDeviceFamily (com.apple.iokit.IOUSBDeviceFamily)" },
{ "ORS232SerialStreamSync", "IOKit Serial Port Family (com.apple.iokit.IOSerialFamily)" },
{ "AppleOnboardSerialBSDClient:", "AppleOnboardSerial (com.apple.driver.AppleOnboardSerial)" },
{"AppleS5L8940XI2CController:", "AppleS5L8940XI2CController (com.apple.driver.AppleS5L8940XI2C)" },
{ "AppleCS46L71Device", "AppleCS42L71Audio (com.apple.driver.AppleCS42L71Audio)" },
{ "AppleCS46L21Device", "AppleCS42L21Audio (com.apple.driver.AppleCS42L21Audio)" },
{ "IOAccessoryPrimaryDevicePort", "IOAccessoryManager (com.apple.iokit.IOAccessoryManager)" },
//AppleBasebandPCIPDPManager/com.apple.driver.AppleBasebandPCIMAVPDP AppleSSE/com.apple.driver.AppleSSE nke.tls
{"AppleSynopsysOTG3Device", "AppleSynopsysOTGDevice (com.apple.driver.AppleSynopsysOTGDevice)" },
{"CCLogStream:", "CoreCapture (com.apple.driver.CoreCapture)"},
{"CoreCaptureResponder", "CoreCaptureResponder (com.apple.driver.CoreCaptureResponder)" },
{"com_apple_driver_FairPlayIOKitUserClient", "FairPlayIOKit (com.apple.driver.FairPlayIOKit)"},
{"AppleTVIRUserClient", "AppleTVIR (com.apple.driver.AppleTVIR)" }, // TvOS
{"AppleMobileApNonce::" "AppleMobileApNonce (com.apple.driver.AppleMobileApNonce)" },
{"AppleStorageProcessorNode", "AppleStorageProcessorNodes (com.apple.driver.ASPSupportNodes"},
{ "ApplePinotLCD:", "ApplePinotLCD (com.apple.driver.ApplePinotLCD)" },
{ "IOAccelDevice", "IOAcceleratorFamily (com.apple.iokit.IOAcceleratorFamily2)" },
{"AppleUSBEthernetHost::", "AppleUSBEthernetHost (com.apple.driver.AppleUSBEthernetHost" },
{"AppleIDAMInterface", "AppleIDAMInterface (com.apple.driver.AppleIDAMInterface)" },
{ "com.apple.kext.tlsnke", "TLS NKE (com.apple.kext.tlsnke)" },
{ "AppleSSEUserClient", "AppleSSE (com.apple.driver.AppleSSE)" },
{ "AppleM2Scaler", "Apple M2 Scaler and Color Space Converter Driver (com.apple.driver.AppleM2ScalerCSCDriver)"},
{ "IOStreamAudio", "IOStreamAudioFamily (com.apple.iokit.IOStreamAudioFamily)" },
{ "Cyclone", "AppleCycloneErrorHandler (com.apple.driver.AppleCycloneErrorHander"},
{ "IOAudio2TransformerUserClient", "IOAudio2Family (com.apple.iokit.IOAudio2Family)" },
{ "IOCECUserClient::", "IOCECFamily (com.apple.iokit.IOCECFamily)" },
{ "IOAVController", "IOAVFamily (com.apple.iokit.IOAVFamily)"},
{ "AppleDiagnosticDataAccessReadOnly", "AppleDiagnosticDataAcccessReadOnly (com.apple.driver.AppleDiagnosticDataAccessReadOnly", }, // 174
{ "AppleBiometricServices", "AppleBiometricServices (com.apple.driver.AppleBiometricServices)" },
{ "IOPDPPlumbers::", "AppleBasebandPCI (com.apple.driver.AppleBasebandPCI" },
{ "IOMobileFramebufferUserClient::","IOMobileGraphicsFamily (com.apple.iokit.IOMobileGraphicsFamily)" }, // 26
{ "AppleMobileADBE0", "AppleH8ADBE0 (com.apple.driver.AppleH8ADBE0)" },
{ "IOEthernetController:", "I/O Kit Networking Family (com.apple.iokit.IONetworkingFamily)" },
{ "ApplePMGR::", "ApplePMGR (com.apple.driver.ApplePMGR)" },
{ "AppleS8000PMGR:", "AppleS8000PMGR (com.apple.driver.AppleS8000PMGR)" },
{ "IOPCIDevice::", "I/O Kit PCI Family (com.apple.iokit.IOPCIFamily)" },
{ "AppleS800xPCIe:", "AppleS8000PCIe (com.apple.driver.AppleS8000PCIe)"}, // 35
{ "AppleSPIBiometricSensor:", "AppleBiometricSensor (com.apple.driver.AppleBiometricSensor)"},
{ "ProvInfo" , "ProvInfoIOKit (com.apple.driver.ProvInfoIOKit)" } , // 40
{ "AppleBCMWLANTimeKeeper", "AppleBCMWLANCore (com.apple.driver.driver.AppleBCMWLANCore)" },
{ "AppleBCMWLANChipManagerPCIe", "AppleBCMWLANBusInterfacePCIe (com.apple.driver.driver.AppleBCMWLANBusInterfacePCIe)" },
{ "AppleStockholmControlUserClient", "AppleStockholmControl (com.apple.driver.AppleStockholmControl)" },
{ "AppleMesaSEPDriver", "AppleMesaSEPDriver (com.apple.driver.AppleMesaSEPDriver)" }, // also found in StockholmControl...
// Die, Die, DIE, YOU $%#$%$##$%!!!!!
{ "AppleMobileFileIntegrityUser", "AppleMobileFileIntegrity (com.apple.driver.AppleMobileFileIntegrity)"},
{ "AppleEmbeddedI2CLightSensor", "AppleEmbeddedLightSensor (com.apple.driver.AppleEmbeddedLightSensor)" },
// Make this T7/8/9 agnostic by looking at suffix..
{ "TempSensorUserClient", "AppleEmbeddedTempSensor (com.apple.driver.AppleEmbeddedTempSensor)" },
{ "IONetworkUserClient", "iokit.IONetworkingFamily)", },
{ "corecrypto_kext", "corecrypto (com.apple.kec.corecrypto)" },
{ "IOReportFamily", "IOReportFamily (com.apple.iokit.IOReportFamily)" },
{ "AppleARMCPU", "AppleARMPlatform (com.apple.driver.AppleARMPlatform)" },
{ "AppleSamsungSPI" ,"AppleSamsungSPI (com.apple.driver.AppleSamsungSPI)" },
{"IOAESAccelerator::","IOAESAccelerator"},
{"AppleEffaceableStorageUserClient::", "AppleEffaceableStorage (com.apple.driver.AppleEffaceableStorage)" },
{"AppleH6CamIn::", "AppleH6CamIn"},
{"AppleUSB20HubPort","AppleUSB20HubPort"},
{"IO80211AWDLMulti", "AWDL"},
{"AppleUSBHostDevice","AppleUSBHostDevice"},
{"KDIUDIFDiskImage", "KDIUDIFDiskImage" },
{"AppleUSBDeviceMux","AppleUSBDeviceMux"},
{"mDNSOffloadUserClient:", "mDNSOffloadUserClient"},
{"AppleNANDConfigAccess","AppleNANDConfigAccess"},
{"AGXFirmwareKextG4P","AGXFirmwareKextG4P"}, // AppleTV
{"BTReset","BlueTooth-unknown-yet"},
{ "AppleS5L8920XPWM", "AppleS5L8920XPWM (com.apple.driver.AppleS5L8920XPWM)" },
{ "AppleSN2400ChargerFunction", "AppleSN2400Charger (com.apple.driver.AppleSN2400Charger)" },
{ "AppleIPAppenderUserClient" , "AppleIPAppender (com.apple.driver.AppleIPAppender)"},
{ "AppleMultitouchSPI", "AppleMultitouchSPI (com.apple.driver.AppleMultitouchSPI)"},
{ "H264IOSurfaceBuf", "H264 Video Encoder (com.apple.driver.AppleAVE)" },
{ "IOSlaveMemory", "IOSlaveProcessor (com.apple.driver.IOSlaveProcessor)" },
{ "ApplePCIEMSIController", "ApplePCIEMSIController (com.apple.driver.AppleEmbeddedPCIE)" },
{ "IOHIDLibUserClient", "IOHIDFamily (com.apple.iokit.IOHIDFamily)" },
{ "AppleA7IOP", "AppleA7IOP (com.apple.driver.AppleA7IOP)" },
{ NULL, NULL }
};
int g_kct = 0;
char *g_kcs;
char* getKernelCacheStart(void) { return g_kcs; }
void setKernelCacheStart(char *Cache) { g_kcs = Cache; }
int getKernelCacheArch(void)
{
return g_kct;
}
void setKernelCacheArch(int Kct)
{
g_kct = Kct;
}
char *getPtr (char *mmapped, uint64_t loadAddr, uint64_t ptr)
{
if (!ptr) return NULL;
printf("...Getting value : %p\n", ptr);
uint32_t off =0;
char *sect = MachOGetSectionNameContainingAddress(ptr);
uint64_t addr = MachOGetSectionAddr (mmapped, sect);
if (sect)
{
off = MachOGetSectionOffset(mmapped, sect);
}
if (off) return (mmapped + (ptr - addr) + off);
else return 0;
}
void dumpOps (void *OpsPtr)
{
// Assuming 64 for now
int op = 0;
uint64_t *ops = (uint64_t *) OpsPtr;
while (mac_policy_ops_names[op])
{
if (ops[op]) {
if (jtoolOutFD)
{
char buf[1024];
sprintf(buf,"0x%llx:_%s\n", ops[op], mac_policy_ops_names[op]);
write (jtoolOutFD, buf, strlen(buf));
}
else printf("\t\t0x%llx:%s (%d)\n", ops[op], mac_policy_ops_names[op],op);
}
op++;
}
if (op)
fprintf (stderr,"Dumped %d MAC Policy ops!\n", op);
return;
};
#ifndef NOSB
#define MAX_SB_OPERATION_NAMES 200 // should last until Sandbox-949, at current rate of expansion :-)
int num_sandbox_operations = 0;
int profile_size = 0;
char **sb_operation_names = NULL;
int doSandboxOperationNames(char *KextBundleHeader, char *Segment)
{
// Also Only in 64, at least for now
if (!is64) return 0;
// Structured approach would be parsing __DATA.__const and getting the pointers to
// the strings. Faster, though, is to get the CStrings directly.
// No risk of AAPL pulling operation names - they need them in kext for sandbox_check :-)
struct section_64 *sec64TC = MachOGetSection(Segment);
if (!sec64TC) {
fprintf(stderr,"Unable to get operation names from %s\n", Segment);
return 1;
}
char *opName =memmem (KextBundleHeader + sec64TC->offset,
sec64TC->size,
"default\0",
8);
if (!opName){
fprintf(stderr,"Unable to find default profile name in %s\n", Segment);
return 1;
}
// Get operation names
sb_operation_names = calloc (MAX_SB_OPERATION_NAMES , sizeof(char *));
sb_operation_names[0] = opName; // "default"
opName+= (strlen (opName) + 1);
int done = 0;
int name = 0;
for (name = 1;
name < MAX_SB_OPERATION_NAMES && !done;
name++)
{
sb_operation_names[name] = opName;
if (strstr(opName, "system-swap") ) done++;
opName+= (strlen (opName) + 1);
}
num_sandbox_operations = name;
profile_size = (num_sandbox_operations + 1) + 1; // this is shorts, remember!
fprintf (stderr, "Expecting profile size to be %d shorts\n", profile_size);
return 0;
} ;
int doSandboxProfiles(char *KextBundleHeader, char *Section)
{
// Also Only in 64, at least for now
if (!is64) return 0;
struct section_64 *sec64TC = MachOGetSection(Section);
uint64_t addr = sec64TC->addr;
int off = sec64TC->offset;
int size = sec64TC->size;
uint64_t zeros[2] = { 0 };
//uint16_t *profiles = memmem(KextBundleHeader + off, size, "\x02\x00\x88\x00\x9c\x86\x00\x00", 8);
char *profiles = memmem(KextBundleHeader + off , size , zeros, 16);
profiles = memmem(profiles + 16 , size , zeros, 16);
if (!profiles)
{
fprintf(stderr,"FUD! Can't get profiles.. Please tell J about it and submit a sample!\n");
return 1;
}
profiles += 16;
addr += (profiles - (KextBundleHeader + off));
// Otherwise
uint16_t numProfiles = * ((uint16_t *)(profiles + 10));
uint16_t *prof = (uint16_t *)(profiles + 12); // to point to 0x869c
int done = 0;
printf("Found profiles at offset %x, vmaddr %llx\n",
profiles - KextBundleHeader, addr);
printf("Got %d (0x%x) profiles, of size %d bytes each\n", numProfiles, numProfiles, profile_size *2);
int profNum = 0;
uint16_t *lastProf =0;
//int profile_size = 0x85; // In XNU 37xx and later, as measured in shorts
while (profNum < numProfiles && !done)
{
char *profName = (profiles + (*prof * 8)) + 4;
if (*profName)
{
printf("Got profile:0x%03hx = %llx - ", *prof, addr + (*prof * 8) );
printf("%s\n", profName);
if ((lastProf) && ((prof - lastProf) != profile_size )) {
fprintf(stderr,"Warning: Profiles are 0x%x (not 0x%hx, %d) apart\n",
(prof - lastProf),profile_size, profile_size);
}
int op = 0;
for (op = 0 ; op < num_sandbox_operations; op++)
{
printf("%s:%s:", profName,sb_operation_names[op]);
uint16_t first = * ((uint16_t *) (profiles + (*(prof + 2 + op) * 8)));
uint16_t second = * ((uint16_t *) (profiles + (*(prof + 2 + op) * 8))+1);
int resolved =0;
if (first == 1) {
if (second == 5) { printf("deny"); resolved++;}
else if (second == 0) { printf("allow"); resolved++;}
}
if (!resolved) {
printf(//" *0x%llx = "
" %04hx %04hx",
//(addr + (*(prof + 2 + op) * 8)),
first, second
);
if (second > 0x6400) printf ("(%llx)",
addr + (second * 8) );
}
printf("\n");
}
profNum++;
lastProf = prof ;
}
else { printf("Got Profile, but no name\n");}
prof+=profile_size;
}
#if 0
// For now, just use "AGXCompiler" as a hook
char *profiles = memmem(KextBundleHeader, size,
"\x13\x00\x00\x00\x41\x47\x58\x43",
8);
if (!profiles)
{
fprintf(stderr,"FUD! Can't get profiles.. Please tell J about it and submit a sample!\n");
return 1;
}
else fprintf(stderr,"Got it\n");
// Ok. So we have our start.
char *currProfile = profiles;
int test = 0;
for (test = 0 ; test < 1700; test ++)
{
uint32_t *len = (uint32_t *) currProfile;
char *name = (char *) (len + 1);
if (*len == 7) {currProfile += 8; continue; }
printf ("Len: %d, Name: %s\n", *len,name);
if (name[*len-1] == '\xa') { printf("OK\n");}
else printf("NOT OK %x\n", name [*len-1]);
currProfile = name + *len ;
// Need to pad to 8
uint64_t pad = 8 - (((uint64_t) currProfile) % 8);
if (pad !=8) currProfile += pad;
printf ("Next: 0x%llx\n", (currProfile - profiles));
}
#endif
return (0);
} // doSandboxProfiles
#endif // NOSB
int doPolicyOps(char *KextBundleHeader, char *Segment)
{
// Only in 64
if (!is64) return 0;
struct section_64 *sec64TC = MachOGetSection((unsigned char *)"__TEXT.__cstring");
struct section_64 *sec64DC = MachOGetSection((unsigned char *)Segment);
int doIt = 1;
int foundPolicy = 0;
if (!sec64TC) { doIt = 0; fprintf(stderr, "Unable to get __TEXT.__cstring from kext - not symbolicating\n"); };
if (!sec64DC) { doIt = 0 ; }
if (doIt)
{
int off = sec64DC->offset;
int size = sec64DC->size;
uint64_t *lookForAMFI = (uint64_t *) (KextBundleHeader + sec64DC->offset);
int i = 0;
for (i = 0;
i < sec64DC->size / 8;
i++)
{
if (*lookForAMFI && *lookForAMFI > sec64TC->addr &&
*lookForAMFI < sec64TC->addr + sec64TC->size)
{
char *str = KextBundleHeader + sec64TC->offset + (*lookForAMFI - sec64TC->addr);
if ((strcmp (str,"AMFI") == 0) || (strcmp(str,"Sandbox") ==0))
{
fprintf(stderr,"Found policy at %p\n", sec64DC->addr + i * sizeof(uint64_t));
struct mac_policy_conf_64 *mpc64 = (struct mac_policy_conf_64 *) lookForAMFI;
printf("\tPolicy name: %s\n", getPtr(KextBundleHeader, sec64TC->addr - sec64TC->offset, mpc64->mpc_name));
// printf("\tFull name of policy: %s\n", getPtr (KextBundleHeader, sec64TC->addr - sec64TC->offset, mpc64->mpc_fullname));
printf("\tFlags: %llx\n", mpc64->mpc_loadtime_flags);
//*((uint64_t *) getPtr (KextBundleHeader, sec64TC->addr - sec64TC->offset, mpc64->mpc_ops)));
if ((mpc64->mpc_ops & 0xffffff8000000000) == 0xffffff8000000000)
{
printf("\tOps: %llx\n", mpc64->mpc_ops);
dumpOps(getPtr (KextBundleHeader, sec64DC->addr - sec64DC->offset, mpc64->mpc_ops));
}
foundPolicy++;
}
}
lookForAMFI++;
}
}
return (foundPolicy);
}
char *identifyKextNew (char *KextBundleHeader, int Size, char *KernelCache)
{
static char returned[1024];
// Look at first page of Kext header
if (Size < 0x1000) { return ("this kext is too small!\n"); }
// MUCH better method: look at kext's __DATA.__data.
// This is guaranteed (well, mostly :-) to contain a com.apple...
// something
// So we process header, and get section data. Then get __DATA.__data
// and isolate "com.apple.XXXXXXX"
if (g_jdebug) fprintf(stderr,"Processing kext...\n");
segments = processFile((unsigned char *) KextBundleHeader,Size, getKernelCacheArch(), 0, 0,0);
uint32_t __DATA__data_off = MachOGetSectionOffset( (unsigned char *) KextBundleHeader, "__DATA.__data");
uint32_t __DATA__data_size = MachOGetSectionSize((unsigned char *) KextBundleHeader, "__DATA.__data");
uint32_t __DATA__CONST_const_off = MachOGetSectionOffset( (unsigned char *) KextBundleHeader, "__DATA_CONST.__const");
struct source_version_command *svc = (struct source_version_command *) findLoadCommand ((unsigned char *) KextBundleHeader, LC_SOURCE_VERSION,NULL);
uint32_t __DATA__CONST_const_size = 0;
if (__DATA__CONST_const_off)
{
__DATA__CONST_const_size = MachOGetSectionSize( (unsigned char *) KextBundleHeader, "__DATA_CONST.__const");
if (g_jdebug) {
fprintf(stderr,"\ngot data const of kext %d at offset %d:\n", __DATA__CONST_const_size, __DATA__CONST_const_off);
// write (2, (unsigned char *) KernelCache+ __DATA__CONST_const_off, __DATA__CONST_const_size);
}
if (svc)
{
sprintf (returned , "Unknown(%ld.%d.%d.%d.%d)",
(long) ((svc->version) >> 40),
(int) (svc->version >> 30) & 0x000003FF ,
(int) (svc->version >> 20) & 0x000003FF,
(int) (svc->version >> 10) & 0x000003FF,
(int) (svc->version) & 0x000003FF);
}
}
if (!__DATA__data_off || ! __DATA__data_size) {
if (g_jdebug) { fprintf(stderr,"Unable to find __DATA.__data (%d, %d\n",
__DATA__data_off, __DATA__data_size); return (NULL);}
}
else if (g_jdebug) { fprintf(stderr," __DATA.__data is @0x%x, Size %d bytes, Kext Size %d bytes\n",
__DATA__data_off, __DATA__data_size, Size); }
if (__DATA__data_size > Size) { fprintf (stderr,"__DATA.__data size is %d, but total kext size is %d. Something is wrong with this\n"); return (NULL);}
char *WhereFrom =(__DATA__CONST_const_off ? KernelCache: KextBundleHeader);
char *bundle = memmem (WhereFrom + __DATA__data_off, __DATA__data_size,
"com.apple.", strlen("com.apple."));
if (g_jdebug) {
fprintf(stderr,"\n...got data data of kext %d at offset %d:\n", __DATA__data_size, __DATA__data_off);
//
}
char *lastmatch = NULL;
//if (!bundle) { fprintf(stderr,"NO BUNDLE\n");}
//else { fprintf(stderr," SO FAR OK\n");}
while(bundle)
{
lastmatch = bundle;
if (strcmp(bundle,"com.apple.security.sandbox") == 0) break;
if (g_jdebug)
{
printf("Match: %s\n", lastmatch);
}
bundle = memmem (lastmatch + 1, (__DATA__data_size - (lastmatch - bundle)),
"com.apple.", strlen("com.apple."));
}
char *name = lastmatch;
// Even without kextracting we can use symbols in the kext to figure
// out symbols in the kernel. This is because, even though kexts are
// prelinked, stubs remain (you going to fix this now back at cupertino? ;-)
if ( name && (strstr(name,"AppleMobileFile") ||
(strstr(name, "sandbox"))))
{
// From AMFI and Sandbox we can find mac_policy_register.
// This requires a little effort: First, get the policy from __DATA.__const,
// then disassemble to find where it is passed as a first argument
// then symbolicate both the stub, and the kernel symbol
char *segName = "__DATA.__const";
xnu3757_and_later = MachOGetSection("__DATA_CONST.__const");
if (xnu3757_and_later ) { segName = "__DATA_CONST.__const"; }
int foundPolicy = doPolicyOps(KextBundleHeader, segName);
if (!foundPolicy && ! xnu3757_and_later)
{
foundPolicy = doPolicyOps(KextBundleHeader, "__DATA.__data");
if (foundPolicy)
fprintf(stderr,"Found policy in __DATA.__data\n");
}
else {
fprintf(stderr,"Found policy in %s\n", segName);
}
if (!foundPolicy && !strstr(name,"AppleMobileFile")) { fprintf(stderr,"MAC policy not found. This is fine for kernels prior to iOS 9.2, but please let J know if yours is newer\n");}
} // AppleMobile
if (name) {
strncpy(returned, name, 1024);
}
else return (NULL); // strcpy(returned, "built-in?");
if (svc)
{
sprintf (returned + strlen(returned), "(%ld.%d.%d.%d.%d)",
(long) ((svc->version) >> 40),
(int) (svc->version >> 30) & 0x000003FF ,
(int) (svc->version >> 20) & 0x000003FF,
(int) (svc->version >> 10) & 0x000003FF,
(int) (svc->version) & 0x000003FF);
}
if (((svc->version >> 40) >= 570) && (name && (strstr(name, "sandbox"))))
{
fprintf(stderr,"This is the sandbox.kext, version %ld - Trying to get seatbelt-profiles\n",
svc->version >> 40);
if (doSandboxOperationNames(KextBundleHeader, "__TEXT.__cstring"))
{
fprintf(stderr,"Can't get profiles with sandbox operation names\n");
}
else
doSandboxProfiles (KextBundleHeader, "__TEXT.__const");
}
return (returned);
};
char *identifyKext (char *KextBundleHeader, int Size)
{
// Look at first page of Kext header
if (Size < 0x1000) { return ("this kext is too small!\n"); }
// This is CRUDE. I know. But hey, it works. It could be optimized in several ways,
// including:
//
// A) bailing on a false positive
// B) marking out kexts already found and skipping them
// and most importantly -
// C) Use machlib to just sift through the TEXT.__cstring section!
//
// (but this is fast, and works!)
int i = 0;
for (i = 0;
KextSigs[i].sig;
i++)
{
if (memmem(KextBundleHeader, Size, KextSigs[i].sig, strlen (KextSigs[i].sig)) )
return KextSigs[i].name;
}
return NULL;
}
// 2.4
// For 64-bit
char output[1024];
int function_identifier (char *Symbol, uint64_t *Regs, int Call)
{
static int panic = 0;
// fprintf(stdout,"Called back : %llx\n", Regs[0]);
if (Regs[R0] > 0xfffffff000000000)
{
// @Todo: > 0xxx.... size of text cstring, also optimize func, mark checks
// in array when check done so can skip
char *sect = MachOGetSectionNameContainingAddress(Regs[0]);
if (sect && (strcmp(sect,"__TEXT.__cstring") == 0))
{
char *str = getPointerToAddr(Regs[0]);
if ((!panic) && strcmp(str,"\"%s[KERNEL]: %s\"") == 0) {
fprintf(stderr,"GOT panic: 0x%llx\n", Regs[32]);
sprintf(output, "_panic");
addSymbolToCache (output, Regs[32],NULL);
panic++;
};
if (strcmp(str,"IOBSD") == 0)
{
sprintf(output,"__ZN9IOService15publishResourceEPKcP8OSObject");
fprintf(stderr,"GOT __ZN9IOService15publishResourceEPKcP8OSObject: 0x%llx\n", Regs[32]);
addSymbolToCache (output, Regs[32],NULL);
//sprintf(output,"%llx:__ZN9IOService15publishResourceEPKcP8OSObject\n", Regs[32]);
// write (jtoolOutFD, output, strlen(output));
}
if (strncmp(str,"BSD root: %s,", strlen("BSD root: %s,")) == 0)
{
fprintf(stderr,"GOT IOLog! 0x%llx\n", Regs[32]);
sprintf(output, "_IOLog");
addSymbolToCache(output, Regs[32],NULL);
//sprintf(output, "%llx:_IOLog\n", Regs[32]);
//write (jtoolOutFD, output, strlen(output));
}
if (strcmp(str,"#size-cells")==0) {
fprintf(stderr,"GOT __ZN8OSSymbol17withCStringNoCopyEPKc: 0x%llx\n", Regs[32]);
//sprintf(output, "%llx:___ZN8OSSymbol17withCStringNoCopyEPKc\n", Regs[32]);
//write (jtoolOutFD, output, strlen(output));
addSymbolToCache("__ZN8OSSymbol17withCStringNoCopyEPKc", Regs[32],NULL);
}
if (strcmp(str,"-zp") == 0) {
fprintf(stderr,"GOT PE_Parse_boot_argn: 0x%llx\n", Regs[32]);
addSymbolToCache("_PE_Parse_boot_argn", Regs[32],NULL);
// sprintf(output, "%llx:_PE_Parse_boot_argn\n", Regs[32]);
// write (jtoolOutFD, output, strlen(output));
}
if (strcmp(str,"hw.memsize") == 0) {
fprintf(stderr,"GOT PE_get_default: 0x%llx\n", Regs[32]);
// sprintf(output, "%llx:_PE_get_default\n", Regs[32]);
//owrite (jtoolOutFD, output, strlen(output));
addSymbolToCache ("_PE_get_default", Regs[32],NULL);
// return (DISASSEMBLE_BREAK);
}
}
}
// TODO - optimize by prefetching and storing TEXT__CSTRING
if (Regs[R3] > 0xfffffff000000000)
{
char *sect = MachOGetSectionNameContainingAddress(Regs[R3]);
if (sect && (strcmp(sect, "__TEXT.__cstring") == 0))
{
char *str = getPointerToAddr(Regs[3]);
if (strcmp(str,"vstruct zone")==0) {
fprintf(stderr,"GOT zinit: 0x%llx\n", Regs[32]);
addSymbolToCache("_zinit" , Regs[32],NULL);
// sprintf(output, "%llx:_zinit\n", Regs[32]);
// write (jtoolOutFD, output, strlen(output));
}
}
}
if (Regs[R2] > 0xfffffff000000000)
{
char *sect = MachOGetSectionNameContainingAddress(Regs[2]);
if (sect && (strcmp(sect, "__TEXT.__cstring") == 0))
{
char *str = getPointerToAddr(Regs[2]);
if (strcmp(str,"Jettisoning kext bootstrap segments.") == 0)
{
printf("GOT OSKextLog: 0x%llx\n", Regs[32]);
addSymbolToCache("_OSKextLog", Regs[32],NULL);
//return DISASSEMBLE_BREAK;
}
}
}
if (Regs[R1] > 0xfffffff000000000)
{
char *sect = MachOGetSectionNameContainingAddress(Regs[1]);
if (sect && (strcmp(sect, "__TEXT.__cstring") == 0))
{
char *str = getPointerToAddr(Regs[1]);
if (strcmp(str,"OSMalloc_tag") == 0) {
fprintf(stderr,"GOT lck_grp_alloc_init: 0x%llx\n", Regs[32]);
// sprintf(output,"%llx:_lck_grp_alloc_init\n", Regs[32]);
// write (jtoolOutFD, output, strlen(output));
addSymbolToCache("_lck_grp_alloc_init" , Regs[32],NULL);
}
if (strcmp(str,"vm_swap_data") == 0) {
fprintf(stderr,"GOT lck_grp_init: 0x%llx\n", Regs[32]);
addSymbolToCache("_lck_grp_init" , Regs[32],NULL);
// sprintf(output,"%llx:_lck_grp_init\n", Regs[32]);
// write (jtoolOutFD, output, strlen(output));
}
}
}
return 0;
}
// From Machlib's CS
void doSignature (void *Blob , int ShowEnt, unsigned char *MachOHeader) {};
void *dumpBlob(unsigned char *blob, int ShowEnt, unsigned char *MachO) {};
int validateBlob(unsigned char *Blob , unsigned int Size, void *SuperBlob) {};
#
char *syscall_names[] = { "syscall", "exit", "fork", "read", "write", "open", "close", "wait4", "8 old creat", "link", "unlink", "11 old execv", "chdir", "fchdir", "mknod", "chmod", "chown", "17 old break", "getfsstat", "19 old lseek", "getpid", "21 old mount", "22 old umount", "setuid", "getuid", "geteuid", "ptrace", "recvmsg", "sendmsg", "recvfrom", "accept", "getpeername", "getsockname", "access", "chflags", "fchflags", "sync", "kill", "38 old stat", "getppid", "40 old lstat", "dup", "pipe", "getegid", "profil", "45 old ktrace", "sigaction", "getgid", "sigprocmask", "getlogin", "setlogin", "acct", "sigpending", "sigaltstack", "ioctl", "reboot", "revoke", "symlink", "readlink", "execve", "umask", "chroot", "62 old fstat", "63 used internally , reserved", "64 old getpagesize", "msync", "vfork", "67 old vread", "68 old vwrite", "69 old sbrk", "70 old sstk", "71 old mmap", "72 old vadvise", "munmap", "mprotect", "madvise", "76 old vhangup", "77 old vlimit", "mincore", "getgroups", "setgroups", "getpgrp", "setpgid", "setitimer", "84 old wait", "swapon", "getitimer", "87 old gethostname", "88 old sethostname", "getdtablesize", "dup2", "91 old getdopt", "fcntl", "select", "94 old setdopt", "fsync", "setpriority", "socket", "connect", "99 old accept", "getpriority", "101 old send", "102 old recv", "103 old sigreturn", "bind", "setsockopt", "listen", "107 old vtimes", "108 old sigvec", "109 old sigblock", "110 old sigsetmask", "sigsuspend", "112 old sigstack", "113 old recvmsg", "114 old sendmsg", "115 old vtrace", "gettimeofday", "getrusage", "getsockopt", "119 old resuba", "readv", "writev", "settimeofday", "fchown", "fchmod", "125 old recvfrom", "setreuid", "setregid", "rename", "129 old truncate", "130 old ftruncate", "flock", "mkfifo", "sendto", "shutdown", "socketpair", "mkdir", "rmdir", "utimes", "futimes", "adjtime", "141 old getpeername", "gethostuuid", "143 old sethostid", "144 old getrlimit", "145 old setrlimit", "146 old killpg", "setsid", "148 old setquota", "149 old qquota", "150 old getsockname", "getpgid", "setprivexec", "pread", "pwrite", "nfssvc", "156 old getdirentries", "statfs", "fstatfs", "unmount", "160 old async_daemon", "getfh", "162 old getdomainname", "163 old setdomainname", "164", "quotactl", "166 old exportfs", "mount", "168 old ustat", "csops", "csops_audittoken", "171 old wait3", "172 old rpause", "waitid", "174 old getdents", "175 old gc_control", "add_profil", "kdebug_typefilter", "kdebug_trace_string", "kdebug_trace64", "kdebug_trace", "setgid", "setegid", "seteuid", "sigreturn", "chud", "186", "fdatasync", "stat", "fstat", "lstat", "pathconf", "fpathconf", "193", "getrlimit", "setrlimit", "getdirentries", "mmap", "198 __syscall", "lseek", "truncate", "ftruncate", "__sysctl", "mlock", "munlock", "undelete", "ATsocket", "ATgetmsg", "ATputmsg", "ATPsndreq", "ATPsndrsp", "ATPgetreq", "ATPgetrsp", "213 Reserved for AppleTalk", "214", "215", "mkcomplex", "statv", "lstatv", "fstatv", "getattrlist", "setattrlist", "getdirentriesattr", "exchangedata", "224 old checkuseraccess / fsgetpath ( which moved to 427 )", "searchfs", "delete", "copyfile", "fgetattrlist", "fsetattrlist", "poll", "watchevent", "waitevent", "modwatch", "getxattr", "fgetxattr", "setxattr", "fsetxattr", "removexattr", "fremovexattr", "listxattr", "flistxattr", "fsctl", "initgroups", "posix_spawn", "ffsctl", "246", "nfsclnt", "fhopen", "249", "minherit", "semsys", "msgsys", "shmsys", "semctl", "semget", "semop", "257", "msgctl", "msgget", "msgsnd", "msgrcv", "shmat", "shmctl", "shmdt", "shmget", "shm_open", "shm_unlink", "sem_open", "sem_close", "sem_unlink", "sem_wait", "sem_trywait", "sem_post", "sem_getvalue", "sem_init", "sem_destroy", "open_extended", "umask_extended", "stat_extended", "lstat_extended", "fstat_extended", "chmod_extended", "fchmod_extended", "access_extended", "settid", "gettid", "setsgroups", "getsgroups", "setwgroups", "getwgroups", "mkfifo_extended", "mkdir_extended", "identitysvc", "shared_region_check_np", "shared_region_map_np", "vm_pressure_monitor", "psynch_rw_longrdlock", "psynch_rw_yieldwrlock", "psynch_rw_downgrade", "psynch_rw_upgrade", "psynch_mutexwait", "psynch_mutexdrop", "psynch_cvbroad", "psynch_cvsignal", "psynch_cvwait", "psynch_rw_rdlock", "psynch_rw_wrlock", "psynch_rw_unlock", "psynch_rw_unlock2", "getsid", "settid_with_pid", "psynch_cvclrprepost", "aio_fsync", "aio_return", "aio_suspend", "aio_cancel", "aio_error", "aio_read", "aio_write", "lio_listio", "321 old __pthread_cond_wait", "iopolicysys", "process_policy", "mlockall", "munlockall", "326", "issetugid", "__pthread_kill", "__pthread_sigmask", "__sigwait", "__disable_threadsignal", "__pthread_markcancel", "__pthread_canceled", "__semwait_signal", "335 old utrace", "proc_info", "sendfile", "stat64", "fstat64", "lstat64", "stat64_extended", "lstat64_extended", "fstat64_extended", "getdirentries64", "statfs64", "fstatfs64", "getfsstat64", "__pthread_chdir", "__pthread_fchdir", "audit", "auditon", "352", "getauid", "setauid", "getaudit", "setaudit", "getaudit_addr", "setaudit_addr", "auditctl", "bsdthread_create", "bsdthread_terminate", "kqueue", "kevent", "lchown", "stack_snapshot", "bsdthread_register", "workq_open", "workq_kernreturn", "kevent64", "__old_semwait_signal", "__old_semwait_signal_nocancel", "thread_selfid", "ledger", "kevent_qos", "375", "376", "377", "378", "379", "__mac_execve", "__mac_syscall", "__mac_get_file", "__mac_set_file", "__mac_get_link", "__mac_set_link", "__mac_get_proc", "__mac_set_proc", "__mac_get_fd", "__mac_set_fd", "__mac_get_pid", "__mac_get_lcid", "__mac_get_lctx", "__mac_set_lctx", "setlcid", "getlcid", "read_nocancel", "write_nocancel", "open_nocancel", "close_nocancel", "wait4_nocancel", "recvmsg_nocancel", "sendmsg_nocancel", "recvfrom_nocancel", "accept_nocancel", "msync_nocancel", "fcntl_nocancel", "select_nocancel", "fsync_nocancel", "connect_nocancel", "sigsuspend_nocancel", "readv_nocancel", "writev_nocancel", "sendto_nocancel", "pread_nocancel", "pwrite_nocancel", "waitid_nocancel", "poll_nocancel", "msgsnd_nocancel", "msgrcv_nocancel", "sem_wait_nocancel", "aio_suspend_nocancel", "__sigwait_nocancel", "__semwait_signal_nocancel", "__mac_mount", "__mac_get_mount", "__mac_getfsstat", "fsgetpath", "audit_session_self", "audit_session_join", "fileport_makeport", "fileport_makefd", "audit_session_port","pid_suspend", "pid_resume", "pid_hibernate", "pid_shutdown_sockets", "437 old shared_region_slide_np", "shared_region_map_and_slide_np" ,
"kas_info", "memorystatus_control", "guarded_open_np","guarded_close_np",
"guarded_kqueue_np",
"change_fdguard_np",
"proc_rlimit_control",
"proc_rlimit_control",
"proc_connectx",
"proc_disconnectx",
"proc_peeloff",
"proc_socket_delegate",
"proc_telemetry",
"proc_uuid_policy", // 452
"memorystatus_get_level", // 453
"system_override", // 454 - as of iOS8
"vfs_purge",
"sfi_ctl",
"sfi_pidctl",
"coalition",
"coalition_info",
"necp_match_policy", // 460
"getattrlistbulk", // 461
"clonefileat", // 462
"openat",
"openat_nocancel",
"renameat",
"faccessat",
"fchmodat",
"fchownat",
"fstatat",
"fstatat64", // 470
"linkat",
"unlinkat", // 472
"readlinkat",
"symlinkat",
"mkdirat",
"getattrlistat",
"proc_trace_log",
"bsdthread_ctl",
"openbyid_np",
"recvmsg_x", // 480
"sendmsg_x",
"thread_selfusage",
"csrctl",
"guarded_open_dprotected_np",
"guarded_write_np",
"guarded_pwrite_np",
"guarded_writev_np",
"rename_ext",
"mremap_encrypted",
// iOS 9/Xnu 3216
"netagent_trigger", // 490
"stack_snapshot_with_config",
"microstackshot",
"grab_pgo_data",
"persona",
"#495",
"#496",
"#497",
"#498",
"work_interval_ctl",
"getentropy ",
"necp_open ",
"necp_client_action",
"__nexus_open ",
"__nexus_register",
"__nexus_deregister",
"__nexus_create",
"__nexus_destroy",
"__nexus_get_opt",
"__nexus_set_opt",
"__channel_open",
"__channel_get_info",
"__channel_sync",
"__channel_get_opt",
"__channel_set_opt",
"ulock_wait ",
"ulock_wake ",
"fclonefileat ",
"fs_snapshot ",
"#519",
"terminate_with_payload",
"abort_with_payload",
NULL
};
// That MOV PC,R9 always gives it away , now..
const char *ARMExcVector = "\x09\xf0\xa0\xe1\xfe\xff\xff\xea";
const char * mach_syscall_name_table[128] = {
/* 0 */ "kern_invalid",
/* 1 */ "kern_invalid",
/* 2 */ "kern_invalid",
/* 3 */ "kern_invalid",
/* 4 */ "kern_invalid",
/* 5 */ "kern_invalid",
/* 6 */ "kern_invalid",
/* 7 */ "kern_invalid",
/* 8 */ "kern_invalid",
/* 9 */ "kern_invalid",
/* 10 */ "_kernelrpc_mach_vm_allocate_trap", // OS X : "kern_invalid",
/* 11 */ "_kernelrpc_vm_allocate_trap", // OS X : "kern_invalid",
/* 12 */ "_kernelrpc_mach_vm_deallocate_trap", // OS X: "kern_invalid",
/* 13 */ "_kernelrpc_vm_deallocate_trap" , // "kern_invalid",
/* 14 */ "_kernelrpc_mach_vm_protect_trap", //"kern_invalid",
/* 15 */ "_kernelrpc_vm_protect_trap", // kern_invalid",
/* 16 */ "_kernelrpc_mach_port_allocate_trap", //"kern_invalid",
/* 17 */ "_kernelrpc_mach_port_destroy_trap" ,//"kern_invalid",
/* 18 */ "_kernelrpc_mach_port_deallocate_trap", // "kern_invalid",
/* 19 */ "_kernelrpc_mach_port_mod_refs_trap", //"kern_invalid",
/* 20 */ "_kernelrpc_mach_port_move_member_trap", //"kern_invalid",
/* 21 */ "_kernelrpc_mach_port_insert_right_trap", //"kern_invalid",
/* 22 */ "_kernelrpc_mach_port_insert_member_trap", // "kern_invalid",
/* 23 */ "_kernelrpc_mach_port_extract_member_trap", // "kern_invalid",
/* 24 */ "__kernelrpc_mach_port_construct_trap", // in 24xx, else "kern_invalid",
/* 25 */ "__kernelrpc_mach_port_destruct_trap", // in 24xx, "kern_invalid",
/* 26 */ "mach_reply_port",
/* 27 */ "thread_self_trap",
/* 28 */ "task_self_trap",
/* 29 */ "host_self_trap",
/* 30 */ "kern_invalid",
/* 31 */ "mach_msg_trap",
/* 32 */ "mach_msg_overwrite_trap",
/* 33 */ "semaphore_signal_trap",
/* 34 */ "semaphore_signal_all_trap",
/* 35 */ "semaphore_signal_thread_trap",
/* 36 */ "semaphore_wait_trap",
/* 37 */ "semaphore_wait_signal_trap",
/* 38 */ "semaphore_timedwait_trap",
/* 39 */ "semaphore_timedwait_signal_trap",
/* 40 */ "kern_invalid",
/* 41 */ "__kernelrpc_mach_port_guard_trap", // as of 24xx - else "kern_invalid",
/* 42 */ "__kernelrpc_mach_port_unguard_trap", // as of 24xx - else "kern_invalid",
/* 43 */ "map_fd", // invalidated in 27xx
/* 44 */ "task_name_for_pid",
/* 45 */ "task_for_pid",
/* 46 */ "pid_for_task",
/* 47 */ "kern_invalid",
/* 48 */ "macx_swapon",
/* 49 */ "macx_swapoff",
/* 50 */ "kern_invalid",
/* 51 */ "macx_triggers",
/* 52 */ "macx_backing_store_suspend",
/* 53 */ "macx_backing_store_recovery",
/* 54 */ "kern_invalid",
/* 55 */ "kern_invalid",
/* 56 */ "kern_invalid",
/* 57 */ "kern_invalid",
/* 58 */ "pfz_exit",
/* 59 */ "swtch_pri",
/* 60 */ "swtch",
/* 61 */ "thread_switch",
/* 62 */ "clock_sleep_trap",
/* 63 */ "kern_invalid",
/* traps 64 - 95 reserved (debo) */
/* 64 */ "kern_invalid",
/* 65 */ "kern_invalid",
/* 66 */ "kern_invalid",
/* 67 */ "kern_invalid",
/* 68 */ "kern_invalid",
/* 69 */ "kern_invalid",
/* 70 */ "kern_invalid",
/* 71 */ "kern_invalid",
/* 72 */ "kern_invalid",
/* 73 */ "kern_invalid",
/* 74 */ "kern_invalid",
/* 75 */ "kern_invalid",
/* 76 */ "kern_invalid",
/* 77 */ "kern_invalid",
/* 78 */ "kern_invalid",
/* 79 */ "kern_invalid",
/* 80 */ "kern_invalid",
/* 81 */ "kern_invalid",
/* 82 */ "kern_invalid",
/* 83 */ "kern_invalid",
/* 84 */ "kern_invalid",
/* 85 */ "kern_invalid",
/* 86 */ "kern_invalid",
/* 87 */ "kern_invalid",
/* 88 */ "kern_invalid",
/* 89 */ "mach_timebase_info_trap",
/* 90 */ "mach_wait_until_trap",
/* 91 */ "mk_timer_create_trap",
/* 92 */ "mk_timer_destroy_trap",
/* 93 */ "mk_timer_arm_trap",
/* 94 */ "mk_timer_cancel_trap",
/* 95 */ "kern_invalid",
/* traps 64 - 95 reserved (debo) */
/* 96 */ "kern_invalid",
/* 97 */ "kern_invalid",
/* 98 */ "kern_invalid",
/* 99 */ "kern_invalid",
/* traps 100-107 reserved for iokit (esb) */
/* 100 */ "iokit_user_client_trap",
/* 101 */ "kern_invalid",
/* 102 */ "kern_invalid",
/* 103 */ "kern_invalid",
/* 104 */ "kern_invalid",
/* 105 */ "kern_invalid",
/* 106 */ "kern_invalid",
/* 107 */ "kern_invalid",
/* traps 108-127 unused */
/* 108 */ "kern_invalid",
/* 109 */ "kern_invalid",
/* 110 */ "kern_invalid",
/* 111 */ "kern_invalid",
/* 112 */ "kern_invalid",
/* 113 */ "kern_invalid",
/* 114 */ "kern_invalid",
/* 115 */ "kern_invalid",
/* 116 */ "kern_invalid",
/* 117 */ "kern_invalid",
/* 118 */ "kern_invalid",
/* 119 */ "kern_invalid",
/* 120 */ "kern_invalid",
/* 121 */ "kern_invalid",
/* 122 */ "kern_invalid",
/* 123 */ "kern_invalid",
/* 124 */ "kern_invalid",
/* 125 */ "kern_invalid",
/* 126 */ "kern_invalid",
/* 127 */ "kern_invalid",
};
// Fixed this because as of iOS 9 or so Apple moved source cache to
// /Library/Caches/com.apple.xbs/Sources/xnu/xnu-3216.0.0.1.15
#define XNUSIG "/xnu-"
#define SYS_MAXSYSCALL 443
#define SYS_MAXSYSCALL_7 454
#define SYS_MAXSYSCALL_8 489
#define SYS_MAXSYSCALL_9 500
#define SYS_MAXSYSCALL_10 521
#define SIG1 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00"
#define SIG1_SUF "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x04\x00\x00\x00"
#define SIG2 "\x00\x00\x00\x00" \
"\x00\x00\x00\x00" \
"\x01\x00\x00\x00" \
"\x1C\x00\x00\x00" \
"\x00\x00\x00\x00"
#define SIG1_IOS7X "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00"
#define SIG2_IOS7X "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x04\x00"
#define SIG1_IOS8X "\x00\x00\x00\x00\x01\x00\x04\x00"
#define SIG1_AFTER_0x18_IOS8X "\x06\x00\x00\x00" "\x03\x00\x0c\x00"
#define SIG_SYSCALL_3 "\x06\x00\x00\x00\x03\x00\x0c\x00"
#define SIG_MIG_MACH_VM "\xC0\x12\x00\x00\xD4\x12\x00\x00"
#define SIG_MIG_TASK "\x48\x0d\x00\x00\x72\x0d\x00\x00"
typedef struct mig_subsystem_struct {
uint32_t min;
uint32_t max;
char *names;
} mig_subsys;
mig_subsys mach_vm_subsys ={ 0x12c0, 0x12d4, NULL};
mig_subsys task_subsys ={ 0xd48, 0xd7a , NULL};
mig_subsys mach_host_subsys_9 = { 200, 230 } ;
mig_subsys host_priv_subsys = { 400, 426 } ;
mig_subsys thread_act_subsys = { 3600, 3628 };
mig_subsys mach_port_subsys = { 3200, 3236 };
mig_subsys is_iokit_subsys = { 2800, 2885 };
mig_subsys is_iokit_subsys_9 = { 2800, 2885 };
mig_subsys processor_set_subsys = { 4000, 4010 };
mig_subsys host_security_subsys = { 600, 602 };
mig_subsys processor_subsys = { 3000, 3006};
// It would be great if we could just get the _subsystem_to_name_map ... mig generated defines,
// but the iOS compiler complains. So no.
typedef struct mig_func_struct {
char *name;
int num;
} mig_func_desc;
mig_func_desc processor_mig[] = {
{ "processor_start", 3000 },\
{ "processor_exit", 3001 },\
{ "processor_info", 3002 },\
{ "processor_control", 3003 },\
{ "processor_assign", 3004 },\
{ "processor_get_assignment", 3005 },
{NULL,3006}
};
mig_func_desc mach_port_mig [] = {{ "mach_port_names", 3200 }, { "mach_port_type", 3201 }, { "mach_port_rename", 3202 }, { "mach_port_allocate_name", 3203 }, { "mach_port_allocate", 3204 }, { "mach_port_destroy", 3205 }, { "mach_port_deallocate", 3206 }, { "mach_port_get_refs", 3207 }, { "mach_port_mod_refs", 3208 }, { "mach_port_peek", 3209 }, { "mach_port_set_mscount", 3210 }, { "mach_port_get_set_status", 3211 }, { "mach_port_move_member", 3212 }, { "mach_port_request_notification", 3213 }, { "mach_port_insert_right", 3214 }, { "mach_port_extract_right", 3215 }, { "mach_port_set_seqno", 3216 }, { "mach_port_get_attributes", 3217 }, { "mach_port_set_attributes" , 3218 }, { "mach_port_allocate_qos", 3219 }, { "mach_port_allocate_full", 3220 }, { "task_set_port_space", 3221 }, { "mach_port_get_srights", 3222 }, { "mach_port_space_info", 3223 }, { "mach_port_dnrequest_info", 3224 }, { "mach_port_kernel_object", 3225 }, { "mach_port_insert_member", 3226 }, { "mach_port_extract_member", 3227 }, { "mach_port_get_context", 3228 }, { "mach_port_set_context", 3229 }, { "mach_port_kobject", 3230 }, { "mach_port_construct", 3231 }, { "mach_port_destruct", 3232 }, { "mach_port_guard", 3233 }, { "mach_port_unguard", 3234 }, { "mach_port_space_basic_info", 3235 } , { ((void*)0), 3236}};
mig_func_desc host_security_mig [] = {{ "host_security_create_task_token", 600 },\
{ "host_security_set_task_token", 601 }, { NULL, 602}};
mig_func_desc processor_set_mig [] = {
{ "processor_set_statistics", 4000 },\
{ "processor_set_destroy", 4001 },\
{ "processor_set_max_priority", 4002 },\
{ "processor_set_policy_enable", 4003 },\
{ "processor_set_policy_disable", 4004 },\
{ "processor_set_tasks", 4005 },\
{ "processor_set_threads", 4006 },\
{ "processor_set_policy_control", 4007 },\
{ "processor_set_stack_usage", 4008 },\
{ "processor_set_info", 4009 } ,
{ NULL, 4010}};
mig_func_desc iokit_mig [] = {
{ "io_object_get_class", 2800 },\
{ "io_object_conforms_to", 2801 },\
{ "io_iterator_next", 2802 },\
{ "io_iterator_reset", 2803 },\
{ "io_service_get_matching_services", 2804 },\
{ "io_registry_entry_get_property", 2805 },\
{ "io_registry_create_iterator", 2806 },\
{ "io_registry_iterator_enter_entry", 2807 },\
{ "io_registry_iterator_exit_entry", 2808 },\
{ "io_registry_entry_from_path", 2809 },\
{ "io_registry_entry_get_name", 2810 },\
{ "io_registry_entry_get_properties", 2811 },\
{ "io_registry_entry_get_property_bytes", 2812 },\
{ "io_registry_entry_get_child_iterator", 2813 },\
{ "io_registry_entry_get_parent_iterator", 2814 },\
{ "io_service_close", 2816 },\
{ "io_connect_get_service", 2817 },\
{ "io_connect_set_notification_port", 2818 },\
{ "io_connect_map_memory", 2819 },\
{ "io_connect_add_client", 2820 },\
{ "io_connect_set_properties", 2821 },\
{ "io_connect_method_scalarI_scalarO", 2822 },\
{ "io_connect_method_scalarI_structureO", 2823 },\
{ "io_connect_method_scalarI_structureI", 2824 },\
{ "io_connect_method_structureI_structureO", 2825 },\
{ "io_registry_entry_get_path", 2826 },\
{ "io_registry_get_root_entry", 2827 },\
{ "io_registry_entry_set_properties", 2828 },\
{ "io_registry_entry_in_plane", 2829 },\
{ "io_object_get_retain_count", 2830 },\
{ "io_service_get_busy_state", 2831 },\
{ "io_service_wait_quiet", 2832 },\
{ "io_registry_entry_create_iterator", 2833 },\
{ "io_iterator_is_valid", 2834 },\
{ "io_catalog_send_data", 2836 },\
{ "io_catalog_terminate", 2837 },\
{ "io_catalog_get_data", 2838 },\
{ "io_catalog_get_gen_count", 2839 },\
{ "io_catalog_module_loaded", 2840 },\
{ "io_catalog_reset", 2841 },\
{ "io_service_request_probe", 2842 },\
{ "io_registry_entry_get_name_in_plane", 2843 },\
{ "io_service_match_property_table", 2844 },\
{ "io_async_method_scalarI_scalarO", 2845 },\
{ "io_async_method_scalarI_structureO", 2846 },\
{ "io_async_method_scalarI_structureI", 2847 },\
{ "io_async_method_structureI_structureO", 2848 },\
{ "io_service_add_notification", 2849 },\
{ "io_service_add_interest_notification", 2850 },\
{ "io_service_acknowledge_notification", 2851 },\
{ "io_connect_get_notification_semaphore", 2852 },\
{ "io_connect_unmap_memory", 2853 },\
{ "io_registry_entry_get_location_in_plane", 2854 },\
{ "io_registry_entry_get_property_recursively", 2855 },\
{ "io_service_get_state", 2856 },\
{ "io_service_get_matching_services_ool", 2857 },\
{ "io_service_match_property_table_ool", 2858 },\
{ "io_service_add_notification_ool", 2859 },\
{ "io_object_get_superclass", 2860 },\
{ "io_object_get_bundle_identifier", 2861 },\
{ "io_service_open_extended", 2862 },\
{ "io_connect_map_memory_into_task", 2863 },\
{ "io_connect_unmap_memory_from_task", 2864 },\
{ "io_connect_method", 2865 },\
{ "io_connect_async_method", 2866 },\
{ "io_registry_entry_get_registry_entry_id", 2871 },\
{ "io_connect_method_var_output", 2872 },\
{ "io_service_get_matching_service", 2873 },\
{ "io_service_get_matching_service_ool", 2874 },\
{ "io_service_get_authorization_id", 2875 },\
{ "io_service_set_authorization_id", 2876 },\
{ "io_server_version", 2877 },\
{ "io_registry_entry_get_properties_bin", 2878 },\
{ "io_registry_entry_get_property_bin", 2879 },\
{ "io_service_get_matching_service_bin", 2880 },\
{ "io_service_get_matching_services_bin", 2881 },\
{ "io_service_match_property_table_bin", 2882 },\
{ "io_service_add_notification_bin", 2883 } ,
{ "io_registry_entry_get_path_ool", 2884 },
{ "io_registry_entry_from_path_ool", 2885 },
{ NULL, 2886 } };
mig_func_desc mach_vm_mig [] = {
{ "mach_vm_allocate", 4800 },\
{ "mach_vm_deallocate", 4801 },\
{ "mach_vm_protect", 4802 },\
{ "mach_vm_inherit", 4803 },\
{ "mach_vm_read", 4804 },\
{ "mach_vm_read_list", 4805 },\
{ "mach_vm_write", 4806 },\
{ "mach_vm_copy", 4807 },\
{ "mach_vm_read_overwrite", 4808 },\
{ "mach_vm_msync", 4809 },\
{ "mach_vm_behavior_set", 4810 },\
{ "mach_vm_map", 4811 },\
{ "mach_vm_machine_attribute", 4812 },\
{ "mach_vm_remap", 4813 },\
{ "mach_vm_page_query", 4814 },\
{ "mach_vm_region_recurse", 4815 },\
{ "mach_vm_region", 4816 },\
{ "_mach_make_memory_entry", 4817 },\
{ "mach_vm_purgable_control", 4818 },\
{ "mach_vm_page_info", 4819 } ,
{ NULL, 4820}};
mig_func_desc mach_host_mig [] = { { "host_info", 200 }, { "host_kernel_version", 201 }, { "_host_page_size", 202 }, { "mach_memory_object_memory_entry", 203 }, { "host_processor_info", 204 }, { "host_get_io_master", 205 }, { "host_get_clock_service", 206 }, { "kmod_get_info", 207 }, { "host_zone_info", 208 }, { "host_virtual_physical_table_info", 209 }, { "processor_set_default", 213 }, { "processor_set_create", 214 }, { "mach_memory_object_memory_entry_64", 215 }, { "host_statistics", 216 }, { "host_request_notification", 217 }, { "host_lockgroup_info", 218 }, { "host_statistics64", 219 }, { "mach_zone_info", 220 }, { "host_create_mach_voucher", 222 }, { "host_register_mach_voucher_attr_manager", 223 }, { "host_register_well_known_mach_voucher_attr_manager", 224 },
{ "host_set_atm_diagnostic_flag", 225 },\
{ "host_get_atm_diagnostic_flag", 226 },\
{ "mach_memory_info", 227 },\
{ "host_set_multiuser_config_flags", 228 },\
{ "host_get_multiuser_config_flags", 229 },\
{ "host_check_multiuser_mode", 230 }
, { ((void*)0), 231}};
mig_func_desc task_mig [] = { { "task_create", 3400 }, { "task_terminate", 3401 }, { "task_threads", 3402 }, { "mach_ports_register", 3403 }, { "mach_ports_lookup", 3404 }, { "task_info", 3405 }, { "task_set_info", 3406 }, { "task_suspend", 3407 }, { "task_resume", 3408 }, { "task_get_special_port", 3409 }, { "task_set_special_port", 3410 }, { "thread_create", 3411 }, { "thread_create_running", 3412 }, { "task_set_exception_ports", 3413 }, { "task_get_exception_ports", 3414 }, { "task_swap_exception_ports", 3415 }, { "lock_set_create", 3416 }, { "lock_set_destroy", 3417 }, { "semaphore_create", 3418 }, { "semaphore_destroy", 3419 }, { "task_policy_set", 3420 }, { "task_policy_get", 3421 }, { "task_sample", 3422 }, { "task_policy", 3423 }, { "task_set_emulation", 3424 }, { "task_get_emulation_vector", 3425 }, { "task_set_emulation_vector", 3426 }, { "task_set_ras_pc", 3427 }, { "task_zone_info", 3428 }, { "task_assign", 3429 }, { "task_assign_default", 3430 }, { "task_get_assignment", 3431 }, { "task_set_policy", 3432 }, { "task_get_state", 3433 }, { "task_set_state", 3434 }, { "task_set_phys_footprint_limit", 3435 }, { "task_suspend2", 3436 }, { "task_resume2", 3437 }, { "task_purgable_info", 3438 }, { "task_get_mach_voucher", 3439 }, { "task_set_mach_voucher", 3440 }, { "task_swap_mach_voucher", 3441 } ,
{ "task_generate_corpse", 3442 },
{ "task_map_corpse_info", 3443 },
{ "task_register_dyld_image_infos", 3444 },
{ "task_unregister_dyld_image_infos", 3445 },
{ "task_get_dyld_image_infos", 3446 },
{ "task_register_dyld_shared_cache_image_info", 3447 },
{ "task_register_dyld_set_dyld_state", 3448 },
{ "task_register_dyld_get_process_state", 3449 },
{ "task_map_corpse_info_64", 3450 },
{ ((void*)0), 3451}};
mig_func_desc host_priv_mig [] = {
{ "host_get_boot_info", 400 },\
{ "host_reboot", 401 },\
{ "host_priv_statistics", 402 },\
{ "host_default_memory_manager", 403 },\
{ "vm_wire", 404 },\
{ "thread_wire", 405 },\
{ "vm_allocate_cpm", 406 },\
{ "host_processors", 407 },\
{ "host_get_clock_control", 408 },\
{ "kmod_create", 409 },\
{ "kmod_destroy", 410 },\
{ "kmod_control", 411 },\
{ "host_get_special_port", 412 },\
{ "host_set_special_port", 413 },\
{ "host_set_exception_ports", 414 },\
{ "host_get_exception_ports", 415 },\
{ "host_swap_exception_ports", 416 },\
{ "mach_vm_wire", 418 },\
{ "host_processor_sets", 419 },\
{ "host_processor_set_priv", 420 },\
{ "set_dp_control_port", 421 },\
{ "get_dp_control_port", 422 },\
{ "host_set_UNDServer", 423 },\
{ "host_get_UNDServer", 424 },\
{ "kext_request", 425 },
{ NULL, 426}};
//mig_func_desc thread_act_mig [] = { subsystem_to_name_map_thread_act , { NULL, -1}};
mig_func_desc thread_act_mig [] = { { "thread_terminate", 3600 }, { "act_get_state", 3601 }, { "act_set_state", 3602 }, { "thread_get_state", 3603 }, { "thread_set_state", 3604 }, { "thread_suspend", 3605 }, { "thread_resume", 3606 }, { "thread_abort", 3607 }, { "thread_abort_safely", 3608 }, { "thread_depress_abort", 3609 }, { "thread_get_special_port", 3610 }, { "thread_set_special_port", 3611 }, { "thread_info", 3612 }, { "thread_set_exception_ports", 3613 }, { "thread_get_exception_ports", 3614 }, { "thread_swap_exception_ports", 3615 }, { "thread_policy", 3616 }, { "thread_policy_set", 3617 }, { "thread_policy_get", 3618 }, { "thread_sample", 3619 }, { "etap_trace_thread", 3620 }, { "thread_assign", 3621 }, { "thread_assign_default", 3622 }, { "thread_get_assignment", 3623 }, { "thread_set_policy", 3624 }, { "thread_get_mach_voucher", 3625 }, { "thread_set_mach_voucher", 3626 }, { "thread_swap_mach_voucher", 3627 } , { ((void*)0), 3628}};
void dumpMIGSubsystem (mig_subsys *Subsys,
mig_subsys *OurSubSys,
int is64)
{
// Behind us is the server routine
char *func = (char *)((char *)(Subsys) - is64? 8 : 4);
uint64_t routineAddr;
uint64_t *routinePtr;
// if (is64) { serverRoutineAddr = *((uint64_t *) func); }
// else { serverRoutineAddr = *((uint64_t *) func); }
mig_func_desc *mig_subsystem_dumped;
if (Subsys->min == processor_mig[0].num) { mig_subsystem_dumped = processor_mig;};
if (Subsys->min == processor_set_mig[0].num) { mig_subsystem_dumped = processor_set_mig;};
if (Subsys->min == host_security_mig[0].num) { mig_subsystem_dumped = host_security_mig;};
if (Subsys->min == iokit_mig[0].num) { mig_subsystem_dumped = iokit_mig;};
if (Subsys->min == mach_port_mig[0].num) { mig_subsystem_dumped = mach_port_mig;};
if (Subsys->min == mach_vm_mig[0].num) { mig_subsystem_dumped = mach_vm_mig;};
if (Subsys->min == mach_host_mig[0].num) { mig_subsystem_dumped = mach_host_mig;};
if (Subsys->min == host_priv_mig[0].num) { mig_subsystem_dumped = host_priv_mig;};
if (Subsys->min == task_mig[0].num) { mig_subsystem_dumped = task_mig;};
if (Subsys->min == thread_act_mig[0].num) { mig_subsystem_dumped = thread_act_mig;};
if (mig_subsystem_dumped)
{
int f = 0;
int adv = (is64? 8 : 4);
routinePtr = (char *) Subsys + ((is64 ?4 : 5) * adv);
int last = OurSubSys->max;
// printf("Max is %d , last is %d\n", Subsys->max, last);
// last - Subsys->min - 1);
if (last > Subsys->max) last = Subsys->max;
while (mig_subsystem_dumped[f].num < last ) //
// while (mig_subsystem_dumped[f].num > -1)
{
if (is64) routineAddr = *routinePtr;
else { routineAddr = *((uint32_t *) routinePtr);}
if(wantJToolOut)
{
char output[1024];
// sprintf(output,"0x%llx:__X%s\n", routineAddr, mig_subsystem_dumped[f].name);
sprintf(output,"__X%s", mig_subsystem_dumped[f].name);
addSymbolToCache(output, routineAddr,"");
// write (jtoolOutFD, output, strlen(output));
}
else
printf ("\t__X%s: 0x%llx (%d)\n", mig_subsystem_dumped[f].name, routineAddr, mig_subsystem_dumped[f].num);
f++;
int skip = mig_subsystem_dumped[f].num - mig_subsystem_dumped[f-1].num ;
routinePtr = ((char *) routinePtr) + ((is64?5:6) * skip *adv);
}
// If the last num we got to is not the subsystem's max, warn
if (last < Subsys->max-1)
{
printf("\tWarning: This kernel is newer than joker is (%d < %d)!\n",
last, Subsys->max-1);
}
}
else {printf("Unknown MIG system (%d-%d)\n", Subsys->min , Subsys->max);}
} // dumpMIG
void dumpMachTraps(char *mach, int is64)
{
int i;
int thumb = 0;
uint64_t kernInvalid = *((uint64_t *) mach);
if (!is64) kernInvalid = *((uint32_t *) mach);
if (mach) printf ("Kern invalid should be %llx. Ignoring those\n", kernInvalid);;
for (i = 0; i < 128; i++)
{
uint64_t addr = * ((int64_t *) (mach + 4*8 *i));
uint32_t addr32 = * ((int *) (mach + 3 * 4*i));
if (is64)
{
}
else {
if (addr == *((int *) (mach + 4))) continue;
if ((addr % 4) == 1) { addr--; thumb++; }
if ((addr % 4) == -3) { addr--; thumb++; }
if (addr % 4) { thumb = "-1"; }
}
if ( addr && (addr != kernInvalid)) {
if (wantJToolOut)
{
char output[1024];
sprintf (output, "_%s", mach_syscall_name_table[i]);
addSymbolToCache (output, addr, NULL);
// sprintf(output,"%llx:%s:_Mach_Trap_%d\n", addr, mach_syscall_name_table[i], i);
// write (jtoolOutFD, output, strlen(output));
}
else
printf ("%3d %-40s %llx %s\n", i, mach_syscall_name_table[i], is64 ?addr: addr32, (thumb? "T": "-"));
}
else
{ /* suppress, but also warn if it's not kern_invalid for whatever reason */
//printf("%llx Trap #%d should be kern_invalid, but isn't\n", addr, i);
}
} // end for < 128 ..
} // dumpMachTraps
int g_Verbose = 0;
char *MachOLookupSymbolAtAddress(uint64_t, unsigned char *File);
extern int disassARMInstr (unsigned char *Loc, uint64_t Address, disassembled_instruction *returned);
extern int disassARM64Instr (uint32_t *Loc, uint64_t Address, disassembled_instruction *returned);
extern int doInstr (disassembled_instruction *Instr, int Print);
uint32_t MachOGetSectionOffset(void *File, char *Name);
int symbolicateKextStubs (char *MMapped, int Size, char *Name, int Split)
{
// Name only actually used for debugging...
// @TODO: Could actually locate stub section by flags, rather than hard coded name. Machlib can do that.
char *splitKext = NULL;
char *d = getenv("JOKER_DIR");
if (!d) d = "/tmp";
char buf[1024];
char filename[1024];
snprintf(buf, 1024, "%s/%s.kext",d, Name);
if (Split)
{
int fd = open (buf, O_RDWR);
if (fd <0) { fprintf(stderr, "Unable to open %s.. can't symbolicate\n",
buf); return -1;}
struct stat stbuf;
int rc = fstat(fd, &stbuf);
int filesize = stbuf.st_size;
splitKext = mmap(NULL,
filesize, // size_t len,
PROT_READ, // int prot,
MAP_SHARED | MAP_FILE, // int flags,
fd, // int fd,
0); // off_t offset);
//zzzz
segments = processFile(splitKext, filesize, getKernelCacheArch(), 0, 0,0);
if (g_jdebug)
fprintf(stderr,"This is a split kext.. %d (0x%x) bytes\n", filesize,filesize);
}
uint32_t stubs_off = MachOGetSectionOffset( (unsigned char *) MMapped, "__TEXT.__stubs");
uint32_t stubs_size = MachOGetSectionSize ((unsigned char *) MMapped, "__TEXT.__stubs");
uint64_t stubs_addr = MachOGetSectionAddr ((unsigned char *) MMapped, "__TEXT.__stubs");
if (!stubs_size)
{
stubs_off = MachOGetSectionOffset( (unsigned char *) MMapped, "__TEXT_EXEC.__stubs");
stubs_size = MachOGetSectionSize ((unsigned char *) MMapped, "__TEXT_EXEC.__stubs");
stubs_addr = MachOGetSectionAddr ((unsigned char *) MMapped, "__TEXT_EXEC.__stubs");
}
char *oh_my_got = "__DATA.__got";
uint32_t got_off = MachOGetSectionOffset( (unsigned char *) MMapped, oh_my_got);
uint32_t got_size = MachOGetSectionSize ((unsigned char *) MMapped, oh_my_got);
uint64_t got_addr = MachOGetSectionAddr ((unsigned char *) MMapped, oh_my_got);
if (!got_size)
{
oh_my_got = "__DATA_CONST.__got";
got_off = MachOGetSectionOffset( (unsigned char *) MMapped, oh_my_got);
got_size = MachOGetSectionSize ((unsigned char *) MMapped, oh_my_got);
got_addr = MachOGetSectionAddr ((unsigned char *) MMapped, oh_my_got);
}
int companionFileFD = 0;
if (!stubs_size) { fprintf(stderr,"Unable to find __TEXT.__stubs in kext %s. Won't symbolicate\n",Name); return (-1);}
// Otherwise, we're here, and found the stubs. Iterate over stubs section, instruction by instruction,
// Looking for following pattern:
// fffffff00405e664 d0000010 ADRP X16, 2 ; ->R16 = 0xfffffff004060000
// fffffff00405e668 f9400210 LDR X16, [X16, #0] ; -R16 = *(R16 + 0) = *(0xfffffff004060000) = 0xfffffff007578748 ... ?..
// fffffff00405e66c d61f0200 BR X16 ; 0xfffffff007578748
// And at the BR, attempt to see what it is we are branching to (based on X16's value).
// We can actually use our disassembly callback here, disassembling three instructions at a time
// But this shows another usage of machlib
// register_disassembled_register_call_callback (function_identifier);
int pos = 0;
if (g_jdebug)
fprintf(stderr,"Symbolicating stubs for %s from off 0x%x\n", Name,stubs_off);
while (pos < stubs_size)
{
// Take the scenic route and actually disassemble the instructions.
// This is longer, but certainly safer and more accurate should AAPL
// decide to ever change the format. It would have been simpler to
// iterate over the stubs themselves (in __DATA.__got) since compiler
// emits them in corresponding order..
disassembled_instruction inst1;
disassembled_instruction inst2;
disassembled_instruction inst3;
disassARM64Instr ((splitKext ? splitKext : MMapped) + stubs_off + pos, stubs_addr+pos, &inst1);
disassARM64Instr ((splitKext ? splitKext : MMapped) + stubs_off + pos + 4, stubs_addr + pos + 4, &inst2);
disassARM64Instr ((splitKext ? splitKext : MMapped) + stubs_off + pos + 8, stubs_addr + pos + 8, &inst3);
if (strcmp(inst1.mnemonic, "ADRP") ||
strcmp(inst2.mnemonic, "LDR") ||
strcmp(inst3.mnemonic, "BR")) {
fprintf(stderr,"0x%x\n", *((uint32_t *) ((splitKext ? splitKext : MMapped) + stubs_off + pos)));
fprintf(stderr,"Warning: Error in disassembly - got %s,%s,%s..\n",
inst1.mnemonic, inst2.mnemonic, inst3.mnemonic);
}
else {
// Can't use this just yet. @TODO
// We can also verify that the registers in all instructions match,
// (i.e. not necessarily R16, but that we ADRP, LDR and BR to same
// register. But that's unnecessary at this point.
//doInstr(&inst1, 0);
//doInstr(&inst2, 0);
//doInstr(&inst3, 0);
uint64_t reg = inst1.immediate;
uint64_t off = inst2.immediate;
uint64_t stub = reg + off;
struct symtabent *sym = NULL;
// if (g_jdebug)
char res[1024];
if (stub >= got_addr && stub <= got_addr + got_size)
{
// Can actually just go by offset in got.. don't need to get
// offset, because we know it's inside section
uint64_t *resolved = getPointerToAddr (stub);
if (splitKext || !resolved)
{
off = stub - got_addr + got_off;
resolved = ( (uint64_t *)(splitKext + off));
}
if (g_jdebug) fprintf(stderr,"Stub at 0x%llx (offset 0x%x) is 0x%llx\n", stub, off, *resolved);
if (resolved)
sym = getClosestSymbol (*resolved, // unsigned long long addr,
kernelSymTable); // struct symtabent *symtab);
if (!sym) { fprintf(stderr,"Unable to resolve kernel symbol at %llx (this is fine if it's a symbol from another kext)\n", *resolved);
sprintf(res,"unknown_0x%llx", resolved);
}
else
{
if (g_jdebug) fprintf (stderr, "Symbol at 0x%llx is %s (0x%llx)\n",
resolved, sym->sym, sym->ptr);
strcpy(res, sym->sym);
}
if ( !companionFileFD)
{
// time to open companionFileFD
sprintf (filename, "%s.ARM64.%s", buf, getUUID(mmapped));
companionFileFD = open (filename, O_RDWR|O_CREAT);
if(companionFileFD< 0) {
fprintf(stderr,"Unable to open companion file %s.. Not symoblicating this kext\n");
return 0;
}
// else fprintf(stderr,"Opened companion file %s\n", filename);
fchmod(companionFileFD, 0666);
}
sprintf(buf, "%llx:%s.stub\n", stubs_addr + pos,res);
// if (jtoolOutFD > 0) write (jtoolOutFD, buf, strlen(buf));
write (companionFileFD, buf, strlen(buf));
}
else
fprintf(stderr,"Warning: Resolved stub in %s falls outside GOT (0x%llx not in 0x%llx-0x%llx)\n", Name,stub, got_addr, got_addr+got_size);
sym =NULL;
} //
pos+=12;
}
char *segName = NULL;
xnu3757_and_later = MachOGetSection("__DATA_CONST.__const");
if (xnu3757_and_later ) {
segName = "__DATA_CONST.__const";
jtoolOutFD = companionFileFD;
int foundPolicy = doPolicyOps(splitKext, segName);
jtoolOutFD = 0;
}
if (pos != stubs_size)
{
fprintf(stderr,"Warning: Disassembly left some unhandled instructions!\n");
}
close(companionFileFD);
fprintf(stderr,"Symbolicated stubs to %s\n", filename);
if (filename && (strstr(filename, "sandbox")))
{
if (xnu3757_and_later) {
fprintf(stderr,"This is the sandbox.kext - Trying to get seatbelt-profiles\n");
if (doSandboxOperationNames(splitKext, "__TEXT.__cstring"))
{
fprintf(stderr,"Can't get profiles with sandbox operation names\n");
}
doSandboxProfiles (splitKext, "__TEXT.__const");
}
}
return 0;
}
int doKextract (char *mmapped, char *Name, int Size)
{
uint32_t magic = MH_MAGIC;
uint32_t magic64 = MH_MAGIC_64;
uint32_t *magicAtAddress = (uint32_t *) mmapped;
//printf("KEXTRACTING FROM %p\n", mmapped);
if ((*magicAtAddress != MH_MAGIC) &&
(*magicAtAddress != MH_MAGIC_64))
{
fprintf(stderr,"No magic at extraction address (0x%x)!\n", *magicAtAddress );
return -5;
}
// if (((int)mmapped) & 0xfff) return 0;
if (g_jdebug) fprintf(stderr,"kextracting %s from %p\n", Name,mmapped);
// Extract - create a file, and seek from here to the next Magic
// YES, this will fail on the last kext. But hey - you can fix the
// code easily. I just never need this for anything but AMFI, Sandbox, etc..
char *nextMagic =mmapped + 0x1000;
while (memcmp (nextMagic, magicAtAddress, sizeof(uint32_t)) != 0)
{
nextMagic += 0x10;
// printf("Next %p\n",nextMagic);
}
Size = nextMagic - mmapped;
unsigned char *kextCopy = (unsigned char *) malloc (Size);
memcpy (kextCopy, mmapped, Size);
uint32_t __DATA__data_off = MachOGetSectionOffset( kextCopy, "__DATA.__data");
segments = processFile(kextCopy,Size, getKernelCacheArch(), 0, 0,0);
uint32_t __DATA__data_size = MachOGetSectionSize( kextCopy, "__DATA.__data");
uint32_t __DATA__CONST_const_off = MachOGetSectionOffset( kextCopy, "__DATA_CONST.__const");
// Ok - we're here, so..
int split = 0;
char *d = getenv("JOKER_DIR");
if (!d) d = "/tmp";
char dumped[1024];
snprintf(dumped, 1024, "%s/%s.kext",d, Name);
int fd = open (dumped, O_RDWR | O_CREAT | O_TRUNC);
printf("Writing kext out to %s\n",dumped);
if (fd <0) { printf ("Unable to create file %s!\n", Name); return -2; }
fchmod (fd, 0666);
if (__DATA__CONST_const_off ||
xnu3757_and_later ) {
split = 1;
if (g_jdebug) fprintf(stderr,"This is a split kext. There's other parts, too...:\n");
// iterate over the segments
extern struct load_command *loadCommands[];
int lc = 0;
int writ = 0;
for (lc = 0; loadCommands[lc]; lc++)
{
// Segments hold both segments/sections. We can tell difference by value of SEGMENT_COMMAND
int bw = 0;
struct segment_command_64 *sc = (struct segment_command_64 *)loadCommands[lc];
if (sc->cmd == LC_SEGMENT_64)
{
if (g_jdebug)
fprintf(stderr, "Segment: %s at addr: 0x%llx-0x%llx, offset 0x%llx-0x%llx (Size: 0x%x)\n",sc->segname, sc->vmaddr,sc->vmaddr+ sc->vmsize, sc->fileoff, sc->fileoff+ sc->filesize, sc->filesize);
int off = lseek (fd, 0, SEEK_CUR);
char *from = mmapped + sc->fileoff;
if (sc->vmaddr >= prelink_data_data_addr &&
sc->vmaddr <= prelink_data_data_addr + prelink_data_data_size)
{
fprintf(stderr,"Workaround for Apple's offset bug in the kernelcache!\n");
from = getKernelCacheStart() + prelink_data_data_offset + (sc->vmaddr - prelink_data_data_addr);
}
bw= write (fd, from, sc->filesize);
if (bw < 0) { perror("write");
// this is a BUG in the sc->fileoff!
// have to go by vmaddr
fprintf(stderr, "Unable to write out segment %s of %x bytes (0x%llx) from offset 0x%x-0x%x! Failing!!!\n", sc->segname, sc->filesize, sc->vmaddr,sc->fileoff, sc->fileoff + sc->filesize);
exit(1);
}
// bw= write (fd, (getKernelCacheStart() + sc->fileoff), sc->filesize);
/// fprintf(stderr," BW is 0x%x\n", bw); }
else {
if (g_jdebug) fprintf(stderr, "Written out segment %s (0x%llx) from offset 0x%x\n", sc->segname, sc->vmaddr,sc->fileoff); bw = 0; }
if (g_jdebug)
{
// fprintf(stderr,"..Written %x bytes (%llx) to offset 0x%x (0x%x)\n", bw,writ, *((((unsigned char *)mmapped) + sc->fileoff)), off);
}
writ+=bw;
int patch = sc->fileoff - off;
if(g_jdebug)
fprintf (stderr,"Patching load command %p in kextCopy %p from 0x%x to 0x%x\n", sc, kextCopy, sc->fileoff, off);
sc->fileoff = off;
// Should also do for segments:
int sect = 0;
struct section_64 *sec64 = (struct section_64 *)(sc + 1);
//int extra;
for (sect =0 ; sect < sc->nsects; sect ++)
{
// extra = sec64->addr & 0xfff;
if (sec64->offset )
{
if (g_jdebug)
{
fprintf(stderr,"Patching section %s from 0x%llx-0x%llx to 0x%llx.. (%llx bytes)\n",
sec64->sectname, sec64->offset, sec64->offset + sec64->size,
(sec64->offset-patch ), patch);
}
}
if (sec64->offset) {
/* char *extraPad = calloc (extra, 1);
write (fd, extraPad,extra);
free(extraPad);
// writ+= extra;
*/
sec64->offset = (sec64->offset-patch );
off += sec64->size;
}
sec64++;
}
off = lseek (fd, 0, SEEK_CUR);
// Pad to a page boundary!
if (off) {
int pad = 0x1000 - ( off % 0x1000);
char *padding = calloc ( pad,1);
if (g_jdebug) fprintf(stderr,"Padding by 0x%x bytes\n", pad);
write (fd, padding, pad);
writ+=pad;
free (padding);
}
} // lc_segment
} // end for
// Now patch header:
if (g_jdebug) fprintf(stderr,"Applying patched header...\n");
int rc = lseek (fd, 0,0);
rc = write (fd, kextCopy, 4096);
if (g_jdebug) fprintf(stderr," written %d bytes\n", writ);
}
else {
// Simple case - we have all the kext.
write (fd, mmapped, nextMagic - mmapped);
}
close (fd);
// Want to get the Kext symbol Stubs now!
symbolicateKextStubs(mmapped, nextMagic -mmapped,Name, split);
//printf ("Extracted %s\n", Name);
return (0);
}
void doKexts(char *mmapped, char *kextractThis, int method)
{
int kexts = 0;
// To do the kexts, we load the dictionary of PRELINK_INFO
char *kextPrelinkInfo = (char *) malloc(1000000);
char *kextNamePtr;
char *kextLoadAddr;
char kextName[2560];
char loadAddr[24];
char *temp = kextPrelinkInfo;
char *loadAddrPtr;
char *prelinkAddr;
extern char *g_SegName;
g_SegName = "__PRELINK_INFO";
struct section *segPI = MachOGetSection((unsigned char *) "__PRELINK_INFO.__info");
struct section_64 *segPI64 = (struct section_64 *) segPI;
struct section *segPT = MachOGetSection((unsigned char *) "__PRELINK_TEXT.__text");
struct section_64 *segPT64 = (struct section_64 *) segPT;
if (!segPT64 || ! segPI64) return;
if (kextractThis) { if (g_jdebug) fprintf (stderr, "Attempting to kextract %s\n", kextractThis);};
uint64_t offsetCorrection = (is64? segPT64->addr - segPT64->offset : segPT->addr - segPT->offset);
int offset = (is64 ? segPI64->offset : segPI->offset);
// PRELINK_TEXT will look something link this:
// Mem: 0x8044f000-0x80eee000 File: 0x00406000-0x00ea5000
kextPrelinkInfo = (char *) (mmapped + offset);
temp = kextPrelinkInfo;
if (!temp)
{
printf("Unable to find __PRELINK_INFO\n");
return;
}
kextNamePtr = strstr(temp,"CFBundleName</key>");
if (method == 1)
// This is EXTREMELY quick and dirty, but I can't find a way to load a CFDictionary
// directly from XML data (and be cross platform), so it will do for now..
//
// ... and it's getting dirtier still but at least I fixed the ID=...
// Definitely clean this up sometime... especially now that INFO is used..
//
while (kextNamePtr) {
temp = strstr(kextNamePtr, "</string>");
prelinkAddr = strstr(kextNamePtr, "_PrelinkExecutableLoadAddr");
if (!prelinkAddr)
{
// prelinkAddr= strstr(kextNamePtr, "_PrelinkExecutable");
if (!prelinkAddr){
fprintf(stderr,"Can't determine kext load addr.. This might be a really old kernelcache. Max - trying method #2 for you..\n");
break;
}
}
loadAddrPtr = strstr(prelinkAddr, "0x");
// overflow, etc..
memset(kextName, '\0', 2560);
// fix for ID=..
char *idFix = NULL;
// idFix = strstr(kextNamePtr, ">");
idFix = strnstr(kextNamePtr, "ID=\"", temp - kextNamePtr);
if (!idFix) idFix = kextNamePtr + 26;
else idFix = strstr(idFix + 5,"\">") + 2;
strncpy (kextName, idFix , temp - idFix);
// temp = strstr(loadAddrPtr, "</integer>");
char * endOfLoadAddr = strchr(loadAddrPtr, '<');
memset(loadAddr, '\0',24);
strncpy (loadAddr, loadAddrPtr, 11 +(is64? 7 : -1));
if (!kextractThis) printf("%s: %s ", loadAddr, kextName);
temp += 10;
kextNamePtr = strstr(temp, "CFBundleIdentifier");
if (kextNamePtr)
{
temp = strstr(kextNamePtr,"</string>");
memset(kextName,'\0',256);
idFix = strnstr(kextNamePtr, "ID=\"", temp - kextNamePtr);
if (!idFix) idFix = kextNamePtr + 32;
else idFix = strstr(idFix + 5,"\">") + 2;
strncpy(kextName, idFix, temp - idFix);
if (!kextractThis) printf ("(%s)\n", kextName);
else
{
//printf("FOUND %s\n", kextName);
if ((strcmp(kextractThis , "all") == 0) || strstr (kextractThis, kextName))
{
uint64_t addr;
int rc = sscanf (loadAddr, "%llx", &addr);
if (!rc) { fprintf (stderr, "Unable to parse load address %x!\n", loadAddr); exit (3);}
printf ("Found %s at load address: %llx, offset: %x\n",kextName,addr, addr - offsetCorrection);
(doKextract (mmapped + (addr - offsetCorrection), kextName,0));
// zzzzz
}
}
}
kextNamePtr = strstr(temp,"CFBundleName</key>");
kexts++;
}
if (g_jdebug) fprintf(stderr,"--METHOD: %d\n", method);
if (method == 1 && kexts > 50) {
if (!kextractThis) fprintf(stderr,"Got %d kexts %s\n", kexts, (kexts > 200 ? "(yowch!)" : "")); return ;
}
else
{
fprintf(stderr,"Number of kexts way too small.. Trying method #2\n", kexts);
}
// Method #2 - applicable for kernel dumps
fprintf(stderr, "Unable to get kexts from __PRELINK_INFO.. going straight for __PRELINK_TEXT\n");
if (!segPT) { printf ("This is weird. Can't get offset of __PRELINK_TEXT. Giving up..\n"); return;}
offset = (is64 ? segPT64->offset : segPT->offset);
int size = (is64 ? segPT64->size : segPT->size);
uint32_t machOSig = (is64? 0xfeedfacf : 0xfeedface);
int prev=0;
int k = 1;
int i = 0;
for (i = 0;
i < size;
i+=0x1000)
{
if (memcmp (&mmapped[offset + i], &machOSig, sizeof(uint32_t)) == 0)
{
if (!prev) { prev = offset +i;}
else
{
int kextSize = (offset +i - prev);
struct mach_header_64 *mh = (struct mach_header_64 *)((&mmapped[offset + i]));
if (mh->filetype != 0xb) // change to kext const
{
fprintf(stderr,"Got Mach-O magic but not a kext. Continuing\n");
continue;
}
if (g_jdebug) fprintf(stderr,"IN KEXT %d (%d/%d), kextSize: %d\n", k,
i,size, kextSize);
if (kextSize + i > size ) { fprintf(stderr,"kext size reported is greater than remaining dump bytes.. skipping\n"); i++; continue; }
char *kextID = identifyKextNew( &mmapped[prev], kextSize, mmapped);
// fallback to older method
if (!kextID) kextID = identifyKext( &mmapped[prev], kextSize);
if (!kextID) kextID = "unrecognized.or.unhandledyet.Please.Report.Me";
// process
if (kextractThis)
{
if ((strcmp(kextractThis, "all") == 0)
|| strstr (kextID, kextractThis))
{
char *kextfile = strdup (kextID);
char *space = strchr (kextfile, '(');
if (space) space[0] = '\0';
char dumped[1024];
doKextract (mmapped +prev, kextfile,0);
/*
snprintf(dumped, 1024, "%s/%d.%s.kext",d, k, kextfile);
printf("Writing kext out to %s\n",dumped);
int fd = open (dumped, O_WRONLY | O_TRUNC | O_CREAT);
if (fd < -1) { perror (dumped); }
else
{
fchmod(fd, 0600);
write (fd, &mmapped[prev], (offset +i - prev));
close(fd);
}
*/
free(kextfile);
}
}
else { // just print
printf("%d: %s at 0x%x (%x bytes)\n", k, kextID, prev, (offset +i - prev));
}
// if (kextractThis && strstr (kextID, kextractThis)) { exit (doKextract (mmapped + prev, kextractThis)); }
prev=offset+i;
k++;
}
}
} // end for
}
struct sysctl_oid {
uint32_t ptr_oid_parent;
uint32_t ptr_oid_link;
int oid_number;
int oid_kind;
uint32_t oid_arg1;
int oid_arg2;
uint32_t ptr_oid_name;
uint32_t ptr_oid_handler;
uint32_t ptr_oid_fmt;
uint32_t ptr_oid_descr; /* offsetof() field / long description */
int oid_version;
int oid_refcnt;
};
struct sysctl_oid_64 {
uint64_t ptr_oid_parent;
uint64_t ptr_oid_link;
int oid_number;
int oid_kind;
uint64_t oid_arg1;
int oid_arg2;
uint64_t ptr_oid_name;
uint64_t ptr_oid_handler;
uint64_t ptr_oid_fmt;
uint64_t ptr_oid_descr; /* offsetof() field / long description */
int oid_version;
int oid_refcnt;
};
typedef struct sysctlNamespace {
char *name;
uint64_t addr ;
int resolved;
} sysctlNamespace_t;
sysctlNamespace_t sysctlNamespaces[256];
int sysctlNamespaceCount =0;
void addSysctlNamespace (uint64_t addr, char *name)
{
// fprintf(stdout," Adding: 0x%llx - %s\n", addr, name);
sysctlNamespaces[sysctlNamespaceCount].name = name;
sysctlNamespaces[sysctlNamespaceCount].addr = addr;
sysctlNamespaces[sysctlNamespaceCount].resolved= (strstr(name,"0x")) ? 0 : 1;
sysctlNamespaceCount++;
}
char *getSysctlNamespaceName(uint64_t Addr)
{
int ns = 0;
for (ns = 0; ns <sysctlNamespaceCount; ns++)
{
if (sysctlNamespaces[ns].addr == Addr)
return sysctlNamespaces[ns].name;
}
return (NULL);
}
char *sysctlName (char *mmapped, uint64_t sysctlPtr)
{
char *name = malloc(1024);
name[0] = '\0';
uint32_t sysCtlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr);
if (sysCtlOffsetInFile == -1) { strcat (name, "?"); return (name); }
struct sysctl_oid *sysctl = (mmapped + sysCtlOffsetInFile);
struct sysctl_oid_64 *sysctl64 = (struct sysctl_oid_64 *) sysctl;
char *parent = MachOLookupSymbolAtAddress((is64 ? sysctl64->ptr_oid_parent : sysctl->ptr_oid_parent),
(unsigned char *)mmapped);
struct section *sec = MachOGetSection ((unsigned char *)"__DATA.__sysctl_set");
struct section_64 *sec64 = (struct section_64 *)sec;
if (!sec64) { fprintf(stderr,"Unable to get section!\n");}
// printf("PARENT: %llx, sec: %llx-%llx\n", sysctl64->ptr_oid_parent, sec64->addr, sec64->addr+sec64->size);;
char *parentName = getSysctlNamespaceName (is64 ? sysctl64->ptr_oid_parent: sysctl->ptr_oid_parent);
if (parentName) {
strcpy(name, parentName);
if (parentName[0]) strcat(name, ".");
}
if (!name)
{
char parentAddr[20];
sprintf (parentAddr,"0x%llx", (is64? sysctl64->ptr_oid_parent : sysctl->ptr_oid_parent));
strcpy(name, parentAddr);
strcat(name,".");
}
uint32_t sysctlNameOffsetInFile = MachOGetFileOffsetOfAddr (is64 ?sysctl64->ptr_oid_name : sysctl->ptr_oid_name);
if (sysctlNameOffsetInFile == -1) {strcat (name,"?"); return (name);}
strcat (name, mmapped + sysctlNameOffsetInFile);
return (name);
} //sysctlName
void doSysctls(char *mmapped, int is64)
{
// assume section 32 for now..
struct section *sec = MachOGetSection ((unsigned char *)"__DATA.__sysctl_set");
struct section_64 *sec64 = (struct section_64 *)sec;
if (sec) {
int numsysctls = (is64? sec64->size : sec->size) / (is64? sizeof(uint64_t) : sizeof(uint32_t));
int s = 0;
uint64_t offset = (is64 ? sec64->offset : sec->offset);
uint64_t addr = (is64 ? sec64->addr : sec->addr);
uint64_t size = (is64 ? sec64->size : sec->size);
printf ("Dumping sysctl_set from 0x%llx (offset in file: 0x%llx), %x sysctls follow:\n", addr,offset, numsysctls);
int i = 0;
for (i = 0; i < 2; i++)
{
// First pass: get namespaces - works better in reverse!
for (s = numsysctls - 1; s >=0; s--)
{
uint64_t sysctlPtr;
if (is64) {
sysctlPtr= *((uint64_t *)(mmapped + offset + s * sizeof(uint64_t)));
}
else sysctlPtr= *((uint32_t *)(mmapped + offset+ s * sizeof(uint32_t)));
uint64_t sysctlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr);
// sanity check, anyone?
if (!is64 && (sysctlOffsetInFile > sec->offset + sec->size)) { printf("(%llx outside __sysctl_set)\n", sysctlPtr); continue;};
if (is64 && (sysctlOffsetInFile > offset + size)) { printf("(%llx is outside __sysctl_set)\n",sysctlPtr); continue;};
struct sysctl_oid *sysctl = (mmapped + sysctlOffsetInFile);
struct sysctl_oid_64 *sysctl64 = (struct sysctl_oid_64 *)sysctl;
uint32_t sysctlDescInFile = MachOGetFileOffsetOfAddr (is64 ? sysctl64->ptr_oid_descr : sysctl->ptr_oid_descr);
uint32_t sysctlFormatInFile = MachOGetFileOffsetOfAddr (is64 ? sysctl64->ptr_oid_fmt : sysctl->ptr_oid_fmt);
char *sysctlFormat = "?";
if (sysctlFormatInFile != -1) { sysctlFormat = mmapped + sysctlFormatInFile;}
uint64_t handler = is64 ? sysctl64->ptr_oid_handler: sysctl->ptr_oid_handler;
if (!handler) {
char *nsname = sysctlName(mmapped, sysctlPtr);
if ((i == 0)) {
if (strstr(nsname,"kperf"))
{
addSysctlNamespace(is64?sysctl64->ptr_oid_parent : sysctl->ptr_oid_parent, "");
}
}
else
addSysctlNamespace (is64 ?sysctl64->oid_arg1 : sysctl->oid_arg1, nsname);
}
}
} // 2 passes
// Second pass:
for (s = 0 ; s < numsysctls; s++)
{
uint64_t sysctlPtr;
if (is64) {
sysctlPtr= *((uint64_t *)(mmapped + offset + s * sizeof(uint64_t)));
}
else sysctlPtr= *((uint32_t *)(mmapped + offset+ s * sizeof(uint32_t)));
uint64_t sysctlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr);
//printf ("0x%llx: ", sysctlPtr , sysctlOffsetInFile);
// sanity check, anyone?
if (!is64 && (sysctlOffsetInFile > sec->offset + sec->size)) { printf("(outside __sysctl_set)\n"); continue;};
if (is64 && (sysctlOffsetInFile > offset + size)) { printf("(outside __sysctl_set)\n"); continue;};
struct sysctl_oid *sysctl = (mmapped + sysctlOffsetInFile);
struct sysctl_oid_64 *sysctl64 = (struct sysctl_oid_64 *)sysctl;
uint32_t sysctlDescInFile = MachOGetFileOffsetOfAddr (is64 ? sysctl64->ptr_oid_descr : sysctl->ptr_oid_descr);
uint32_t sysctlFormatInFile = MachOGetFileOffsetOfAddr (is64 ? sysctl64->ptr_oid_fmt : sysctl->ptr_oid_fmt);
char *sysctlFormat = "?";
if (sysctlFormatInFile != -1) { sysctlFormat = mmapped + sysctlFormatInFile;}
uint64_t handler = is64 ? sysctl64->ptr_oid_handler: sysctl->ptr_oid_handler;
if (!handler) continue; // covered these in first pass...
printf ("0x%llx: ", sysctlPtr , sysctlOffsetInFile);
char *name = sysctlName(mmapped,sysctlPtr);
uint64_t arg1 = is64 ?sysctl64->oid_arg1 : sysctl->oid_arg1;
printf ( (is64 ?
"%s\tDescription: %s\n\t\tHandler: 0x%llx\n\t\tFormat: %s\n\t\tParent: 0x%llx\n\t\tArg1: %llx\n\t\tArg2: 0x%llx\n"
:"%s\tDescription: %s\n\t\tHandler: 0x%x\n\t\tFormat: %s\n\t\tParent: 0x%x\n\t\tArg1: %x\n\t\tArg2: 0x%x\n"),
name,
mmapped + sysctlDescInFile,
handler,
sysctlFormat,
is64 ? sysctl64->ptr_oid_parent: sysctl->ptr_oid_parent,
arg1,
is64 ?sysctl64->oid_arg2 : sysctl->oid_arg2);
if ((arg1 > 0x80000000) && wantJToolOut)
{
char output[1024];
addSymbolToCache(name, arg1, NULL);
// sprintf (output, "0x%llx:%s\n", arg1, name);
// write (jtoolOutFD, output, strlen(output));
}
}
}
} // doSysctls
void doMachTraps(char *mmapped, int xnu32xx)
{
char *zeros = calloc (24,1);
struct section_64 *segDC = MachOGetSection((unsigned char *)"__DATA.__const");
if (!segDC)segDC = MachOGetSection((unsigned char *)"__CONST.__constdata");
if (!segDC) { segDC = MachOGetSection((unsigned char *)"__DATA_CONST.__const"); }
if (!segDC) { fprintf(stderr,"No __DATA.__const or __CONST??!\n"); return; }
struct section *segDC32 = (struct section *) segDC;
int offset = (is64 ? segDC->offset : segDC32->offset);
int adv = (is64 ? 8 : 4);
int i = 0 ;
char *mach = NULL;
char *pos = mmapped + offset;
uint64_t segAddr = (is64 ? segDC->addr : segDC32->addr);
uint32_t segOffset = (is64 ? segDC->offset : segDC32->offset);
int segSize = (is64 ? segDC->size : segDC32->size);
int skip = (is64 ? 3 : 5);
for (i = 0; i< segSize; i+= adv)
// Ugly, I know, but works in both 32 and 64-bit cases
if (( ((memcmp(&pos[i], zeros, skip*adv) == 0) &&
(memcmp(&pos[i+(skip+1)*adv], zeros, skip*adv) == 0) &&
(memcmp(&pos[i+2*(skip+1)*adv], zeros, skip*adv) == 0) &&
(memcmp(&pos[i+3*(skip+1)*adv], zeros, skip*adv) == 0) &&
(memcmp(&pos[i+4*(skip+1)*adv], zeros, skip *adv) == 0) &&
( (*((uint64_t *) &pos[i-adv])) && *((int64_t *) &pos[i+skip*adv]))))
)
{
printf ("mach_trap_table offset in file/memory (for patching purposes): 0x%x/%llx\n", segOffset + i , segAddr + i);
mach = &pos[i] - adv;
dumpMachTraps (mach, is64);
break;
}
} // doMachTraps
void doMIG(char *mmapped, int xnu32xx)
{
struct section_64 *segDC = MachOGetSection((unsigned char *)"__DATA.__const");
if (!segDC)segDC = MachOGetSection((unsigned char *)"__CONST.__constdata");
if (!segDC) { segDC = MachOGetSection((unsigned char *)"__DATA_CONST.__const"); }
if (!segDC) { fprintf(stderr,"No __DATA.__const?!\n"); return ; }
struct section *segDC32 = (struct section *) segDC;
int offset = (is64 ? segDC->offset : segDC32->offset);
int adv = (is64 ? 8 : 4);
int i = 0 ;
char *pos = mmapped + offset;
uint32_t subsysMachVM = 0;
uint32_t subsysTask = 0;
uint64_t segAddr = (is64 ? segDC->addr : segDC32->addr);
int segSize = (is64 ? segDC->size : segDC32->size);
for (i = 0; i< segSize; i+= adv)
{
if (memcmp(&pos[i], &mach_vm_subsys, 8) ==0) {
printf("mach_vm_subsystem is @0x%llx!\n", segAddr +i - adv);
subsysMachVM = offset + i;
dumpMIGSubsystem((mig_subsys * )(pos +i),&mach_vm_subsys, is64);
}
if (memcmp(&pos[i], &thread_act_subsys, 8) ==0) {
printf("thread_act_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&thread_act_subsys, is64);
}
if (memcmp(&pos[i], &mach_port_subsys, 8) ==0) {
printf("mach_port_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&mach_port_subsys, is64);
}
if (memcmp(&pos[i], (xnu32xx ? &is_iokit_subsys_9 : &is_iokit_subsys), 4) ==0) {
printf("is_iokit_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&is_iokit_subsys, is64);
}
if (memcmp(&pos[i], (&processor_subsys), 8) ==0) {
printf("processor_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&processor_subsys, is64);
}
if (memcmp(&pos[i], (&processor_set_subsys), 8) ==0) {
printf("processor_set_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&processor_set_subsys, is64);
}
if (memcmp(&pos[i], (&host_security_subsys), 8) ==0) {
printf("host_security_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&host_security_subsys, is64);
}
if (memcmp(&pos[i], &host_priv_subsys, 8) ==0) {
printf("host_priv_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&host_priv_subsys, is64);
}
if (memcmp(&pos[i], (&mach_host_subsys_9), 4) ==0) {
printf("mach_host_subsystem is @0x%llx!\n", segAddr +i - adv);
dumpMIGSubsystem((mig_subsys * )(pos +i),&mach_host_subsys_9, is64);
}
if (memcmp(&pos[i], &task_subsys, 4) ==0) {
printf("task_subsystem is @0x%llx!\n", segAddr +i - adv);
subsysTask = offset + i;
dumpMIGSubsystem((mig_subsys * )(pos +i),&task_subsys,is64);
}
}
} //doMig
uint64_t look_for_inst (char *Func, uint32_t Inst, char *Where, int Size, uint64_t addr, int jtoolOutFD)
{
// fprintf(stderr,"Looking for %s ...\n", Func);
char *found = memmem (Where, Size, &Inst, 4);
if (found) {
fprintf(stderr,"Found %s at offset 0x%x, Addr: 0x%llx\n",
Func,
found - (Where),
addr + (found - Where));
addSymbolToCache (Func,addr + (found - Where), NULL);
//char output[1024];
// sprintf(output, "%llx:_%s\n", addr + (found - Where), Func);
//write (jtoolOutFD, output, strlen(output));
}
return (addr + (found - Where));
} // look_for_inst
struct compHeader {
char sig[8] ; // "complzss"
uint32_t unknown; // Likely CRC32. But who cares, anyway?
uint32_t uncompressedSize;
uint32_t compressedSize;
uint32_t unknown1; // 1
};
char *tryLZSS (char *compressed, int *filesize)
{
struct compHeader *compHeader = strstr(compressed,"complzss");
if (!compHeader) return (NULL);
fprintf(stderr,"Feeding me a compressed kernelcache, eh? That's fine, now. I can decompress! ");
if (!g_dec) fprintf(stderr,"(Type -dec _file_ if you want to save to file)!");
fprintf(stderr,"\nCompressed Size: %d, Uncompressed: %d. Unknown: 0x%x, Unknown 1: 0x%x\n",
ntohl(compHeader->compressedSize),
ntohl(compHeader->uncompressedSize),
ntohl(compHeader->unknown),
ntohl(compHeader->unknown1));
int sig[2] = { 0xfeedfacf, 0x0100000c };
// But check for KPP:
char *found = NULL;
if ((found = memmem(mmapped + 0x2000, *filesize, "__IMAGEEND", 8)))
{
// the 0xfeedfacf before that is kpp..
char *kpp = memmem (found - 0x1000, 0x1000, sig, 4);
if (kpp) {
fprintf(stderr,"btw, KPP is at %lld (0x%x)", kpp -mmapped, kpp-mmapped);
int out= open ("/tmp/kpp", O_TRUNC | O_CREAT |O_WRONLY);
if (out < 0) { fprintf(stderr,"But I can't save it for you\n"); exit(3);}
fchmod (out, 0600);
write (out, kpp, *filesize - (kpp - mmapped));
close(out);
fprintf(stderr,"..And I saved it for you in /tmp/kpp\n");
}
}
// For lzss I'm using code verbatim from BootX, which, like Apple, is ripped from
// Haruhiko Okumura (CompuServe 74050,1022, if anyone can find him to thank him from me!)
// Code is: from BootX-81//bootx.tproj/sl.subproj/lzss.c
//
// The code is in the public domain, Stefan, I'm not stealing anything.
// Unlike people who never credit OpenSSL in their Apps ;-)
//
// I trust AAPL to report sizes and not give me a heap overflow now ;-)
char *decomp = malloc (ntohl(compHeader->uncompressedSize));
// find kernel 0xfeedfa... If I ever support 32-bit, I'll need faCE or faCF..
int MachOSig = 0xfeedfacf;
char *feed = memmem (mmapped+64, 1024, &MachOSig, 3);
if (!feed) { fprintf(stderr,"Can't find kernel here.. Sorry. LZSS this yourself\n"); exit(5);}
else {fprintf(stderr,"Got kernel at %d\n", feed -mmapped); }
feed--;
int rc = decompress_lzss(decomp,
feed,
ntohl(compHeader->compressedSize));
if (rc != ntohl(compHeader->uncompressedSize)) {
fprintf(stderr,"Expected %d bytes ... Got %d bytes. Aborting\n",
ntohl(compHeader->uncompressedSize), rc);
}
*filesize = rc;
if (g_dec)
{
int fd = open ("/tmp/kernel", O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) { fprintf(stderr,"Can't write decompressed kernel to /tmp/kernel...\n");}
else
{
fchmod (fd, 0600);
write (fd, decomp, ntohl(compHeader->uncompressedSize));
close(fd);
}
}
return (decomp);
} // compLZSS
int main (int argc, char **argv)
{
int xnu24xx = 0;
int xnu27xx = 0;
int xnu32xx = 0;
int xnu37xx = 0;
int iOS = 0;
g_jdebug = (getenv ("JDEBUG") != NULL);
int fd;
int rc;
struct stat stbuf;
int filesize;
filename = argv[1];
struct mach_header *mh;
int i,j ;
int magic;
char *sysent = NULL;
uint64_t sysentAddr;
char *mach = NULL;
char *xnuSig = NULL;
int showUNIX = 0, showMach = 0;
int suppressEnosys = 1;
int suppressOld = 1;
int showVersion = 0;
int showKexts = 0;
int showSysctls = 0;
char *kextName = NULL;
int kextract = 0;
FILE *jtoolOutFile = NULL;
if (!filename) { fprintf (stderr,"Usage: joker [-j] [-MmaSsKk] _filename_\n", argv[0]);
fprintf (stderr," _filename_ should be a decrypted iOS kernelcache, or kernel dump. Tested on ARMv7/s 3.x-9.3, and ARM64 through 10.0.1GM\n\n");
fprintf (stderr," -m: dump Mach Traps and MIG tables (NEW)\n");
fprintf (stderr," -a: dump everything\n");
fprintf (stderr," -k: dump kexts\n");
fprintf (stderr," -K: kextract [kext_bundle_id_or_name_shown_in_-k|all] to JOKER_DIR or /tmp\n");
fprintf (stderr," -S: dump sysctls\n");
fprintf (stderr," -s: dump UNIX syscalls\n");
fprintf (stderr," -j: Jtool compatible output (to companion file)\n");
fprintf (stderr ,"\n-dec: Decompress kernelcache to /tmp/kernel (complzss only at this stage)\n");
fprintf (stderr,"\nKernels not included. Get your own dump or decrypted kernel from iPhoneWiki, or Apple itself (as of iOS 10b1! Thanks, guys!)\n");
fprintf (stderr, "\n3.0 with MACF Policies, stub symbolication, SPLIT KEXTS, Sandbox Profiles (beta, collections only at this point) , kpp and (coming soon) IOUserClients!\nCompiled on " __DATE__ "\n");
#ifdef HAVE_LZSS
fprintf(stderr,"\nContains code from Haruhiko Okumura (CompuServe 74050,1022) from BootX-81//bootx.tproj/sl.subproj/lzss.c\n");
#endif
exit(0);
}
if (filename[0] == '-') { showVersion = (filename[1] == 'v' ? 1 : 0 ) ; filename = argv[2]; };
int arg = 0;
for (arg= 1; arg < argc -1 ; arg++)
{
if (strcmp (argv[arg], "-k") ==0 ) { showKexts = 1; showUNIX =0; showMach = 0;};
if (strcmp (argv[arg], "-K") ==0 ) { kextract = 1; kextName = argv[arg+1]; filename = argv[argc-1]; showUNIX =0; showMach = 0;};
if (strcmp (argv[arg], "-S") ==0 ) { showSysctls = 1; filename = argv[2]; };
if (strcmp (argv[arg], "-a") ==0 ) { showSysctls = 1; showKexts=1;filename = argv[2]; showUNIX =showMach = 1;};
if (strcmp (argv[arg], "-m") ==0 ) { showMach = 1; };
if (strcmp (argv[arg], "-s") ==0 ) { showUNIX = 1; };
if (strcmp (argv[arg], "-dec") ==0 ) { g_dec = 1;}
if (strcmp (argv[arg], "-j") ==0 ) { wantJToolOut = 1; }
}
filename = argv[argc-1];
rc = stat(filename, &stbuf);
if (rc == -1) { perror (filename); exit (1); }
filesize = stbuf.st_size;
fd = open (filename, O_RDONLY);
if (fd < 0) { perror ("open"); exit(2);}
mmapped = mmap(NULL,
filesize, // size_t len,
PROT_READ, // int prot,
MAP_SHARED | MAP_FILE, // int flags,
fd, // int fd,
0); // off_t offset);
if (!mmapped) { perror ("mmap"); exit(3);}
int xnu =0;
// Examine first
retry:
mh = (struct mach_header *) (mmapped);
if (mh->cputype == 12 || mh->cputype == 16777228) /* ARM */ { iOS = 1; }
else { iOS = 0;}
switch (mh->magic)
{
case 0x496d6733: // IMG3
fprintf(stderr,"Not handling IMG3. 32-bit is passe', man\n");
exit(0);
case 0xFEEDFACE:
/* Good, this is a Mach-O */
// This is an ARM binary. Good.
setKernelCacheArch(CPU_TYPE_ARM);
setKernelCacheStart(mmapped);
segments= processFile((unsigned char *) mmapped,filesize, CPU_TYPE_ARM, 0, 0,0);
break;
case 0xFEEDFACF:
setKernelCacheArch(CPU_TYPE_ARM64);
setKernelCacheStart(mmapped);
segments =processFile((unsigned char *) mmapped,filesize, CPU_TYPE_ARM64, 0, 0,0);
is64++;
break;
case 0xbebafeca:
fprintf (stderr, "This is an Intel FAT binary, but I can't handle these yet\n");
exit(5);
default:
{
// Could be we were fed a kernelcache
int origSize = filesize;
char *mem = tryLZSS (mmapped, &filesize);
if (!mem)
{
fprintf(stderr, "I have no idea how to handle a file with a magic of 0%x\n", magic); exit(6);
}
munmap(mmapped, origSize);
mmapped = mem;
goto retry;
}
}
// Got segments - get PLK_DATA_
prelink_data_data_addr = MachOGetSectionAddr (mmapped, "__PRELINK_DATA.__data");
prelink_data_data_offset = MachOGetSectionOffset (mmapped, "__PRELINK_DATA.__data");
prelink_data_data_size = MachOGetSectionSize (mmapped, "__PRELINK_DATA.__data");
struct source_version_command *svc = (struct source_version_command *) findLoadCommand ((unsigned char *) mmapped, LC_SOURCE_VERSION,NULL);
struct uuid_command *uuidc = (struct uuid_command *) findLoadCommand ((unsigned char *) mmapped, LC_UUID,NULL);
if (filesize > 2000000)
{
xnu = 1;
}
if (xnu && svc && (svc->version >> 40) >= 2423)
{
if ((svc->version >> 40) >= 3789)
{
xnu37xx = 1;
xnu3757_and_later = 1;
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ? 64: 32),
(iOS ? "iOS 10.x (b7+)": "OS X (Sorry, \"MacOS\" 10.12b7)"));
}
else
if ((svc->version >> 40) >= 3777)
{
xnu37xx = 1;
xnu3757_and_later = 1;
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ? 64: 32),
(iOS ? "iOS 10.x (b3+)": "OS X (Sorry, \"MacOS\" 10.12b3)"));
}
else if ((svc->version >> 40) >= 3757)
{
xnu37xx = 1;
xnu3757_and_later = 1;
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ? 64: 32),
(iOS ? "iOS 10.x (b2+)": "OS X (Sorry, \"MacOS\" 10.12)"));
}
else
if ((svc->version >> 40) >= 3705)
{
xnu37xx = 1;
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ? 64: 32),
(iOS ? "iOS 10.x": "OS X (Sorry, \"MacOS\" 10.12)"));
}
else
if ((svc->version >> 40) >= 3216)
{
xnu32xx = 1;
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ? 64: 32),
(iOS ? "iOS 9.x": "OS X 10.11"));
}
else
if ((svc->version >> 40) >= 2780)
{
fprintf(stdout, "This is a %d-bit kernel from %s, or later ",
(is64 ?64: 32),
(iOS ? "iOS 8.x": "OS X 10.10"));
xnu27xx = 1;
}
else
xnu24xx = 1;
}
//else this is a kext
if (!xnu) { fprintf(stdout, "This is %s\n" , identifyKextNew(mmapped, filesize,mmapped));}
else if (svc) {
fprintf (stdout, "(%ld.%d.%d.%d.%d)\n",
(long) ((svc->version) >> 40),
(int) (svc->version >> 30) & 0x000003FF ,
(int) (svc->version >> 20) & 0x000003FF,
(int) (svc->version >> 10) & 0x000003FF,
(int) (svc->version) & 0x000003FF);
}
else {
fprintf(stdout, "(No LC_SOURCE_VERSION.. your dump may be corrupt.. or this might be a really old kernel!)\n");
}
if (wantJToolOut) {
extern char *g_fileName;
g_fileName = filename;
jtoolOutFD = openCompanionFileName(mmapped,2);
if (jtoolOutFD) fprintf(stderr,"Opening companion file\n");
}
uint32_t numSyms;
struct symtabent *symTable = MachOGetSymbols (mmapped, filesize, &numSyms); // uint32_t *numsyms)
//struct symtabent *symTable = NULL;
if (symTable)
{
if (g_jdebug) fprintf(stderr,"Got %d syms from kernel\n", numSyms);
kernelSymTable = symTable;
}
else
{
if (xnu) printf("Unable to get symbols from SYMTAB (fine for dumps)\n");
}
int wantDis =1;
if (wantDis){
if (wantJToolOut)
{
char *seg = "__TEXT.__text";
uint64_t addr = MachOGetSectionAddr (mmapped, seg);
if (!addr) {
seg = "__TEXT_EXEC.__text";
addr = MachOGetSectionAddr (mmapped, seg);
}
uint32_t offset = MachOGetSectionOffset (mmapped, seg);
uint32_t size = MachOGetSectionOffset (mmapped, seg);
uint32_t SMC_inst = 0xd4000223;
uint64_t SMC_inst_addr = look_for_inst ("_secure_monitor", SMC_inst, mmapped + offset, size, addr,jtoolOutFD);
uint32_t start_cpu = 0xd5034fdf; // DAIFSet ... #15
uint64_t start_cpu_addr = look_for_inst ("_start_cpu", start_cpu, mmapped + offset, size, addr,jtoolOutFD);
register_disassembled_function_call_callback (function_identifier);
printf("Auto-Disassembling %s from 0x%llx to find rest..\n", seg, addr);
printf("This may take a little while, but you only need to do this once\n");
disassemble(mmapped ,
addr ,
segments,
DISASSEMBLE_QUIET,
DISASSEMBLE_END_OF_SECTION);
}
}
//printf ("Entry point is 0x%llx..", getEntryPoint());
for (i = 0;
i < filesize-50;
i++)
{
if (!xnu) break;
if (!xnuSig && memcmp(&mmapped[i], XNUSIG, strlen(XNUSIG)) == 0)
{
/* Could actually get the version from LC_SOURCE_VERSION... */
char buf[80];
xnuSig = mmapped + i + strlen(XNUSIG);
memset(buf, '\0', 80);
strncpy (buf, xnuSig, 40);
// The signature we get is from a panic, with the full path to the
// xnu sources. Remove the "/" following the XNU version. Because the
// memory is mmap(2)ed read only, we have to copy this first.
char *temp = strstr(buf, "/");
if (temp) {
*temp = '\0';
}
xnuSig = buf;
if (showVersion) {
printf ("This is XNU %s\n", xnuSig);
exit(0);
}
}
if (memcmp(&mmapped[i], ARMExcVector, 8) == 0)
{
sysentAddr = findAddressOfOffset(i-28);
if (showUNIX) printf("ARM Exception Vector is at file offset @0x%x (Addr: 0x%llx)\n", i-28, findAddressOfOffset(i-28));
}
int ARM64_exception_vector_base = 0xd5385201;
if (memcmp(&mmapped[i], &ARM64_exception_vector_base, 4) == 0)
{
if (! ((i - 8) & 0xfff))
{
printf("ARM64 Exception Vector is at file offset @0x%x (Addr: 0x%llx)\n", i - 8, findAddressOfOffset(i-8));
if (jtoolOutFD > 0)
{
char output[1024];
uint64_t addr = findAddressOfOffset(i-8);
sprintf (output, "0x%llx:ARM64ExceptionVectorBase\n0x%llx:Synchronous_handler\n0x%llx:IRQ_vIRQ_handler\n0x%llx:FIQ_vFIQ\n0x%llx:SError_vSError\n",
addr, addr, addr +0x80, addr +0x100,
addr +0x180);
/*
0x4100003000:synchronous_handler
0x4100003080:IRQ/vIRQ
0x4100003100:FIQ/vFIQ
0x4100003180:SError/vSError
0x4100003200:synchronous_SPx
0x4100003280:IRQ/vIRQ_SPx
0x4100003300:FIQ/vFIQ_SPx
0x4100003380:SError/vSError_SPx
0x4100003400:synchronous_Lower_EL
0x4100003480:IRQ/vIRQ_Lower_EL
0x4100003500:FIQ/vFIQ_SPx
0x4100003580:SError/vSError_Lower_EL
*/
write (jtoolOutFD, output, strlen(output));
output[0] = '\0';
}
}
}
if (xnu27xx || xnu32xx || xnu37xx) {
if ((memcmp(&mmapped[i], SIG1_IOS8X, 8) ==0) &&
(memcmp(&mmapped[i+0x18], SIG1_AFTER_0x18_IOS8X,8) == 0))
{
sysentAddr = findAddressOfOffset(i-0x10);
printf("Found iOS 8+ sysent table @%x (Addr: 0x%llx)\n",i - 0x10, findAddressOfOffset(i-0x10));
sysent = mmapped + i - 0x10;
}
} // xnu27xx
else {
if (memcmp(&mmapped[i], SIG1, 20) == 0)
{
if (memcmp(&mmapped[i+24], SIG1_SUF, 16) == 0)
{
sysent = mmapped + i - 24 ;
// if (xnuSig) break;
}
}
if ( (memcmp(&mmapped[i], SIG1_IOS7X, 16) == 0) &&
(memcmp(&mmapped[i+20], SIG2_IOS7X, 16) ==0) &&
(memcmp(&mmapped[i+40], SIG1_IOS7X, 16) ==0))
{
sysent = mmapped + i - 24 ;
// if (xnuSig) break;
}
} // ! iOS 8
// Can and should actually rewrite this to a) read from the __const section and b) be 32/64-bit agnostic
} // end for i..
if (xnu && !xnuSig) {
fprintf (stderr, "This doesn't seem to be a kernel! Continuing anyway..\n");
}
if (!sysent && is64)
{
struct section_64 *segDC = MachOGetSection((unsigned char *) "__DATA.__const");
if (!segDC)
{
segDC = MachOGetSection((unsigned char *)"__CONST.__constdata");
}
if (!segDC) { segDC = MachOGetSection((unsigned char *)"__DATA_CONST.__const"); }
if (!segDC) { fprintf(stderr,"No __DATA.__const or CONST?!\n"); return 0; }
int offset = (is64 ? segDC->offset : segDC->offset);
int i = 0 ;
char *pos = mmapped + offset;
int adv = 8;
for (i = 0; i < segDC->size ; i+=adv)
{
if (memcmp(&pos[i], SIG_SYSCALL_3, 8) == 0)
{
// if (memcmp (&pos[i] + 0x18, SIG_SYSCALL_3,8) == 0) printf("DOUBLE BINGO\n");
sysent = pos +i - 0x10 - (3 * 0x18 );
sysentAddr = segDC->addr + i - 0x10 - (3 * 0x18 );
// Can double check since same sig is also at + 0x18 from here..
// Bingo!
break;
}
}
}
if (showMach) doMachTraps(mmapped, xnu32xx || xnu37xx);
if (showMach) doMIG(mmapped, xnu32xx||xnu37xx);
if (showUNIX && sysent)
{
if (memcmp(&mmapped[i], "syscall\0exit", 12) == 0)
{
// syscall_names = &mmapped[i];
printf ("Syscall names are @%x\n", i);
}
printf("Syscalls at address 0x%llx\n", sysentAddr);
if (is64)
printf ("Sysent offset in file (for patching purposes): %lx\n", (sysent - mmapped));
uint64_t enosys, old = 0;
if (suppressEnosys)
{
enosys = (xnu27xx || xnu32xx|| xnu37xx) ?
* ((int *) (sysent + 0x60)) :
* ((int *) (sysent + 20 + 24*4));
old = (xnu27xx || xnu32xx||xnu37xx) ?
* ((int *) (sysent + 0x60+ 30*12)) :
* ((int *) (sysent + 20 + 24*4));
if (is64) {
// enosys is at syscall 0
enosys = *(uint64_t *) sysent;
// old is at syscall 8
old = *(((uint64_t *) sysent) + (8 * 0x3));
}
printf ("Suppressing enosys (%llx) and old (%llx)\n", enosys,old);
}
int maxsyscall = SYS_MAXSYSCALL;
if (xnu24xx) maxsyscall = SYS_MAXSYSCALL_7;
if (xnu27xx) maxsyscall = SYS_MAXSYSCALL_8;
if (xnu32xx ) maxsyscall = SYS_MAXSYSCALL_9;
if (xnu37xx ) maxsyscall = SYS_MAXSYSCALL_10;
for (i = 0; i< maxsyscall ; i++)
{
int suppress = 0;
int thumb = 0;
int jump = (xnu24xx? 20 : 24);
if (xnu27xx || xnu32xx|| xnu37xx) jump = 12;
if (is64) jump = 0x18;
uint64_t addr = * ((int *) (sysent + 20 + jump*i));
if (xnu27xx || xnu32xx || xnu37xx) addr = * ((int *) (sysent + jump*i));
if (is64) { addr = *((uint64_t *) (sysent + jump*i));}
if ((addr == enosys) || addr == old) suppress =1;
if (!is64) {
if ((addr % 4) == 1) { addr--; thumb++; }
if ((addr % 4) == -3) { addr--; thumb++; }
}
if (!suppress)
{
if (wantJToolOut) {
char output[1024];
sprintf (output, "_%s", syscall_names[i]);
addSymbolToCache (output, addr, NULL);
//
// sprintf (output, "0x%llx:_%s\n", addr, syscall_names[i]);
// write (jtoolOutFD, output, strlen(output));
}
else
{
if (is64)
{printf ("%d.. %-20s 0x%llx\n", i,syscall_names[i], addr);}
else
printf ("%d. %-20s %x %s\n", i,syscall_names[i], addr, (thumb? "T": "-"));
}
} // !suppress`
// skip to next post null byte - unfortunately wont work due to optimizations
// putting some of the system call name strings elsewhere (in their first appearance
// in the binary)
// for (; *syscall_names; syscall_names++);
// syscall_names++;
}
} // showUNIX
// Do KEXTs
void *seg = MachOGetSection((unsigned char *) "__DATA.__const");
if (!seg) seg = MachOGetSection((unsigned char *) "__CONST.__constdata");
if (!seg) { seg = MachOGetSection((unsigned char *)"__DATA_CONST.__const"); }
if (!seg)
{
fprintf(stderr,"Unable to find const section. This shouldn't be happening.. continuting anyway, but can't look for sysent/mach_trap_table\n");
}
else
{
}
_sysctls:
if (showSysctls) doSysctls(mmapped,is64);
_kexts:
_kextraction:
if (kextract || showKexts) {
int meth = 2;
if (xnu37xx)
{
if(g_jdebug) fprintf(stderr,"This is a XNU 37xx or later kernel, so defaulting to method #1 (__PRELINK_INFO)\n");
meth =1;
if (getenv("METH2")) meth=2;
}
doKexts(mmapped, kextName,meth);
}
if(jtoolOutFD){
char *cfn = getCompanionFileName(mmapped);
printf("Output written to %s in Jtool-compatible format. Run jtool with --jtooldir . or set JTOOLDIR=\n",
cfn);
// merge cache syms
dumpSymbolCacheToFile(jtoolOutFD);
close(jtoolOutFD);
}
}
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
// Barebones purple_console clone - constructed with some reverse and forward engineering.
//
// No copyright or license. Feel free to share. Comments/Feedback welcome @http://newosxbook.com/forum/
//
//
// To compile: gcc jurpleConsole.c -o jc -framework CoreFoundation -framework MobileDevice
//
// You will need to make MobileDevice.framework "public" - simplest way is to copy it (-pR)
// to Frameworks/ from PrivateFrameworks
#ifdef DEBUG
#define Dprintf fprintf
#else
#define Dprintf(...)
#endif
// These are obviously internal to Apple. The void * is likely some AMDDeviceRef * or something like that
// But since it's opaque anyway, just void * it..
extern int AMDeviceConnect (void *device);
extern int AMDeviceValidatePairing (void *device);
extern int AMDeviceStartSession (void *device);
extern int AMDeviceStopSession (void *device);
extern int AMDeviceDisconnect (void *device);
extern int AMDServiceConnectionReceive(void *device, char *buf,int size, int );
extern int AMDeviceSecureStartService(void *device,
CFStringRef serviceName, // One of the services in lockdown's Services.plist
int flagsButZeroIsFineAlso,
void *handle);
extern int AMDeviceNotificationSubscribe(void *, int , int , int, void **);
struct AMDeviceNotificationCallbackInformation {
void *deviceHandle;
uint32_t msgType;
} ;
#define BUFSIZE 128 // This is used by purpleConsole. So I figure I'll use it too.
void doDeviceConnect(void *deviceHandle)
{
int rc = AMDeviceConnect(deviceHandle);
int fd;
char buf[BUFSIZE];
if (rc) {
fprintf (stderr, "AMDeviceConnect returned: %d\n", rc);
exit(1);
}
rc = AMDeviceValidatePairing(deviceHandle);
if (rc)
{
fprintf (stderr, "AMDeviceValidatePairing() returned: %d\n", rc);
exit(2);
}
rc = AMDeviceStartSession(deviceHandle);
if (rc)
{
fprintf (stderr, "AMStartSession() returned: %d\n", rc);
exit(2);
}
void *f;
rc = AMDeviceSecureStartService(deviceHandle,
CFSTR("com.apple.syslog_relay"), // Any of the services in lockdown's services.plist
0,
&f);
if (rc != 0)
{
fprintf(stderr, "Unable to start service -- Rc %d fd: %p\n", rc, f);
exit(4);
}
rc = AMDeviceStopSession(deviceHandle);
if (rc != 0)
{
fprintf(stderr, "Unable to disconnect - rc is %d\n",rc);
exit(4);
}
rc = AMDeviceDisconnect(deviceHandle);
if (rc != 0)
{
fprintf(stderr, "Unable to disconnect - rc is %d\n",rc);
exit(5);
}
memset (buf, '\0', 128); // technically, buf[0] = '\0' is enough
while ((rc = AMDServiceConnectionReceive(f,
buf,
BUFSIZE, 0) > 0))
{
// Messages are read to the buf (Framework uses a socket descriptor)
// For syslog_relay, messages are just the raw ASL output (including
// kernel.debug), which makes it easy for this sample code.
// Other services use bplists, which would call for those nasty
// CFDictionary and other APIs..
printf("%s", buf);
fflush(NULL);
memset (buf, '\0', 128);
}
} // end doDevice
void callback(struct AMDeviceNotificationCallbackInformation *CallbackInfo)
{
void *deviceHandle = CallbackInfo->deviceHandle;
Dprintf (stderr,"In callback, msgType is %d\n", CallbackInfo->msgType);
switch (CallbackInfo->msgType)
{
case 1:
Dprintf(stderr, "Device %p connected\n", deviceHandle);
doDeviceConnect(deviceHandle);
break;
case 2:
Dprintf(stderr, "Device %p disconnected\n", deviceHandle);
break;
case 3:
Dprintf(stderr, "Unsubscribed\n");
break;
default:
Dprintf(stderr, "Unknown message %d\n", CallbackInfo->msgType);
}
}
int main (int argc, char **argv)
{
void *subscribe;
int rc = AMDeviceNotificationSubscribe(callback, 0,0,0, &subscribe);
if (rc <0) {
fprintf(stderr, "Unable to subscribe: AMDeviceNotificationSubscribe returned %d\n", rc);
exit(1);
}
CFRunLoopRun();
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h> // for fchmod
#include <mach/mach.h>
#include <mach/task.h>
#include <ctype.h>
// Sample memory dumper for processes (PID=...) or kernel proper (checkrain)
// Compile with gcc-arm64 ....c -o ....
//
// and don't forget to sign:
// JENTS=com.apple.private.security.container-required,task_for_pid-allow,com.apple.system-task-ports jtool2 --sign /tmp/memdump
//
// As an exercise, you might want to implement mach_vm_write - to turn this into a kernel/process memory patcher :-)
// CAVEAT: Reading "wrong" addresses (e.g. kernel base, unslid) can and probably will lead to a panic since
// mach_vm_read() has no sanity checks in it.
//
// Instead of including <mach/mach_vm.h> which would #error unsupported
// I include the relevant functions inlined here...
kern_return_t mach_vm_read
(
vm_map_t target_task,
mach_vm_address_t address,
mach_vm_size_t size,
vm_offset_t *data,
mach_msg_type_number_t *dataCnt
);
void hexDump (void *buf, int len, uint64_t addr);
/////
int main (int argc, char **argv){
if (argc < 3) {
fprintf(stderr,"Usage: %s <address> <size>\n",
argv[0]);
exit(0);
}
// Assume that our target is a valid task port which we obtain
// per checkra1n via task_for_pid
// Also rig the pid here. (if you want this as an arbitrary user mode
// reader - just get pid as argument, or..
pid_t pid = 0;
// hack - you might want pid as an arg...
if (getenv("PID")) { pid = atoi(getenv("PID")); }
mach_port_t target_task = MACH_PORT_NULL;
kern_return_t kr = task_for_pid (mach_task_self(), // ipc_space
pid, // 0 or target
&target_task);
if (kr != KERN_SUCCESS) {
fprintf(stderr,"Our method of getting target for PID %d doesn't work. Sorry\n", pid);
return (1);
}
uint64_t addr ;
uint32_t len ;
len = atoi(argv[2]);
int rc = sscanf (argv[1], "0x%llx", &addr);
if (rc !=1 ) { fprintf(stderr," HELLO! Usage, man! USAGE!!!!\n"); return 2;}
fprintf(stderr,"Reading %d bytes from 0x%llx\n", len, addr);
vm_offset_t data;
mach_msg_type_number_t dataCnt = 0;
// FOR KERNEL MEMORY, we might need to dump a pointer that is UNSLID
// Or one which is SLID.
// #1: how do we tell difference?
// (use getenv("SLIDE");
if (pid == 0 && (getenv("SLIDE") != NULL)) {
// #2: how do we get slide?
// - in MacOS - easy - we discussed SEVERAL KASLR leaks
// - in *OS - most JBs will leave the slide value somehow.
// CHECKRAIN ONLY!
// Luca made the astute observation that there is a task_info (..DYLD_...)
// Which - on the kernel task - would make no sense
// That's where he puts the slide value.
struct task_dyld_info dyld_info = {};
uint64_t slide = 0;
mach_msg_type_number_t count = sizeof(struct task_dyld_info);
kern_return_t kr = task_info (target_task, // task_inspect_t task,
TASK_DYLD_INFO, (task_info_t) &dyld_info, &count);
if (kr != KERN_SUCCESS) {
fprintf(stderr,"Can't get slide - are you using checkrain?\n");
// too risky. Fail
return(3);
}
else {
slide = dyld_info.all_image_info_size;
printf("SLIDE is 0%llx\n", slide);
addr+=slide;
}
} // pid 0
kr = mach_vm_read (target_task, // vm_map_t target_task,
addr, // mach_vm_address_t address,
len, // mach_vm_size_t size,
&data, // vm_offset_t *data,
&dataCnt); //mach_msg_type_number_t *dataCnt
// Dump memory!
if (getenv("RAW") != NULL) {
char filename[128];
sprintf(filename, "/tmp/mem.pid.%d.0x%llx-0x%llx.bin",
pid,
addr, addr +len);
int fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY);
fchmod (fd, 0644);
int rc = write (fd, (void *)data, dataCnt);
printf("RC of write was %d\n", rc);
close(fd);
}
else
hexDump ((void *)data, dataCnt, addr);
return 0;
}
// Simple hexdump (from disarm.c)
void hexDump(void *Mem, int Len, uint64_t Addr)
{
unsigned char *Buf = (unsigned char *)Mem;
int i = 0;
char formatStr[80];
strcpy(formatStr,"%06llx");
int rowBegin = 0;
for (i = 0; i < Len; i++)
{
if ((i % 16) == 0) {
if (i>0) {
// Have to print previous line
int j = 0;
printf (" |");
for (j = i - 16; j < i; j++)
{
if (isprint(Buf[j])) printf("%c", Buf[j]);
else printf(".");
}
printf ("|\n");
}
if (1) { fprintf(stdout, "%08llx ", //formatStr,
Addr +i); }
rowBegin = i;
}
int rowPos = i%16;
if ((rowPos == 0) || (rowPos == 8))
{
uint64_t Address = *((uint64_t *) (Buf + rowBegin + rowPos)) ;
if ((Address & 0xFFFFFFF000000000) == 0xFFFFFFF000000000)
{
// Kernel address
printf(" %s0x%llx%s ",
"", Address, "");
i +=7; // and +1 when we leave loop
continue;
}
}
// If we're here, we want to print normally
if (rowPos == 8) printf (" ");
printf ("%02x ", Buf[i]);
} // end for
if ((i % 16) == 0) {
if (i>0) {
// Have to print previous line
int j = 0;
printf (" |");
for (j = i - 16; j < i; j++)
{
if (isprint(Buf[j])) printf("%c", Buf[j]);
else printf(".");
}
printf ("|\n");
}
}
}
#!/bin/bash
#
## vars
# Main
/usr/sbin/dtrace -n '
inline uint64_t a = '$1';
inline int l = '$2';
#pragma D option quiet
dtrace:::BEGIN { tracemem(a,l); exit(0); }
dtrace:::ERROR { exit(1); } '
#include <mach/mach.h>
#include <dispatch/dispatch.h>
// These would go in ktrace.h, if Apple ever provided it:
typedef struct trace_point {
uint64_t timestamp;
uintptr_t arg1, arg2, arg3, arg4, arg5;
uint32_t debugid;
#if defined(__LP64__)
uint32_t cpuid;
uintptr_t unused;
#endif
} *ktrace_event_t;
typedef void *ktrace_session_t;
ktrace_session_t ktrace_session_create(void);
char *ktrace_name_for_eventid(ktrace_session_t s, uint32_t eventid);
void ktrace_events_all(ktrace_session_t S, void (^)(ktrace_event_t tp));
int main (int argc,char **argv) {
// Example: Simple kdebug client. Note this is intentionally
// unfiltered - you can use ktrace_events_class, .._single, .._range
// and other more specific calls in a similar manner.
ktrace_session_t s= ktrace_session_create();
ktrace_events_all(s, ^(ktrace_event_t tp) {
printf("IN TRACE Code %x, Arg1: %llx Name %s\n",
tp->debugid, tp->arg1,
ktrace_name_for_eventid(s,tp->debugid));
});
int rc = ktrace_start(s, dispatch_get_main_queue());
// A more complete sample would use ktrace_set_signal_handler to
// install a handler and call ktrace_end, to ensure resources are freed
dispatch_main();
}
#include <objc/runtime.h>
#include <libgen.h>
#include <dlfcn.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#ifdef ARM
#include <Foundation/Foundation.h> // NSObject
#endif
CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void);
//
// LSDTrip: A simple tool to demonstrate LaunchServices in OS X *and* iOS
// -------
//
// Jonathan Levin, http://NeWOSXBook.com - @Morpheus______/@TechnoloGeeks
//
//
// License: free, but if you GitHub it or mod it, at least leave a mention and
// don't pass it as your own code (as too many people have been doing with
// my other demo code)
//
// And maybe get Vol. I when it's when it's finally out.. or Tg's training
// where this tool is used: http://Technologeeks.com/OSXRE
//
// Compile: gcc ls.m -o lsdtrip -lobjc -framework Foundation
// or
// gcc-iphone -DARM ls.m -o lsdtrip -lobjc -framework Foundation -framework MobileCoreServices
//
// (that "MobileCoreServices" is required for the dlopen(3) to work)
//
//
// To run:
// Usage: lsdtrip [apps|plugins|publicurls|privateurls] [-v]
// app _bundle_id_ (implies -v for this app)
//
// On iOS: privateurls|publicurls both work (via LSApplicationProxy)
// advid
// launch (CFBundleIdentifier)
// On MacOS:
// front [-v]
// visible
// metainfo
// [app|memory|notification|port|session]status
// whohas _url_
// types
// dump <-- Try this :-)
//
// Explanation:
//
// Shows what are (IMHO) the most useful of the LaunchServices APIs, as exported by
// [Mobile]CoreServices. In iOS most of the good stuff isn't exported (t), instead wrapped
// by LSApplicationWorkSpace, LSApplicationProxy, and friends. Even though the straight LS
// and _LS calls are much nicer, I chose to use objective-C here in order to maintain
// write-once-run-everywhere, and demonstrate yet again just how OS X and iOS really are
// so similar.
//
// How this works:
//
// Over rather nice XPC to lsd (10.11/9.0) and friends. Full explanation in MOXiI 2.
// (was Chapter 3, "Promenade, A Tour of the OS X and iOS Frameworks", but now in Chapter 5,
// Application Services, to be exact, where more "private" frameworks are exposed).
//
// I use this tool a lot in my MOXiI trainings, and since it's one of the companion tools of
// MOXiI 2 Vol. 1, I thought people would appreciate a hands-on preview :-) This is just one of the many
// open source examples I have planned.
//
// MOXiI 2 Volume 1 is undergoing final review (typos, typos and typos), and will be out imminently.
//
//
// Improvements:
//
// - Can be made into pure C (i.e. not objective-C, .m), but that would make
// the already ugly [] syntax even worse..
//
// - There's a whole undocumented side to LaunchServices (even as an allegedly
// "public" framework that it is) one can add here. And I left the other
// methods out in an #if 0 block, for future use.
//
// - The UUIDs aren't actually CFUUIDs. They're some $%#$%#$ NS...UUID, which
// isn't compatible, so the UUIDs come with some 0s at the end. Meh.
//
// ChangeLog:
//
// 11/04/2017 - "listen" for notifications re-enabled
// "shmem" added
//
// 07/01/2017 - Lots more functions for MacOS (metainfo-related)
//
// 02/01/2017 - Added advertising identifier stuff
// Fixed dump for iOS 10 and MacOS 12 (stderr)
//
// 06/21/2016 - Added "front" (for MacOS), minor fixes
//
// License:
// -------
// As will my other open sources, free - but be so kind as to at least leave the sources intact,
// give credit, cite the MOXiI books, or something.
//
// At the very least, don't github this and claim it as your own. People have been doing that,
// and it's $%#$% annoying and makes me want to quit releasing these things openly.
// Ok. Let's begin:
// My own prototypes and globals:
typedef int32_t LSSessionID;
#ifndef ARM
extern CFTypeID _LSASNGetTypeID(void);
int _LSASNExtractHighAndLowParts(void *, uint32_t *H, uint32_t *L);
extern CFDictionaryRef _LSCopyApplicationInformation (signed int,
const void *ASN,
uint64_t Zero);
extern void * _LSCopyFrontApplication(signed int);
extern NSArray *_LSCopyApplicationArrayInFrontToBackOrder(signed int, uint64_t unknown);
#endif
CFStringRef dumpApp (NSObject *AppRef, int Verbose) ;
NSObject * workspace; // Intentionally void * so as to NOT involve @interface files
NSObject * fbSvcs;
// OS X and iOS APIs are virtually identical, but the framework name is different
#ifdef ARM
#define CORE_SERVICE_FRAMEWORK "/System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices"
#else
#define CORE_SERVICE_FRAMEWORK "/System/Library/Frameworks/CoreServices.framework/CoreServices"
#endif
void keyDumper (CFStringRef Key, void *Value, void *Nesting)
{
// My own dumper, in simplisting format
static char buf[1024000]; //big, but not too big - need this for MetaInfo status
CFTypeID valueTypeID = CFGetTypeID(Value);
if (Key ) {
//CFStringGetCStringPtr may fail, so opt for slow path
CFStringGetCString(Key, // theString
buf, // buffer
1024,// CFIndex
kCFStringEncodingUTF8); //CFStringEncoding
}
if (Key && valueTypeID != CFArrayGetTypeID()) {
printf("\t%s%s: ", Nesting ? "\t":"", buf);
}
if (valueTypeID == CFStringGetTypeID())
{
CFStringGetCString(Value, // theString
buf, // buffer
1024000,// CFIndex
kCFStringEncodingUTF8); //CFStringEncoding
printf("%s\n", buf);
return;
}
if (valueTypeID == CFNumberGetTypeID())
{
uint64_t valNum;
Boolean conv = CFNumberGetValue(Value, // CFNumberRef number,
CFNumberGetType(Value), // CFNumberType theType,
&valNum); // void *valuePtr);
printf("%llu\n", valNum); // TODO - handle floats, etc
return;
}
if (valueTypeID == CFBooleanGetTypeID())
{
printf("%s\n", CFBooleanGetValue(Value) ? "true" : "false");
return;
}
if (valueTypeID == CFURLGetTypeID())
{
printf("CFURL\n");
}
if (valueTypeID == CFArrayGetTypeID())
{
int count = CFArrayGetCount(Value);
int i = 0;
char key[1024]; // yeah, not null term. ok. I know.
strncpy(key,buf,1024);
// for first element, shove a "\n"..
for (i = 0; i < count; i++)
{
// Call ourselves recursively, but fake a key :-)
// @TODO: proper nesting
printf("\t%s%s[%d]: ", Nesting ? "\t\t" : "", key,i);
keyDumper (NULL, // CFStringRef *Key,
(void *)CFArrayGetValueAtIndex(Value, i), // void *Value,
Nesting); // void *Nesting
}
return;
}
if (valueTypeID == CFDateGetTypeID())
{
// GOD I hate CF.
CFStringRef enUSLocaleIdentifier = CFSTR("en_US");
CFLocaleRef enUSLocale = CFLocaleCreate(NULL, enUSLocaleIdentifier);
CFDateFormatterRef fullFormatter = CFDateFormatterCreate
(NULL, enUSLocale, kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
CFStringRef dateStr = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
fullFormatter,
Value);
keyDumper(NULL,(void*)dateStr,0);
return;
}
if (valueTypeID == CFDictionaryGetTypeID())
{
printf(" (dict)\n");
CFDictionaryApplyFunction(Value, // CFDictionaryRef theDict,
keyDumper, // CFDictionaryApplierFunction CF_NOESCAPE applier,
(void *)1); //void *context);
printf("\n");
return;
}
#ifndef ARM
if (valueTypeID == _LSASNGetTypeID() )
{
uint32_t h, l;
_LSASNExtractHighAndLowParts(Value, &h, &l);
printf("0x%x-0x%x\n", h, l);
return;
}
#endif
printf("@TODO: ");
CFShow(Value);
}
void
dumpDict(CFDictionaryRef dict)
{
if(getenv("CFSHOW")) { CFShow(dict); return;}
// @TODO: perfect SimPLIST format
CFDictionaryApplyFunction(dict, // CFDictionaryRef theDict,
keyDumper, // CFDictionaryApplierFunction CF_NOESCAPE applier,
NULL); //void *context);
}
CFStringRef
serializeCFArrayToCFString (CFArrayRef Array, CFStringRef Delimiter)
{
if (!Array) { return (CFSTR("(null)"));}
CFMutableStringRef returned = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
4096); //CFIndex maxLength);
int len = CFArrayGetCount(Array);
int i = 0;
for (i = 0; i < len ; i++)
{
CFTypeRef val = (CFTypeRef) CFArrayGetValueAtIndex(Array, i);
if (i > 0) CFStringAppend(returned, Delimiter);
// UGLY, I know. But PoC, people. PoC
if (CFGetTypeID(val) == CFStringGetTypeID()) CFStringAppend(returned, val);
else
if (CFGetTypeID(val) == CFUUIDGetTypeID()){
CFStringRef UUID = CFUUIDCreateString(kCFAllocatorDefault, val);
CFStringAppend(returned, UUID);
}
else {
CFStringAppend(returned, dumpApp (val, 0));
};
}
return (returned);
} // serializeCFArrayToCFstring
CFStringRef
dumpApp (NSObject *AppRef, int Verbose)
{
// App is an LSApplicationProxy object
CFStringRef appID = (CFStringRef) [AppRef performSelector:@selector( applicationIdentifier)];
CFStringRef appName = (CFStringRef)[AppRef performSelector:@selector(localizedName)];
if (!appName) { appName = CFSTR("Not Set");}
// CFBooleanRef isDeletable = (CFBooleanRef) [AppRef performSelector:@selector(isDeletable)];
CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
4096); //CFIndex maxLength);
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t%@ (%@) \n"),
appName, appID);
#if 0
// Can also use objective-C to enumerate ivars.. left as an exercise for reader :-)
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList([AppRef class], &ivarCount);
int i = 0;
for (i = 0; i < ivarCount; i++)
{
fprintf(stderr,"\t%s: \n" , ivar_getName(ivars[i]));
// etc.
}
#endif
if (Verbose)
{
// Dump more - this is just a fraction of the info -
// jtool -v -d LSApplicationProxy /Sy*/L*/Fra*/C*Se*/Frameworks/La*S*/LaunchServices
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tExecutable: %@\n"),
[AppRef performSelector:@selector(bundleExecutable)]);
#ifdef ARM
// Set on iOS. Can also use versionIdentifier here, but that requires working back from the
// number to a version string (which LaunchServices lets you do with
// LSVersionNumberCopyStringRepresentation/LSVersionNumberGetXXX()
// But why bother when you have a short version string..
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tVersion: %@\n"),
(CFStringRef)[AppRef performSelector:@selector(shortVersionString)]);
#endif
// This is apparently unset..
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tVendor Name: %@\n"),
(CFStringRef)[AppRef performSelector:@selector(vendorName)]);
/*
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tMach-O UUIDs: %@\n"),
serializeCFArrayToCFString((CFArrayRef)[AppRef performSelector:@selector(machOUUIDs)], CFSTR(",")));
*/
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tDisk Usage (Static): %@\n"),
(CFStringRef)[AppRef performSelector:@selector(staticDiskUsage)]);
#if 0
// This apparently doesn't work in 9.2 anymore. Not sure about this..
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tDisk Usage (Dynamic): %@\n"),
(CFStringRef)[AppRef performSelector:@selector(dynamicDiskUsage)]);
#endif
CFArrayRef UIBackgroundModes = (CFArrayRef) [AppRef performSelector: @selector(UIBackgroundModes)];
// This is a CFArray
if (!CFArrayGetCount(UIBackgroundModes)) {
CFStringAppend (out,CFSTR("\t\tno BackgroundModes"));
}
else
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\t BackgroundModes: %@"),
serializeCFArrayToCFString((CFArrayRef)UIBackgroundModes, CFSTR(",")));
CFStringAppend(out, CFSTR("\n"));
#ifdef ARM
// Only on iOS
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tApplication DSID: %@\n"),
(CFStringRef)[AppRef performSelector:@selector(applicationDSID)]);
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tPurchaser DSID: %@\n"),
(CFStringRef)[AppRef performSelector:@selector(purchaserDSID)]);
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tDownloader DSID: %@\n"),
(CFStringRef)[AppRef performSelector:@selector(downloaderDSID)]);
#endif
#if 0
uint64_t modTime = (uint64_t)([AppRef performSelector:@selector( bundleModTime)]);
fprintf(stderr, "\t\tBundle Mod Time: %llu\n", modTime);
#endif
int cont = (int)([AppRef performSelector:@selector( isContainerized)]);
int restricted = (int)([AppRef performSelector:@selector( isRestricted)]);
CFStringAppendFormat(out,
NULL,
CFSTR("\t\tContainerized: %@\n\t\tRestricted: %@\n"),
(cont ? CFSTR("YES (q.v. App-Store Receipt URL for container)") : CFSTR("NO")),
(restricted ? CFSTR("YES") : CFSTR("NO")));
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tApp Store Receipt URL: %@\n"),
(CFStringRef)[AppRef performSelector:@selector( appStoreReceiptURL)]);
// These are from LSBundleProxy, which is the parent of LSApplicationProxy
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tContainer URL: %@\n"),
[AppRef performSelector:@selector(containerURL)]);
CFDictionaryRef entitlements = (CFDictionaryRef) [AppRef performSelector:@selector(entitlements)];
if (entitlements && CFDictionaryGetCount(entitlements) )
{
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)entitlements);
CFStringRef xmlAsString = CFStringCreateFromExternalRepresentation(NULL, xml, kCFStringEncodingUTF8);
if (xmlAsString) {
CFStringAppendFormat(out, // CFMutableStringRef theString,
NULL, // CFDictionaryRef formatOptions,
CFSTR("\t\tEntitlements:\n-----\n %@\n-----\n"),
xmlAsString);
}
else { CFStringAppend (out, CFSTR("\t\tEntitlements: Internal error\n"));}
} // entitlements
else
CFStringAppend(out,CFSTR("\t\tEntitlements: None\n"));
} // Verbose
return (out);
}
/*
*
* Soooo much more, courtesy of JTool. Feel free to extend these on your own time:
*
-[LSApplicationProxy applicationIdentifier]:
-[LSApplicationProxy roleIdentifier]:
-[LSApplicationProxy bundleModTime]:
-[LSApplicationProxy registeredDate]:
-[LSApplicationProxy deviceFamily]:
-[LSApplicationProxy minimumSystemVersion]:
-[LSApplicationProxy sdkVersion]:
-[LSApplicationProxy applicationType]:
-[LSApplicationProxy itemName]:
-[LSApplicationProxy sourceAppIdentifier]:
-[LSApplicationProxy companionApplicationIdentifier]:
-[LSApplicationProxy applicationVariant]:
-[LSApplicationProxy storeCohortMetadata]:
-[LSApplicationProxy shortVersionString]:
-[LSApplicationProxy preferredArchitecture]:
-[LSApplicationProxy familyID]:
-[LSApplicationProxy groupContainers]:
-[LSApplicationProxy directionsModes]:
-[LSApplicationProxy UIBackgroundModes]:
-[LSApplicationProxy audioComponents]:
-[LSApplicationProxy externalAccessoryProtocols]:
-[LSApplicationProxy VPNPlugins]:
-[LSApplicationProxy plugInKitPlugins]:
-[LSApplicationProxy appTags]:
-[LSApplicationProxy requiredDeviceCapabilities]:
-[LSApplicationProxy deviceIdentifierForVendor]:
-[LSApplicationProxy ODRDiskUsage]:
-[LSApplicationProxy storeFront]:
-[LSApplicationProxy externalVersionIdentifier]:
-[LSApplicationProxy betaExternalVersionIdentifier]:
-[LSApplicationProxy appStoreReceiptURL]:
-[LSApplicationProxy installProgress]:
-[LSApplicationProxy installProgressSync]:
-[LSApplicationProxy resourcesDirectoryURL]:
-[LSApplicationProxy privateDocumentIconNames]:
-[LSApplicationProxy setPrivateDocumentIconNames:]:
-[LSApplicationProxy privateIconsDictionary]:
-[LSApplicationProxy privateDocumentIconAllowOverride]:
-[LSApplicationProxy setPrivateDocumentIconAllowOverride:]:
-[LSApplicationProxy iconDataForVariant:]:
-[LSApplicationProxy privateDocumentTypeOwner]:
-[LSApplicationProxy setPrivateDocumentTypeOwner:]:
-[LSApplicationProxy localizedName]:
-[LSApplicationProxy localizedShortName]:
-[LSApplicationProxy localizedNameForContext:]:
-[LSApplicationProxy iconIsPrerendered]:
-[LSApplicationProxy fileSharingEnabled]:
-[LSApplicationProxy profileValidated]:
-[LSApplicationProxy isAdHocCodeSigned]:
-[LSApplicationProxy isPlaceholder]:
-[LSApplicationProxy isAppUpdate]:
-[LSApplicationProxy isNewsstandApp]:
-[LSApplicationProxy isRestricted]:
-[LSApplicationProxy supportsAudiobooks]:
-[LSApplicationProxy supportsExternallyPlayableContent]:
-[LSApplicationProxy supportsOpenInPlace]:
-[LSApplicationProxy hasSettingsBundle]:
-[LSApplicationProxy isInstalled]:
-[LSApplicationProxy isWhitelisted]:
-[LSApplicationProxy isBetaApp]:
-[LSApplicationProxy isPurchasedReDownload]:
-[LSApplicationProxy isWatchKitApp]:
-[LSApplicationProxy hasMIDBasedSINF]:
-[LSApplicationProxy missingRequiredSINF]:
-[LSApplicationProxy isEqual:]:
-[LSApplicationProxy hash]:
-[LSApplicationProxy description]:
-[LSApplicationProxy iconStyleDomain]:
-[LSApplicationProxy userActivityStringForAdvertisementData:]:
-[LSApplicationProxy populateNotificationData]:
-[LSApplicationProxy itemID]:
-[LSApplicationProxy installType]:
-[LSApplicationProxy originalInstallType]:
-[LSApplicationProxy groupIdentifiers]:
-[LSApplicationProxy teamID]:
-[LSApplicationProxy isContainerized]:
*/
struct appInfoInShmem {
uint32_t ASN;
uint32_t seed;
uint32_t flags;
uint32_t visibleIndex;
uint32_t pid;
};
struct LSShmem {
uint32_t version;
uint32_t sizeInPages;
uint32_t session;
uint32_t zero;
uint32_t debugLogLevel;
uint32_t zero1;
uint64_t alsoZero;
uint32_t frontASNSeed;
uint32_t menuBarOwnerASNSeed;
uint32_t applicationListSeed;
uint32_t pendingApplicationListSeed;
uint32_t visibleApplicationListSeed;
uint32_t applicationInformationSeed;
uint64_t unknown[3];
uint32_t frontASN;
uint32_t menuBarOwnerASN;
uint32_t systemProcessASN;
uint32_t systemLauncherASN;
uint64_t nevermind[5];
uint32_t numPids;
uint32_t whatever;
struct appInfoInShmem appList[0];
/*
menuBarOwnerASN="Terminal" ASN:0x0-0xa00a:
systemProcessASN="loginwindow" ASN:0x0-0x1001:
systemLauncherASN=ASN:0x0-0x0:
nextAppToBringForwardASNLowHalf=ASN:0x0-0x0:
expectedFrontASNLowerHalf=ASN:0x0-0x0:
systemUIPresentationMode=0 "Normal" systemUIPresentationOptions=0
launchProgress=0
launchProgessUserActivityCount=0
processes = #37
*/
};
#ifndef ARM
void dumpShmem (struct LSShmem *Shmem)
{
printf("Shmem at address %p (%d pages = 0x%x):\n", Shmem,
Shmem->sizeInPages, Shmem->sizeInPages * 0x1000);
printf("Session: %d\n", Shmem->session);
// lockCount=0
printf("DebugLogLevel: %d\n", Shmem->debugLogLevel);
printf("frontASNSeed: %d\n", Shmem->frontASNSeed);
printf("menuBarOwnerASNSeed: %d\n", Shmem->menuBarOwnerASNSeed);
printf("applicationListSeed: %d\n", Shmem->applicationListSeed);
printf("pendingApplicationListSeed: %d\n", Shmem->pendingApplicationListSeed);
printf("visibleApplicationListSeed: %d\n", Shmem->visibleApplicationListSeed);
printf("applicationInformationSeed: %d\n", Shmem->applicationInformationSeed);
printf("frontASN: 0x%x\n", Shmem->frontASN);
printf("systemProcessASN: 0x%x\n", Shmem->systemProcessASN);
printf("systemLauncherASN: 0x%x\n", Shmem->systemLauncherASN);
printf("Processes: %d\n", Shmem->numPids);
int p = 0;
for (p = 0; p < Shmem->numPids; p++)
{
printf("\t App: ASN 0x%x PID: %d Visible Index: %d Seed: %d, Flags: 0x%x\n",
Shmem->appList[p].ASN,
Shmem->appList[p].pid,
Shmem->appList[p].visibleIndex,
Shmem->appList[p].seed,
Shmem->appList[p].flags);
}
} // dump Shmem
#endif
void
dumpURL (CFStringRef URL, int Verbose)
{
CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
4096); //CFIndex maxLength);
CFStringAppend(out, URL);
if (Verbose) {
CFArrayRef appsForURL = (CFArrayRef) [workspace performSelector:@selector(applicationsAvailableForHandlingURLScheme:) withObject:(id)URL];
if (!appsForURL) {
// Maybe it's a document type..
// appsForURL = (CFArrayRef) [workspace performSelector:@selector(applicationsAvailableForOpeningDocument:) withObject:(id)URL];
}
// if still no apps, then no claim.
if (!appsForURL)
{
fprintf(stderr,"No app has claimed this URL\n"); exit(1);
}
// This is a CFArray
if (CFGetTypeID(appsForURL) != CFArrayGetTypeID())
{
fprintf(stderr, "Was expecting a CFArray of Apps!\n");
exit(2);
}
if (!CFArrayGetCount (appsForURL)) CFStringAppend(out,CFSTR(" - Not claimed by anyone"));
else {
CFStringRef apps = serializeCFArrayToCFString (appsForURL, CFSTR("\n\t\t\t"));
CFStringAppend (out, apps);
CFRelease (apps);
}
}
CFShow(out);
CFRelease(out);
}
void
dumpPlugin (NSObject *PluginRef, int Verbose)
{
CFStringRef pluginName = (CFStringRef) [PluginRef performSelector:@selector( localizedName)];
CFStringRef pluginID = (CFStringRef) [PluginRef performSelector:@selector( pluginIdentifier)];
CFUUIDRef pluginUUID = (CFUUIDRef)[PluginRef performSelector:@selector(pluginUUID)];
CFStringRef out = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("\t%@ (%@) - %@"),
pluginName,pluginID, CFUUIDCreateString(kCFAllocatorDefault, pluginUUID));
CFShow(out);
} // end Dump
void monitorCallback()
{
printf("In callback\n");
}
int notificationCallbackFunc(int a, CFDictionaryRef *Notif, void *todo1, void *x, uint64_t xx, void *y) {
printf("Notification Received:\n");
dumpDict(Notif);
return 0;
}
void
usage (char *ProgName)
{
#ifdef ARM
fprintf(stderr, "Usage: %s [apps|plugins|publicurls|privateurls] [-v]\n", basename(ProgName));
fprintf(stderr, " app _bundle_id_ (implies -v for this app)\n");
fprintf(stderr, " launch _bundle_id_\n");
fprintf(stderr, " advid [clear]\n");
#else
fprintf(stderr, "Usage: %s [apps|plugins] [-v]\n", basename(ProgName));
fprintf(stderr, " app _bundle_id_ (implies -v for this app)\n");
fprintf(stderr, " front [-v]\n");
fprintf(stderr, " visible\n");
fprintf(stderr, " listen (Get app launch/foreground notifications - cool)\n");
fprintf(stderr, " shmem\n");
fprintf(stderr, " metainfo\n");
fprintf(stderr, " [app|memory|notification|port|session]status\n");
#endif
fprintf(stderr, " whohas _url_ \n");
fprintf(stderr, " types\n");
fprintf(stderr, " dump\n");
fprintf(stderr, "\nSet CFSHOW=1 to use CFshow in place of J's dumping function (which is still a work in progress)\n");
exit(0);
} // usage
int
main (int argc, char **argv)
{
int verbose = 0;
// So - why dup? because CFShow(), which I use extensively, write to stderr.
// And I really CANNOT stand the whole CFStringToCstr $%#$%#$ just to get a stupid
// printout! So instead, I save stderr, and then reopen stderr to stdout. This
// way, the output can be grep(1)-ed easily.
dup2(2,3);
dup2(1,2);
if (argc < 2)
{
usage(argv[0]);
}
if (argv[2] && strcmp(argv[2], "-v")==0) verbose++;
// Getting the LS* classes and functions we need here:
// ---------------------------------------------------
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
if (!LSApplicationWorkspace_class) {fprintf(stderr,"Unable to get Workspace class\n"); exit(1);}
workspace = [LSApplicationWorkspace_class performSelector:@selector (defaultWorkspace)];
if (!workspace) {fprintf(stderr,"Unable to get Workspace\n"); exit(2);}
void *CS_handle = dlopen (CORE_SERVICE_FRAMEWORK, RTLD_NOW);
if (!CS_handle) { fprintf(stderr,"Can't find %s!\n", CORE_SERVICE_FRAMEWORK); exit(2); };
#ifdef ARM
if (strcmp(argv[1],"running") == 0)
{
#if 0
Class fbSvcs_class = objc_getClass("FBSSystemService");
if (!fbSvcs_class) { fprintf(stderr,"Unable to get Workspace\n"); exit(2);}
fbSvcs = [fbSvcs_class performSelector:@selector (sharedService)];
printf("GOT SVCS %p\n", fbSvcs);
#endif
printf("Not yet\n");
exit(0);
}
#endif
if ( (strcmp(argv[1],"apps") == 0) || (strcmp(argv[1], "app") == 0))
{
CFStringRef wantedBundleID = NULL;
if (strcmp(argv[1], "apps")) // that is, not all apps
{
// expecting a bundle identifier here
if (!argv[2]) { fprintf(stderr,"app option requires a specific bundle id. Try 'apps' to get a list\n");
exit(5); }
wantedBundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
argv[2], // const char *cStr,
kCFStringEncodingUTF8); //CFStringEncoding encoding);
}
CFArrayRef apps = (CFArrayRef) [workspace performSelector:@selector(allApplications)];
// This is a CFArray
if (CFGetTypeID(apps) != CFArrayGetTypeID())
{
fprintf(stderr, "Was expecting a CFArray of Apps!\n");
exit(2);
}
int len = CFArrayGetCount(apps);
int i = 0;
for (i = 0; i < len ; i++)
{
CFTypeRef app = CFArrayGetValueAtIndex(apps,i);
// Got app: Dump if want all, or if matches id.
// I'm sure there's some Workspace method I missed to get an app by bundle ID, instead
// of iterating over all of them..
CFStringRef appID = (CFStringRef) [(id)app performSelector:@selector(applicationIdentifier)];
if (appID && wantedBundleID)
{
if (CFEqual(appID, wantedBundleID))
{
CFStringRef dump = dumpApp(app, 1);
CFShow(dump);
CFRelease(dump);
exit(0); // only one match here.
}
}
else
{
CFStringRef dump = dumpApp(app, verbose);
CFShow(dump);
CFRelease(dump);
}
}
if ( wantedBundleID) {
fprintf(stderr,"Application with Bundle ID %s not found. Try '%s apps' first, and remember case-sensitivity\n",
argv[2], argv[0]);
exit(2);
}
exit(0);
}
if (strcmp(argv[1], "publicurls") == 0)
{
#ifdef ARM
CFArrayRef puburls = (CFArrayRef) [workspace performSelector:@selector(publicURLSchemes)];
// This is a CFArray
if (CFGetTypeID(puburls) != CFArrayGetTypeID())
{
fprintf(stderr, "Was expecting a CFArray of publicURLSchemes!\n");
exit(2);
}
int len = CFArrayGetCount(puburls);
int i = 0;
for (i = 0; i < len ; i++)
{
CFStringRef url = CFArrayGetValueAtIndex(puburls,i);
dumpURL(url, verbose);
}
#else
fprintf(stderr,"Not implemented on MacOS.. yet\n");
#endif
exit(0);
}
if (strcmp(argv[1], "privateurls") == 0)
{
#ifndef ARM
// MacOS's LSApplicationWorkSpace does not respond to private URLs..
// @TODO:__LSCopyAllApplicationURLs
printf("Not implemented on MacOS.. yet\n");
#else
CFArrayRef (privurls) = (CFArrayRef) [workspace performSelector:@selector(privateURLSchemes)];
// This is a CFArray
if (CFGetTypeID(privurls) != CFArrayGetTypeID())
{
fprintf(stderr, "Was expecting a CFArray of privateURLSchemes!\n");
exit(2);
}
int len = CFArrayGetCount(privurls);
int i = 0;
for (i = 0; i < len ; i++)
{
CFStringRef url = CFArrayGetValueAtIndex(privurls,i);
dumpURL(url, verbose);
}
#endif
exit(0);
}
if (strcmp(argv[1], "plugins") == 0)
{
CFArrayRef plugins = (CFArrayRef) [workspace performSelector:@selector(installedPlugins)];
// This, too, is a CFArray
if (CFGetTypeID(plugins) != CFArrayGetTypeID())
{
fprintf(stderr, "Was expecting a CFArray of plugins!\n");
exit(2);
}
int len = CFArrayGetCount(plugins);
int i = 0;
for (i = 0; i < len ; i++)
{
CFTypeRef plugin = CFArrayGetValueAtIndex(plugins,i);
dumpPlugin(plugin, verbose);
}
exit(0);
}
if (strcmp(argv[1], "types") == 0)
{
// I resort to dlopen/dlsym here to make linking simpler. You don't have to.
typedef CFArrayRef (*UTCopyDeclaredTypeIdentifiersFunc)(void);
UTCopyDeclaredTypeIdentifiersFunc UTCopyDeclaredTypeIdentifiers
= dlsym (CS_handle, "_UTCopyDeclaredTypeIdentifiers");
CFArrayRef utis = UTCopyDeclaredTypeIdentifiers();
// As usual, this is an array..
int len = CFArrayGetCount(utis);
int i = 0;
for (i = 0; i < len ; i++)
{
// Seriously, AAPL - couldn't you find a better acronym?
CFTypeRef uti = CFArrayGetValueAtIndex(utis,i);
CFShow(uti);
}
exit(0);
}
if (strcmp(argv[1], "whohas") == 0)
{
if (!argv[2]) {
fprintf(stderr,"whohas: Option requires an URL Scheme or UTI as an argument!\n");
exit(3);
}
CFStringRef url = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
argv[2], // const char *cStr,
kCFStringEncodingUTF8); //CFStringEncoding encoding);
dumpURL(url,1);
exit(0);
}
#if 0
// ignore this part for now
if (strcmp(argv[1],"claims") == 0)
{
typedef CFStringRef (*LSCopyClaimedActivityIdentifiersAndDomainsFunc)(CFStringRef, CFStringRef);
LSCopyClaimedActivityIdentifiersAndDomainsFunc
LSCopyClaimedActivityIdentifiersAndDomains = dlsym (CS_handle, "_LSCopyClaimedActivityIdentifiersAndDomains");
if (!LSCopyClaimedActivityIdentifiersAndDomains )
{
fprintf(stderr,"Unable to find LSCopyClaimedActivityIdentifiersAndDomains ");
exit(3);
}
CFStringRef result = LSCopyClaimedActivityIdentifiersAndDomains (NULL, NULL);
CFShow(result);
exit(0);
}
#endif
if (strcmp(argv[1],"advid") == 0)
{
if ((argc ==3) && strcmp(argv[2], "clear") == 0) {
(void) [workspace performSelector:@selector(clearAdvertisingIdentifier) ];
printf("advertising identifier should be clear\n");
}
else {
// just show
CFStringRef advid = [workspace performSelector:@selector(deviceIdentifierForAdvertising)];
CFShow(advid);
}
exit(0);
}
#ifdef ARM
if (strcmp(argv[1],"open") == 0)
{
if (!argv[2]) {
fprintf(stderr,"open: Option requires an argument!\n"); exit(3);
}
CFStringRef theURLs = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
argv[2], // const char *cStr,
kCFStringEncodingUTF8); //CFStringEncoding encoding);
CFURLRef theURL;
theURL = CFURLCreateWithString(kCFAllocatorDefault, // CFAllocatorRef allocator
theURLs, //CFStringRef URLString,
NULL); // CFURLRef baseURL);
printf("CREATED URL %p\n", theURL);
int t = (int) [workspace performSelector:@selector(openURL:) withObject:(id) theURL ];
fprintf(stderr, "%s %s (RC: %d)\n",
t ? "opened" : "Unable to open",
argv[2], t);
#if 0
extern int
LSOpenCFURLRef(
CFURLRef inURL,
__nullable CFURLRef *__nullable outLaunchedURL) ;
LSOpenCFURLRef(theURL, NULL);
#endif
exit(0);
}
#endif
if (strcmp(argv[1],"launch") == 0)
{
if (!argv[2]) {
fprintf(stderr,"launch: Option requires an argument!\n"); exit(3);
}
CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
argv[2], // const char *cStr,
kCFStringEncodingUTF8); //CFStringEncoding encoding);
int t = (int) [workspace performSelector:@selector(openApplicationWithBundleID:) withObject:(id) bundleID ];
fprintf(stderr, "%s %s\n",
t ? "Launched" : "Unable to launch",
argv[2]);
exit(0);
}
#ifndef ARM
// This only works on MacOS
// because A) API doesn't exist in *OS
// B) there's no "visible application ordering" in *OS, to begin with.
if (strcmp(argv[1], "visible") == 0)
{
NSArray *apps = _LSCopyApplicationArrayInFrontToBackOrder(-2, 0);
// Apps is an array of LSASN types
int a = 0;
for (a = 0; a < [apps count]; a++)
{
void *asn = [apps objectAtIndex:a];
CFDictionaryRef dict = _LSCopyApplicationInformation (-2, asn,0);
CFStringRef bn = CFDictionaryGetValue(dict, CFSTR("CFBundleName"));
CFStringRef bp = CFDictionaryGetValue(dict, CFSTR("CFBundleExecutablePath"));
// ShowASN (ignoring return value here)
uint32_t high, low;
//int _LSASNExtractHighAndLowParts(void *, uint32_t *H, uint32_t *L);
(void) _LSASNExtractHighAndLowParts (asn, &high, &low);
fprintf(stderr,"0x%x-0x%-6x ", high, low);
// CFShow(bn);
CFShow(bp);
}
exit(0);
}
if (strcmp(argv[1], "front") == 0)
{
// If you wanted the front most application you could also
// print the first entry in the array from "Visible" (that is,
// _LSCopyApplicationArrayInFrontToBackOrder)
CFStringRef front = _LSCopyFrontApplication(-2);
if (!front) {
fprintf(stderr,"Can't get front application?\n"); exit(2);
}
if (_LSASNGetTypeID() == CFGetTypeID(front))
{
// Now get info
CFDictionaryRef dict = _LSCopyApplicationInformation (-2, front,0);
if (verbose)
{
dumpDict(dict);
}
else
{
CFStringRef dn = CFDictionaryGetValue(dict, CFSTR("CFBundleName"));
CFShow(dn);
}
exit(0);
}
else
{
fprintf(stderr,"Got front application but not an LSASN type.. Has AAPL changed the API or something?\n"); exit(3);
}
exit(0);
}
extern CFDictionaryRef _LSCopyMetaApplicationInformation(LSSessionID id, CFArrayRef *What);
if (strcmp(argv[1], "metainfo") == 0)
{
// A null pointer for What gets you a raw dict.
CFDictionaryRef meta = _LSCopyMetaApplicationInformation(-2,0);
if (!meta) { printf("NO META\n"); exit(0);}
dumpDict(meta);
exit(0);
}
if (strstr(argv[1], "status")) {
CFStringRef vals[4];
vals[0] = CFSTR("Status");
vals[1] = NULL;
// A non null pointer for What (NS/CFArray) gets you a specific dict
// which is already human-readable
//
if (strcmp(argv[1], "appstatus") == 0) vals[1] = CFSTR("ApplicationStatus");
if (strcmp(argv[1], "notificationstatus") == 0) vals[1] = CFSTR("NotificationStatus");
if (strcmp(argv[1], "portstatus") == 0) vals[1] = CFSTR("PortStatus");
if (strcmp(argv[1], "sessionstatus") == 0) vals[1] = CFSTR("SessionStatus");
// Only supported in DEBUG builds :-(
if (strcmp(argv[1], "memorystatus") == 0) vals[1] = CFSTR("MemoryStatus");
if (!vals[1]) { fprintf(stderr,"What kind of status is %s?\n", argv[1]); exit(5);}
CFArrayRef what = CFArrayCreate(kCFAllocatorDefault, // CFAllocatorRef allocator,
(const void **) vals, // const void **values,
2, // CFIndex numValues,
&kCFTypeArrayCallBacks); //const CFArrayCallBacks *callBacks);
CFDictionaryRef meta = _LSCopyMetaApplicationInformation(-2,what);
dumpDict(meta);
exit(0);
}
typedef void *LSNotificationReceiverRef;
if (strcmp(argv[1],"shmem") == 0)
{
extern void * _LSDebugGetSharedMemoryPageAddress(LSSessionID);
void *shmem = _LSDebugGetSharedMemoryPageAddress(-2);
if (shmem) {
dumpShmem(shmem);
}
exit(0);
}
extern LSNotificationReceiverRef _LSScheduleNotificationFunction(LSSessionID,
void *callback, // actually a func pointer
uint64_t zero,
NSMutableArray *context, // RCX
CFRunLoopRef CFRunLoop,
const CFRunLoopMode CFRunLoopMode);
if (strcmp(argv[1],"listen") == 0)
{
CFRunLoopRef rl = CFRunLoopGetCurrent();
// If you don't give an array, even empty, you get RC 775
NSMutableArray *notifs = [[NSMutableArray alloc ] init];
LSNotificationReceiverRef ref =
_LSScheduleNotificationFunction(-2,
notificationCallbackFunc,
0,
notifs, // 0, //notifs,
rl,
NULL); //kCFRunLoopDefaultMode); //CFRunLoopCopyCurrentMode(rl));
// 11/1/2017 This was missing from earlier version
extern _LSModifyNotification(LSNotificationReceiverRef ref,
int numKeys,
uint32_t *mask,
uint32_t unknown,
uint32_t unknown1,
uint32_t unknown2);
int mask=-1; // get all
_LSModifyNotification(ref, //
1, // uint32
&mask,
0,
0, 0);
// If you want to actually see the XPC notification, put a breakpoint on:
//LaunchServices`LSNotificationReceiver::receiveNotificationFromServer(_xpc_connection_s*, void*)
// Need a runloop for this, so run.
CFRunLoopRun();
/* --- */
printf("Not reached ....\n");
exit(0);
}
#endif
// NOT YET
if (strcmp(argv[1],"monitor-notyet") == 0)
{
// You can do this with an LSApplicationWorkspaceObserver, but that requires
// objective C, in order to implement the callback messages. Much easier
// in a pure C solution to do this by directly accessing the Notification center
// extern void *CFNotificationCenterGetDistributedCenter(void);
CFNotificationCenterRef distCent = CFNotificationCenterGetDistributedCenter();
//CFStringRef name = CFSTR("com.lsinstallprogress.appcontrols.pause"); // com.apple.LaunchServices.applicationStateChanged");
CFStringRef name = CFSTR("com.apple.LaunchServices.applicationStateChanged");
extern void CFNotificationCenterAddObserver(CFNotificationCenterRef center, const void *observer, CFNotificationCallback callBack, CFStringRef name, const void *object, CFNotificationSuspensionBehavior suspensionBehavior);
void *observer;
void *monitorCallback;
CFNotificationCenterAddObserver(distCent,
NULL, //observer,
monitorCallback,
name,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
printf("Runloop\n");
CFRunLoopRun();
#if 0
0000000000166c5 callq 0x130cc4 ## symbol stub for: _CFNotificationCenterGetDistributedCenter
00000000000166ca leaq _LSApplicationWorkspaceObserverCallback(%rip), %rdx
00000000000166d1 leaq 0x16e308(%rip), %rcx ## Objc cfstring ref: @"com.lsinstallprogress.appcontrols.cancel"
00000000000166d8 xorl %r8d, %r8d
00000000000166db movl $0x4, %r9d
00000000000166e1 movq %rax, %rdi
00000000000166e4 movq %rbx, %rsi
00000000000166e7 callq 0x130cb8 ## symbol stub for: _CFNotificationCenterAddObserver
#endif
sleep(100);
exit(0);
}
// And this is the real fun part:
//
// The little known "lsregister" utility in OS X is closed source, and un-man(1)ned,
// But one of its coolest features is "dump" - to dump the LaunchServices Database.
// Turns out this is a *single* line of code. And guess what -- it works on iOS too :-)
//
if (strcmp(argv[1], "dump") == 0)
{
typedef int (*LSDisplayDataFunc)(FILE *, void*);
LSDisplayDataFunc LSDisplayData;
// If you want to get the raw format of the file, you can dump with this, instead:
//extern _LSDisplayRawStoreData(char *, int);
//_LSDisplayRawStoreData("/var/mobile/Library/Caches/com.apple.LaunchServices-134.csstore", (void *) 0xff);
LSDisplayData = dlsym (CS_handle, "_LSDisplayData");
if (!LSDisplayData) { fprintf(stderr, "Can't find LSDisplayData! Has Apple removed it by now? :-P"); exit(1);}
// The argument expected here is likely the name of the CoreServices (LaunchServices)
// DataStore - in iOS "/var/mobile/Library/Caches/com.apple.LaunchServices-###.csstore"
// but turns out NULL <del>works</del> on both OS X and iOS!
// Until iOS 10, that is - wherein you need FILE * - so I use stderr
int rc = LSDisplayData(stderr,NULL);
if (rc != 0) { fprintf(stderr,"LSDisplayData returned %d\n",rc);}
exit(rc);
}
// Still here? Usage
fprintf(stderr,"I don't understand '%s'\n", argv[1]);
usage(argv[0]);
#if 0
0000000183063cb8 t +[LSApplicationWorkspaceObserver supportsSecureCoding]
0000000183063cc0 t -[LSApplicationWorkspaceObserver encodeWithCoder:]
0000000183063d00 t -[LSApplicationWorkspaceObserver init]
0000000183063d74 t -[LSApplicationWorkspaceObserver initWithCoder:]
0000000183063e08 t -[LSApplicationWorkspaceObserver dealloc]
0000000183063e64 t -[LSApplicationWorkspaceObserver applicationInstallsDidStart:]
0000000183063fa0 t -[LSApplicationWorkspaceObserver applicationInstallsDidChange:]
00000001830640dc t -[LSApplicationWorkspaceObserver applicationInstallsDidUpdateIcon:]
0000000183064218 t -[LSApplicationWorkspaceObserver applicationsDidInstall:]
0000000183064354 t -[LSApplicationWorkspaceObserver applicationsWillInstall:]
0000000183064490 t -[LSApplicationWorkspaceObserver applicationsWillUninstall:]
00000001830645cc t -[LSApplicationWorkspaceObserver applicationsDidFailToInstall:]
0000000183064708 t -[LSApplicationWorkspaceObserver applicationsDidFailToUninstall:]
0000000183064844 t -[LSApplicationWorkspaceObserver applicationsDidUninstall:]
0000000183064980 t -[LSApplicationWorkspaceObserver applicationInstallsArePrioritized:arePaused:]
0000000183064be8 t -[LSApplicationWorkspaceObserver applicationInstallsDidPause:]
0000000183064d24 t -[LSApplicationWorkspaceObserver applicationInstallsDidResume:]
0000000183064e60 t -[LSApplicationWorkspaceObserver applicationInstallsDidCancel:]
0000000183064f9c t -[LSApplicationWorkspaceObserver applicationInstallsDidPrioritize:]
00000001830650d8 t -[LSApplicationWorkspaceObserver applicationStateDidChange:]
0000000183065214 t -[LSApplicationWorkspaceObserver networkUsageChanged:]
0000000183065368 t -[LSApplicationWorkspaceObserver uuid]
0000000183065378 t -[LSApplicationWorkspaceObserver setUuid:]
#endif
}
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> // malloc
//#undef ntohs
#include <arpa/inet.h> // inet_ntop
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include "lsock.h"
#include <ncurses.h>
#include <netdb.h> // GAI
int g_debug =0;
int noResolve = 0;
int wantCurses =1;
int wantHumanReadable = 0;
int wantPackets = 0;
int wantOnce = 0;
int wantUDP = 1;
int wantListen = 1;
int wantTCP = 1;
int wantColors =0;
int wantRefresh = 0;
// JDELAY because DELAY is #defined elsewhere..
#define JDELAY 100000// 0.1 second
/**
* lsock.c : A TCPView inspired netstat(1)-clone (in text mode) for OS X/iOS
*
* Coded by Jonathan Levin - http://www.newosxbook.com/
*
* Consider this the missing source of nettop (with a few improvements):
* AAPL uses the com.apple.network.statistics socket through the NetworkStatistics PrivateFramework.
* This basically shows the same usage, without the dispatch queues and with direct access to the
* system socket.
*
* No (C). Distribute at will. It would be appreciate if you gave credit, though.
*
* SPECIFICALLY those #%$#%ers who rip my sources and put them on GitHUB
* as if they're their own.
*
* If you want to use this commercially, please let me know first.
*
* The makefile, universal binary and iOS CLI compilation script can be found in the tar this came in.
*
* Note you need "lsock.h" to compile (these are the ntstat headers from kernel mode, which AAPL
* never bothered to publish). You can find the .h in the same URL you found this, or in the tar file.
*
* ----------------------------------------------------------------------------------------------
* Arguments: "nc" - disable full-screen (will automatically disable curses if piping output)
* "once" - one shot - automatically disables curses
* "tcp" - start in TCP-only mode
* "udp" - start in UDP-only mode
*
* Commands for full screen mode: 'T' - TCP, 'U' - UDP, 'L' - Listeners, 'C' - Colors
* 'R' - Name Resolution
*
* In CLI/nc mode, delimiters are tabs - this makes it useful to use this command as a continuous
* source for cut -d'(tab)' and select only the fields which interest you.
*
* Possible improvements
*
* - GUI: Make this more like Windows' TCPView and Procmon
* - Remote monitoring
* <del>- Periodic polling on sources: Currently only when a source notification is received do
* I poll its description</del>
* - Add routing provider (prov #1)
* - Show provider statistics, too
*
* ChangeLog:
* ----------
* 02/16/2016 - Almost annual update..
*
* - Updated for iOS 10/OS X 10.12, because AAPL changed format (AGAIN)
* - Much better error handling
* - added atexit(3) handling to reset term
*
* 03/16/2016 - Wow. It's been a while...
*
* - Updated for iOS 9/OSX 10.11
* - Message now at first row, table starts at second. Nicer output this way
* - Added "-n" to NOT resolve IPs. Now doing so by default - the Sergio fix.
*
*
* 06/14/2014 - Plenty of improvements, including:
* Fixed bug in the public version which prevented binding if any other ntstat
* client was registered.. (e.g. UserEventAgent - This one's for you, Jason)
*
* Ordered requests to control socket so that all descriptors are shown, always (no race, etc)
*
* Updated for Yosemite and Mavericks (tested on ML, Yosemite, iOS 7, but not on Mavericks or iOS8)
* When the folks @Cupertino share the xnu 27 headers, I'll update again
*
* Added packet/byte counts (makes this tool so much more useful)
*
* Added "once" mode (basically, an improved, colorful netstat(1) - run and exit)
*
* Added 'K' (display in K/M/G), 'P' (display packets/bytes), and more.
*
*
* TODO: When network activity is high, I poll counts (1004) frequently, which leads to high CPU
* utilization (but accurate results :-)
*
* better colors?
*
* use gai to resolve port numbers
*
* Format IPv6 better (full addresses mess up tabulation)
*
* State of TCP connections (especially in nc mode)
*
* No toggle of udp/tcp/packets for "once" (in fact, just one command line argument)
*
* Assimilate nettop entirely
*
* (I'm integrating nettop into procexp, so no promises about maintaining this - but
* your emails and comments are always welcome)
*
*/
int process_nstat_msg (void *msg, int size);
int g_OsVer = 12; // Sierra by default
char *resolveAddr (struct sockaddr *addr, int family)
{
static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (getnameinfo(addr, addr->sa_len, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf),NI_NAMEREQD )) {
return NULL;
}
return (hbuf);
} // resolveAddr
void getOsVer()
{
struct utsname name;
uname (&name);
// ugly hack, for now.. This works with iOS as well, since the structs are common
if (strstr(name.version, "xnu-24")) { g_OsVer = 9; }
if (strstr(name.version, "xnu-20")) { g_OsVer = 8; }
if (strstr(name.version, "xnu-27")) { g_OsVer = 10; }
if (strstr(name.version, "xnu-32")) { g_OsVer = 11; }
if (strstr(name.version, "xnu-37")) { g_OsVer = 12; }
}
int fd;
void print_header (WantCurses)
{
char *header = "Time PID Name \tLocal Addr \tRemote Addr \tIf\tState \tRX\t TX";
if (WantCurses) {
attrset(COLOR_PAIR(0));
attrset(A_UNDERLINE| A_BOLD);
mvwaddstr(stdscr, 1,0 ,header);
attroff(A_UNDERLINE); attron(A_NORMAL);
attrset(COLOR_PAIR(0));
}
else printf ("%s\n", header);
}
int setupSocket(void)
{
struct sockaddr_ctl sc;
struct ctl_info ctlInfo;
int fd;
memset(&ctlInfo, 0, sizeof(ctlInfo));
if (strlcpy(ctlInfo.ctl_name, "com.apple.network.statistics", sizeof(ctlInfo.ctl_name)) >=
sizeof(ctlInfo.ctl_name)) {
fprintf(stderr,"CONTROL NAME too long");
return -1;
}
if ((fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) == -1) {
fprintf(stderr,"socket(SYSPROTO_CONTROL): %s", strerror(errno));
return -1;
}
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
fprintf(stderr,"ioctl(CTLIOCGINFO): %s", strerror(errno));
close(fd);
return -1;
}
sc.sc_id = ctlInfo.ctl_id;
sc.sc_len = sizeof(sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
/*
int bufsize = 0x100000;
int bufsize_len = sizeof(int);
int rc =setsockopt (fd, SOL_SOCKET, SO_RCVBUF , &bufsize, &bufsize_len);
printf("SET SOCKOPT %d\n", rc);
*/
// The open source had a bug here, asking for num +1.
// which meant that it refused to work unless no other clients were
// connected.
sc.sc_unit = 0 ; /* zero means unspecified */
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
fprintf(stderr,"connect(AF_SYS_CONTROL): %s\n", strerror(errno));
if (errno == EBUSY )
{
printf("Apparently some other process is using the system socket. "
"On iOS, this is usually UserEventAgent. Kill it (-9!) and immediately start this program\n");
}
close(fd);
return -1;
}
return fd;
}
const char *stateToText(int State)
{
switch(State)
{
case TCPS_CLOSED: return ("CLOSED");
case TCPS_LISTEN: return ("LISTENING");
case TCPS_ESTABLISHED: return ("ESTABLISHED");
case TCPS_CLOSING: return ("CLOSING");
case TCPS_SYN_SENT: return ("SYN_SENT");
case TCPS_LAST_ACK: return ("LAST_ACK");
case TCPS_CLOSE_WAIT: return ("CLOSE_WAIT");
case TCPS_TIME_WAIT: return ("TIME_WAIT");
case TCPS_FIN_WAIT_1 : return ("FIN_WAIT_1");
case TCPS_FIN_WAIT_2 : return ("FIN_WAIT_2");
default:
return("?");
}
} // stateToText
#define MAX_DESC 2000
char *descriptors[MAX_DESC];
nstat_counts descriptorCounts[MAX_DESC];
int maxDesc = 0;
char *message = " ";
void setMessage (char *Message)
{
message = Message;
}
void print_descriptors()
{
int i;
int j = 2;
if (!wantUDP && !wantTCP) { setMessage ( "Warning: Both TCP and UDP are filtered");}
if ( wantCurses) {
attrset(COLOR_PAIR(0) | A_BOLD);
mvwaddstr(stdscr, 0,0 ,message);
attroff(A_BOLD);
clrtoeol();
print_header(wantCurses);
}
if (wantCurses && wantRefresh)
{
wantRefresh = 0;erase();
}
for (i = 0; i < MAX_DESC; i++)
{
if (descriptors[i] && strcmp(descriptors[i], "reading") !=0)
{
// This is a quick and dirty example - So instead of mucking around with
// descriptor data structures, I look at the text output of each, and figure
// out from it whether or not it's UDP, or the TCP state, etc..
if (strstr(descriptors[i], "N/A")) // UDP have a state of "N/A"
{
if ( ! wantUDP) continue;
if (wantColors) {
if (wantCurses) { init_pair(4,COLOR_CYAN, COLOR_BLACK); attrset(COLOR_PAIR(4)); }
else
printf (GREEN);
}
}
else // TCP (not showing route for now)
{
if (!wantTCP) continue;
if (wantColors)
{
if (wantCurses) attrset(COLOR_PAIR(0)); else printf(NORMAL);
}
if (strstr(descriptors[i], "ESTABLISHED") ) {
if (wantColors)
{
if (wantCurses) { init_pair(3, COLOR_GREEN, COLOR_BLACK); attrset(COLOR_PAIR(3));}
else printf(GREEN);
}
} // ESTABLISHED
if (strstr(descriptors[i], "LISTEN") ) {
if (!wantListen) continue;
if (wantColors)
{
if (wantCurses) { init_pair(2, COLOR_WHITE, COLOR_BLACK); attrset(COLOR_PAIR(2));}
else printf(BLUE);
}
} // ESTABLISHED
if (strstr(descriptors[i], "SYN_SENT") ) {
if (wantColors) {
if ( wantCurses) { init_pair(5, COLOR_RED, COLOR_BLACK); attrset(COLOR_PAIR(5));}
else printf(RED);
}
} // SYN_SENT
} // TCP
if (wantCurses) {
mvwaddstr(stdscr, j,0 ,descriptors[i]);
clrtoeol();
}
else
{
printf( "%s\n", descriptors[i]);
}
j++;
if (wantColors) {
if ( wantCurses) { attrset(COLOR_PAIR(0));}
else printf(NORMAL);
}
}
}
clrtobot();
refresh();
}
char *print_udp_descriptor (char *desc, int prov, int num)
{
nstat_udp_descriptor *nud = (nstat_udp_descriptor *) desc;
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
char *resolved;
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
memset(timeBuf,'\0', 30);
strftime(timeBuf, 29, "%H:%M:%S", n);
// sprintf(timeBuf, "%d......", num);
//sprintf (returned + strlen(returned), "%-8s ", timeBuf);
sprintf(returned + strlen(returned), "%d", num);
sprintf(returned + strlen(returned), "%6d\t", nud->pid);
if (nud->pname[0]) { sprintf(returned + strlen(returned), "%-16s\t", nud->pname );}
else { sprintf(returned + strlen(returned), " \t");}
if (nud->local.v4.sin_family == AF_INET6) {
ipv6++; }
if (!ipv6)
{
localPort = ntohs(nud->local.v4.sin_port);
remotePort = ntohs(nud->remote.v4.sin_port);
}
else {
localPort = ntohs(nud->local.v6.sin6_port);
remotePort = ntohs(nud->remote.v6.sin6_port);
}
if (nud->remote.v6.sin6_family == AF_INET6) {
inet_ntop(nud->local.v6.sin6_family,&(nud->local.v6.sin6_addr), localAddrPrint, 40);
inet_ntop(nud->remote.v6.sin6_family,&(nud->remote.v6.sin6_addr), remoteAddrPrint, 40);
if (!noResolve) resolved = resolveAddr(&(nud->remote.v6), AF_INET6);
if (noResolve || !resolved) resolved = remoteAddrPrint;
}
if (nud->remote.v4.sin_family == AF_INET) {
inet_ntop(nud->local.v4.sin_family,&(nud->local.v4.sin_addr), localAddrPrint, 40);
inet_ntop(nud->remote.v4.sin_family,&(nud->remote.v4.sin_addr), remoteAddrPrint, 40);
if (!noResolve) resolved = resolveAddr(&(nud->remote.v4), AF_INET);
if (noResolve || !resolved) resolved = remoteAddrPrint;
}
//if (localAddrPrint)
{
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-21s %-5hu\t", localAddrPrint, resolved, (unsigned short) remotePort);
}
sprintf(returned + strlen(returned), "%d\tN/A \t", nud->ifindex);
if (wantPackets)
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
else
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);
return (returned);
}
void refreshSrc (int Prov, int Num)
{
int rc ;
nstat_msg_get_src_description gsdreq;
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=Num;
gsdreq.hdr.context = 2410; ;//;nmsa->provider;
rc = write (fd, &gsdreq, sizeof(gsdreq));
// Get reply
char c[2048];
rc = read(fd,c,2048);
nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
process_nstat_msg (ns, rc);
}
// @TODO: Merge common into print_descriptor, then udp/tcp specifics in each of these
// functions..
char *print_tcp_descriptor (char *desc, int prov, int num)
{
#ifdef PRE_37xx
nstat_tcp_descriptor *ntd = (nstat_tcp_descriptor *) desc;
#else
nstat_tcp_descriptor_10_12 *ntd = (nstat_tcp_descriptor_10_12 *) desc;
#endif
#if 0
int i = 0;
fprintf(stderr, "\nDESC:\n 0: ");
for (i = 0; i < 160; i++)
{
unsigned char *debug = (char *) ntd;
fprintf (stderr, "%x ", debug[i]);
if (i %16 == 15) { fprintf(stderr, "\n%d: ", i+1);}
}
#endif
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
strftime(timeBuf, 29, "%H:%M:%S", n);
//sprintf(timeBuf, "%d......", num);
// sprintf (returned + strlen(returned), "%-8s ", timeBuf);
sprintf(returned + strlen(returned), "%d", num);
// This was changed in ML - this might change again..
char *pname;
int pid;
char *resolved =NULL;
switch (g_OsVer)
{
case 7:
pid = ntd->pid;
pname = (char *)&ntd->pname;
break;
case 8: case 9:
pid = ((nstat_tcp_descriptor_10_8 *) ntd)->pid;
pname = ((nstat_tcp_descriptor_10_8 *) ntd)->pname;
break;
case 10:
pid = ((nstat_tcp_descriptor_10_10 *) ntd)->pid;
pname = ((nstat_tcp_descriptor_10_10 *) ntd)->pname;
break;
case 11: case 12:
pid = ((nstat_tcp_descriptor_10_11 *) ntd)->pid;
pname = ((nstat_tcp_descriptor_10_11 *) ntd)->pname;
break;
default: // now changed to 10.12..
// When we get to 10.12, we'll deal with it.. for now , stay silent
pname = NULL;
pid = -1;
break;
}
sprintf(returned + strlen(returned), "%6d\t", pid);
if (pname[0]) { sprintf(returned + strlen(returned), "%-16s\t", pname );}
else { sprintf(returned + strlen(returned), " \t");}
// sprintf(returned+strlen(returned), "%d\t", ntd->sndbufused);
if (ntd->local.v4.sin_family == AF_INET6) {
//printf("IPv6\t");
ipv6++; }
if (!ipv6)
{
localPort = ntohs(ntd->local.v4.sin_port);
remotePort = ntohs(ntd->remote.v4.sin_port);
}
else {
localPort = ntohs(ntd->local.v6.sin6_port);
remotePort = ntohs(ntd->remote.v6.sin6_port);
}
if (ntd->remote.v6.sin6_family == AF_INET6) {
inet_ntop(ntd->local.v6.sin6_family,&(ntd->local.v6.sin6_addr), localAddrPrint, 40);
inet_ntop(ntd->remote.v6.sin6_family,&(ntd->remote.v6.sin6_addr), remoteAddrPrint, 40);
if (!noResolve) { resolved = resolveAddr(&(ntd->remote.v6), AF_INET6); }
if (noResolve || !resolved) resolved = remoteAddrPrint;
} // AF_INET 6
if (ntd->remote.v4.sin_family == AF_INET) {
inet_ntop(ntd->local.v4.sin_family,&(ntd->local.v4.sin_addr), localAddrPrint, 40);
inet_ntop(ntd->remote.v4.sin_family,&(ntd->remote.v4.sin_addr), remoteAddrPrint, 40);
if (!noResolve) { resolved = resolveAddr(&(ntd->remote.v4), AF_INET); }
if (noResolve || !resolved) resolved = remoteAddrPrint;
}
if (resolved[0])
{
sprintf (remoteAddrPrint + strlen(remoteAddrPrint),":%-5hu", (unsigned short) remotePort);
}
else sprintf (remoteAddrPrint, "*.*");
// if (localAddrPrint)
{
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-25s\t", localAddrPrint, resolved);
}
sprintf(returned + strlen(returned), "%d\t%-10s\t", ntd->ifindex, stateToText(ntd->state));
if (wantPackets)
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
else
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);
if ( strstr(returned, "CLOSED") && strstr(returned,"("))
{
refreshSrc(prov,num);
}
return (returned);
}
void humanReadable (uint64_t number, char *output)
{
char unit = 'B';
float num = (float) number;
if (num < 1024) {}
else if ( num < 1024*1024) { unit='K'; num /=1024;}
else if ( num < 1024*1024*1024) { unit='M'; num /=(1024*1024);}
else if ( num < 1024UL*1024UL*1024UL*1024UL) { unit='G'; num /=(1024*1024 *1024);}
else { // let's not get carried away here...
}
sprintf (output, "%5.2f%c", num, unit);
}
void update_provider_statistics (int prov)
{
// the descriptors array holds a textual printable output. I'm piggybacking on that fact
// and doing minor string parsing, rather than keep all the src_description structs in
// memory. Let's not forget this is PoC code
char *desc = descriptors[prov];
if (desc)
{
// get last tab..
char *col = strrchr (desc,'\t');
// get penultimate tab
if (col)
{
*col ='\0';
col = strrchr (desc,'\t');
if (col)
{
*(col) = '\0';
char humanReadableRX [15];
char humanReadableTX [15];
if (wantPackets)
sprintf(desc + strlen(desc), "\t%7lld\t%7lld", descriptorCounts[prov].nstat_rxpackets, descriptorCounts[prov].nstat_txpackets);
else
if (wantHumanReadable)
{
humanReadable(descriptorCounts[prov].nstat_rxbytes, humanReadableRX);
humanReadable(descriptorCounts[prov].nstat_txbytes, humanReadableTX);
sprintf(desc + strlen(desc), "\t%s\t%s", humanReadableRX, humanReadableTX);
}
else
{
sprintf(desc + strlen(desc), "\t%lld\t%lld", descriptorCounts[prov].nstat_rxbytes, descriptorCounts[prov].nstat_txbytes);
}
}
}
}
}
int process_nstat_msg (void *msg, int size)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) msg;
nstat_msg_get_src_description gsdreq;
int rc = 0;
switch (ns->type)
{
case NSTAT_MSG_TYPE_SRC_ADDED : // 10001:
{
nstat_msg_src_added *nmsa = (nstat_msg_src_added *) (ns);
uint64_t i = nmsa->srcref;
// Initialize counts for a new source
memset(&descriptorCounts[i], '\0', sizeof(nstat_counts));
if (g_debug) { fprintf(stderr,"added src %llx\n", nmsa->srcref); }
descriptors[i] = "reading"; //nmsa->srcref] = "reading";
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=(int)nmsa->srcref;
gsdreq.hdr.context = 2411; // This way I can tell if errors get returned for dead sources
rc = write (fd, &gsdreq, sizeof(gsdreq));
break;
}
case NSTAT_MSG_TYPE_SRC_REMOVED: // = 10002
{
//struct nstat_msg_src_removed *rem;
nstat_msg_src_removed *nmsr = (nstat_msg_src_removed *) (ns);
if (g_debug) { fprintf(stderr,"removed src %llx\n", nmsr->srcref); }
if (descriptors[nmsr->srcref])
{
free(descriptors[nmsr->srcref] ); // = NULL; // @TODO: free..
descriptors[nmsr->srcref] = NULL; // @TODO: free..
// force a refresh
if (wantCurses)
{
print_descriptors();
}
}
break;
}
case NSTAT_MSG_TYPE_SRC_COUNTS: // = 10004
{
nstat_msg_src_counts *nmsc = (nstat_msg_src_counts *) (ns );
memcpy (&(descriptorCounts[nmsc->srcref]), &(nmsc->counts), sizeof (nstat_counts));
update_provider_statistics (nmsc->srcref);
}
break;
case NSTAT_MSG_TYPE_SRC_DESC: // = 10003
{
#ifdef PRE_37xx
nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
#else
nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
#endif
switch (nmsd->provider)
{
case NSTAT_PROVIDER_TCP_KERNEL:
case NSTAT_PROVIDER_TCP_USERLAND:
descriptors[nmsd->srcref] = print_tcp_descriptor((char *)nmsd->data, nmsd->provider,nmsd->srcref);
break;
case NSTAT_PROVIDER_UDP_KERNEL:
case NSTAT_PROVIDER_UDP_USERLAND:
descriptors[nmsd->srcref] = print_udp_descriptor((char *) nmsd->data, nmsd->provider, nmsd->srcref);
break;
}
if (g_debug) { fprintf(stderr,"Description for src %llx (provider %d) - %s:\n", nmsd->srcref, nmsd->provider, descriptors[nmsd->srcref]); }
}
break;
case 1:
{
if (ns->context < MAX_DESC && !descriptors[ns->context]) break;
nstat_msg_error *err = (nstat_msg_error *) ns;
// printf("Error: %s (%d) for context %d\n", strerror(err->error),err->error, ns->context); return rc;
break;
}
case 0: printf("Success for context %x\n", ns->context); break;
break;
default:
printf("Type : %d\n", ns->type);
}
fflush(NULL);
return (ns->type) ;
}
int addAll(int fd, int provider)
{
int rc;
// #@$@#%$#% AAPL can't keep anything internal stable for more than one generation
// 02/15/2017
if (g_OsVer > 11) {
struct nstat_msg_add_all_srcs_as_of_xnu_37xx aasreq;
bzero( &aasreq, sizeof(aasreq));
aasreq.provider = provider ;
aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
aasreq.hdr.context = 3;
rc = write (fd, &aasreq, sizeof(aasreq));
}
else
if (g_OsVer > 10) {
nstat_msg_add_all_srcs_as_of_10_11 aasreq;
aasreq.provider = provider ;
aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
aasreq.hdr.context = 3;
rc = write (fd, &aasreq, sizeof(aasreq));
}
else
{
nstat_msg_add_all_srcs aasreq;
aasreq.provider = provider ;
aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
aasreq.hdr.context = 3;
rc = write (fd, &aasreq, sizeof(aasreq));
}
if (rc < 0)
{
fprintf(stderr,"addAll: Error during write(2) operation : %s\n", strerror(errno));
return (rc);
}
// no process reply
for (;;) // we'll exit on error on success anyway
{
char c[20480];
rc = read(fd,c,20480);
if (rc < 0)
{
fprintf(stderr,"addAll: Error during read(2) of reply : %s\n", strerror(errno));
return (rc);
}
nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
if (ns->type == 1)
{
nstat_msg_error *err = (nstat_msg_error *) ns;
printf("Error: %s (%d) for context %d\n", strerror(err->error),err->error, ns->context); ///exit(err->error);
}
else if (ns->type == 0) {
if (g_debug)fprintf(stderr,"Source added successfully\n");
break; /* OK */ }
else { //printf("RC: %d, type: %d\n", rc, ns->type);
process_nstat_msg(ns,rc);}// { process_nstat_msg(ns, rc);} // }fprintf(stderr,"Unrecognized reply: %d\n", ns->type);}
}
// now refresh
int src = 0;
for (src = 0; src < MAX_DESC; src++)
{
if ((descriptors[src])) // && (strcmp(descriptors[src], "reading") == 0))
{
fprintf(stderr,"REFRESHING %d\n", src);
refreshSrc(provider, src);
}
}
return 0;
}
void printHelp()
{
fprintf (stderr,"Usage:\n");
fprintf (stderr,"\tlsock \tRun in curses (full screen) mode\n");
fprintf (stderr,"\tlsock tcp \tRun in curses mode, but filter on TCP only (can change with 'T')\n");
fprintf (stderr,"\tlsock udp \tRun in curses mode, but filter on UDP only (can change with 'U')\n");
fprintf (stderr,"\tlsock nc \tRun in filter mode, ongoing\n");
fprintf (stderr,"\tlsock once \tRun in filter mode, and exit\n");
fprintf (stderr,"\tNote ncurses is broken on iOS - either install from cydia, or use supplied 78/x256-color file (TERMINFO=.)\n");
fprintf (stderr,"\nIn curses mode, you can try the following keys:\n");
fprintf (stderr,"\tC\tToggle colors on/off (recommended: on)\n");
fprintf (stderr,"\tK\tDisplay byte counts in K/M/G\n");
fprintf (stderr,"\tP\tToggle byte or packet counts\n");
fprintf (stderr,"\tT\tToggle TCP on/off\n");
fprintf (stderr,"\tU\tToggle UDP on/off\n");
}
int querySources(int fd, int ref)
{
int rc;
nstat_msg_query_src_req qsreq = { 0};
qsreq.hdr.type = ref; //NSTAT_MSG_TYPE_QUERY_SRC ; // 1004
qsreq.srcref= NSTAT_SRC_REF_ALL;
qsreq.hdr.context = 1025; // This way I can tell if errors get returned for dead sources
rc = write (fd, &qsreq, sizeof(qsreq));
char buf[20480];
errno = 0;
rc = read (fd, buf, 20480);
//endwin();
errno = 0;
nstat_msg_hdr *ns = (nstat_msg_hdr *) buf;
if (ns->type ==1)
{
//printf("ERROR: %d on context %d\n", ((nstat_msg_error *) ns)->error, ns->context);
//exit(0);
}
else {
// printf("GOT %d bytes - %d, TYPE: %d\n", rc, ns->context, ns->type);
process_nstat_msg (buf,rc);
}
}
void resetTerm(void)
{
if (wantCurses) { endwin();}
}
int main (int argc, char **argv)
{
atexit(resetTerm);
if (getenv("JDEBUG")) g_debug++;
int rc = 0;
char c[20480];
getOsVer();
memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
fd =setupSocket ();
if (fd == -1)
{
fprintf(stderr,"Unable to continue without socket\n"); exit(1);
}
struct stat stbuf;
fstat(1, &stbuf);
if (stbuf.st_mode & S_IFCHR) { wantColors = 1; wantCurses = 1;} else {wantColors = 0; wantCurses = 0;}
int arg = 1;
for (arg = 1; arg < argc; arg++)
{
if (strcmp(argv[arg],"-n") == 0) noResolve = 1;
else
if (strcmp(argv[arg], "nc") == 0) wantCurses = 0;
else if (strcmp(argv[arg], "once") == 0) {wantCurses = 0; wantOnce = 1; wantColors = 0;}
else if (strcmp(argv[arg], "udp") == 0) { wantUDP = 1; wantTCP = 0;}
else if (strcmp(argv[arg], "tcp") == 0) { wantTCP = 1; wantUDP = 0;}
else { printHelp(); exit(2);}
}
// wantOnce = 1; wantCurses = 0 ;wantOnce= 1; wantColors=0;
if (getenv("JCOLOR")) wantColors =1;
else wantColors = 0;
nstat_msg_query_src_req qsreq;
char ch;
// Want both - we'll just filter
int udpAdded = 0;
int tcpAdded = 0 ;
int gettingCounts = 0 ;
int gotCounts = 0;
if (wantTCP)
{
rc = addAll (fd, NSTAT_PROVIDER_TCP_KERNEL);
if (rc !=0){ fprintf(stderr,"Unable to add TCP kernel provider - exiting\n"); exit(1);}
rc = addAll (fd, NSTAT_PROVIDER_TCP_USERLAND);
if (rc !=0){ fprintf(stderr,"Unable to add TCP userland provider - exiting\n"); exit(1);}
}
if (wantUDP)
{
rc = addAll (fd, NSTAT_PROVIDER_UDP_KERNEL);
if (rc !=0){ fprintf(stderr,"Unable to add UDP kernel provider - exiting\n"); exit(1);}
rc = addAll (fd, NSTAT_PROVIDER_UDP_USERLAND);
if (rc !=0){ fprintf(stderr,"Unable to add UDP userland provider - exiting\n"); exit(1);}
}
if (wantCurses)
{
initscr(); cbreak();
noecho();
start_color();
nodelay (stdscr,1);
}
print_header(wantCurses);
while (1) {
if (wantCurses || wantOnce)
{
fd_set fds;
struct timeval to;
to.tv_sec = 0;
to.tv_usec = JDELAY;
FD_ZERO (&fds);
FD_SET (fd, &fds);
// select on socket, rather than read..
rc = select(fd + 1, &fds, NULL, NULL, &to);
if (rc > 0) {
rc = read(fd,c,20480);
}
else
{
// Timed out on select: now we can print
if (wantOnce) { print_descriptors(); exit(0);}
else {
print_descriptors();
querySources(fd, 0);
}
}
}
else
rc = read (fd, c, 2048);
// check rc. Meh.
if (rc > 0)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
switch (ns->type)
{
case 10001: case 10002: case 10003: case 10004:
rc = process_nstat_msg(c,rc);
break;
case 0:
// Got all sources, or all counts
// if we were getting sources, ask now for all descriptions
if (!tcpAdded)
{ tcpAdded++;
addAll (fd, NSTAT_PROVIDER_TCP_USERLAND);
addAll (fd, NSTAT_PROVIDER_TCP_KERNEL);
}
else {
if (!udpAdded) udpAdded++;
addAll (fd, NSTAT_PROVIDER_UDP_KERNEL);
addAll (fd, NSTAT_PROVIDER_UDP_USERLAND);
}
if (tcpAdded && udpAdded )
{
if (!gettingCounts)
{
qsreq.hdr.type= NSTAT_MSG_TYPE_QUERY_SRC ; // 1004
qsreq.srcref= NSTAT_SRC_REF_ALL; //0xffffffff; //NSTAT_SRC_REF_ALL;
qsreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources
rc = write (fd, &qsreq, sizeof(qsreq));
gettingCounts++;
}
else gotCounts++;
}
break;
case 1:
//Error message - these are usually for dead sources (context will be 1005)
{
nstat_msg_error *err = (nstat_msg_error *) ns;
printf("Error: %s (%d)\n", strerror(err->error),err->error);
break;
}
default:
break;
}
}
if (wantCurses)
{
ch = getch(); // could do lowercase instead of 'H'/'h', etc...
if (ch != ERR) {
if (ch == 'H' || ch =='h') printf("Too late now :-)\n");
if (ch == 'U' || ch =='u') {wantUDP = !wantUDP; setMessage(wantUDP ? "Showing UDP Sockets" :"Not showing UDP sockets"); }
if (ch == 'T' || ch == 't') { wantTCP = !wantTCP; setMessage(wantTCP ? "Showing TCP Sockets" : "Not showing TCP sockets"); }
if (ch == 'L' || ch == 'l') { wantListen= !wantListen; setMessage("Toggling TCP listeners"); }
if (ch == 'P' || ch == 'p') { wantPackets = !wantPackets; setMessage(wantPackets ? "Showing Packets" : "Showing Bytes"); }
if (ch == 'C' || ch == 'c') { wantColors = !wantColors; setMessage("Toggling color display"); }
if (ch == 'K' || ch == 'k') { wantHumanReadable = !wantHumanReadable; }
if (ch == 'R' || ch == 'r') { noResolve = !noResolve; setMessage(noResolve ? "Not resolving IPs from now on" : "Resolving IPs from now on"); }
if (ch =='Q' || ch == 'q') { if (wantCurses) {endwin();}exit(0);}
}
}
if (wantOnce > 1) exit(24);
if (!wantOnce && gotCounts) {
print_descriptors();
if (!wantCurses) memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
}
} // end while
return (0);
}
#define BOLD "\033[1;1m"
#define RED "\033[0;31m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[0;33m"
#define BLUE "\e[0;34m"
#define NORMAL "\e[0;0m"
#define TCPS_CLOSED 0 /* closed */
#define TCPS_LISTEN 1 /* listening for connection */
#define TCPS_SYN_SENT 2 /* active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
/* states < TCPS_ESTABLISHED are those where connections not established */
#define TCPS_ESTABLISHED 4 /* established */
#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
/* states > TCPS_CLOSE_WAIT are those where user has closed */
#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
enum
{
#ifdef PRE_37xx
NSTAT_PROVIDER_ROUTE = 1
,NSTAT_PROVIDER_TCP = 2
,NSTAT_PROVIDER_UDP = 3
#else
NSTAT_PROVIDER_NONE = 0
,NSTAT_PROVIDER_ROUTE = 1
,NSTAT_PROVIDER_TCP_KERNEL = 2
,NSTAT_PROVIDER_TCP_USERLAND = 3
,NSTAT_PROVIDER_UDP_KERNEL = 4
,NSTAT_PROVIDER_UDP_USERLAND = 5
,NSTAT_PROVIDER_IFNET = 6
,NSTAT_PROVIDER_SYSINFO = 7
#define NSTAT_PROVIDER_UDP NSTAT_PROVIDER_UDP_USERLAND
#define NSTAT_PROVIDER_TCP NSTAT_PROVIDER_TCP_USERLAND
#endif
};
enum
{
// generic response messages
NSTAT_MSG_TYPE_SUCCESS = 0
,NSTAT_MSG_TYPE_ERROR = 1
// Requests
,NSTAT_MSG_TYPE_ADD_SRC = 1001
,NSTAT_MSG_TYPE_ADD_ALL_SRCS = 1002
,NSTAT_MSG_TYPE_REM_SRC = 1003
,NSTAT_MSG_TYPE_QUERY_SRC = 1004
,NSTAT_MSG_TYPE_GET_SRC_DESC = 1005
// Responses/Notfications
,NSTAT_MSG_TYPE_SRC_ADDED = 10001
,NSTAT_MSG_TYPE_SRC_REMOVED = 10002
,NSTAT_MSG_TYPE_SRC_DESC = 10003
,NSTAT_MSG_TYPE_SRC_COUNTS = 10004
};
enum
{
NSTAT_SRC_REF_ALL_PRE_37xx = 0xffffffff
,NSTAT_SRC_REF_INVALID = 0,
NSTAT_SRC_REF_ALL = 0xffffffffffffffffULL
};
// These are all defined in kernel headers, which are not
// accessible in user mode (or in iOS). By re-declaring everything here
// I make sure this will also compile on iOS
typedef u_int32_t nstat_provider_id_t;
#ifdef PRE_37xx
typedef u_int32_t nstat_src_ref_t;
#else
typedef u_int64_t nstat_src_ref_t;
#endif
typedef struct nstat_tcp_add_param
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
} nstat_tcp_add_param;
#pragma pack(1)
typedef struct nstat_counts
{
/* Counters */
u_int64_t nstat_rxpackets __attribute__((aligned(8)));
u_int64_t nstat_rxbytes __attribute__((aligned(8)));
u_int64_t nstat_txpackets __attribute__((aligned(8)));
u_int64_t nstat_txbytes __attribute__((aligned(8)));
u_int32_t nstat_rxduplicatebytes;
u_int32_t nstat_rxoutoforderbytes;
u_int32_t nstat_txretransmit;
u_int32_t nstat_connectattempts;
u_int32_t nstat_connectsuccesses;
u_int32_t nstat_min_rtt;
u_int32_t nstat_avg_rtt;
u_int32_t nstat_var_rtt;
} nstat_counts;
#pragma pack()
#pragma pack(push, 4)
typedef struct nstat_msg_hdr
{
u_int64_t context;
u_int32_t type;
u_int32_t pad; // unused for now
} nstat_msg_hdr;
typedef struct nstat_msg_error
{
nstat_msg_hdr hdr;
u_int32_t error; // errno error
} nstat_msg_error;
typedef struct nstat_msg_add_all_srcs
{
nstat_msg_hdr hdr;
nstat_provider_id_t provider;
} nstat_msg_add_all_srcs;
typedef struct nstat_msg_add_all_srcs_as_of_10_11
{
nstat_msg_hdr hdr;
nstat_provider_id_t provider;
u_int64_t filter;
} nstat_msg_add_all_srcs_as_of_10_11;
typedef u_int64_t nstat_event_flags_t;
typedef struct nstat_msg_add_all_srcs_as_of_xnu_37xx
{
nstat_msg_hdr hdr;
nstat_provider_id_t provider;
u_int64_t filter;
nstat_event_flags_t events;
pid_t target_pid;
uuid_t target_uuid;
} nstat_msg_add_all_srcs_as_of_xnu_37xx;
#ifdef PRE_37xx
typedef struct nstat_msg_src_description
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
nstat_provider_id_t provider;
u_int8_t data[];
} nstat_msg_src_description;
#else
typedef struct nstat_msg_src_description_10_12
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
nstat_event_flags_t event_flags;
nstat_provider_id_t provider;
u_int8_t data[];
} nstat_msg_src_description;
#endif
typedef struct nstat_msg_query_src
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
} nstat_msg_query_src_req;
typedef struct nstat_msg_src_added
{
nstat_msg_hdr hdr;
nstat_provider_id_t provider;
nstat_src_ref_t srcref;
} nstat_msg_src_added;
typedef struct nstat_msg_src_removed
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
} nstat_msg_src_removed;
typedef struct nstat_msg_src_counts
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
nstat_counts counts;
} nstat_msg_src_counts;
typedef struct nstat_msg_get_src_description
{
nstat_msg_hdr hdr;
nstat_src_ref_t srcref;
} nstat_msg_get_src_description;
#pragma pack(1)
typedef struct nstat_tcp_descriptor_10_8
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t state;
u_int32_t sndbufsize;
u_int32_t sndbufused;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t txunacked;
u_int32_t txwindow;
u_int32_t txcwindow;
// This was added in 10_8, pushing the rest by four bytes
u_int32_t traffic_class;
u_int64_t upid;
u_int32_t pid;
char pname[64];
// And so was this..
u_int64_t eupid;
u_int32_t epid;
uint8_t uuid[16];
uint8_t euuid[16];
} nstat_tcp_descriptor_10_8;
typedef struct nstat_tcp_descriptor_10_10
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t state;
u_int32_t sndbufsize;
u_int32_t sndbufused;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t txunacked;
u_int32_t txwindow;
u_int32_t txcwindow;
// This was added in 10_8, pushing the rest by four bytes
u_int32_t traffic_class;
// Some 20 bytes were added here. "cubic.."
char dunno[20];
u_int64_t upid;
u_int32_t pid;
char pname[64];
// And so was this..
u_int64_t eupid;
u_int32_t epid;
uint8_t uuid[16];
uint8_t euuid[16];
} nstat_tcp_descriptor_10_10;
// 03/16/16 - added 10.11
struct tcp_conn_status {
unsigned int probe_activated : 1;
unsigned int write_probe_failed : 1;
unsigned int read_probe_failed : 1;
unsigned int conn_probe_failed : 1;
};
typedef struct nstat_tcp_descriptor_10_11
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t state;
u_int32_t sndbufsize;
u_int32_t sndbufused;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t txunacked;
u_int32_t txwindow;
u_int32_t txcwindow;
u_int32_t traffic_class;
u_int32_t traffic_mgt_flags;
char cc_algo[16];
u_int64_t upid;
u_int32_t pid;
char pname[64];
u_int64_t eupid;
u_int32_t epid;
uint8_t uuid[16];
uint8_t euuid[16];
uint8_t vuuid[16];
struct tcp_conn_status connstatus;
uint16_t ifnet_properties __attribute__((aligned(4)));
} nstat_tcp_descriptor_10_11;
typedef struct nstat_tcp_descriptor
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t state;
u_int32_t sndbufsize;
u_int32_t sndbufused;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t txunacked;
u_int32_t txwindow;
u_int32_t txcwindow;
u_int32_t traffic_class;
u_int32_t traffic_mgt_flags;
char cc_algo[16];
u_int64_t upid;
u_int32_t pid;
char pname[64];
u_int64_t eupid;
u_int32_t epid;
uuid_t uuid;
uuid_t euuid;
uuid_t vuuid;
struct tcp_conn_status connstatus;
uint16_t ifnet_properties __attribute__((aligned(4)));
} nstat_tcp_descriptor_10_12;
typedef struct nstat_tcp_descriptor_old
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t state;
u_int32_t sndbufsize;
u_int32_t sndbufused;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t txunacked;
u_int32_t txwindow;
u_int32_t txcwindow;
u_int64_t upid;
u_int32_t pid;
char pname[64];
} nstat_tcp_descriptor;
#pragma pack()
typedef struct nstat_udp_descriptor
{
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} local;
union
{
struct sockaddr_in v4;
struct sockaddr_in6 v6;
} remote;
u_int32_t ifindex;
u_int32_t rcvbufsize;
u_int32_t rcvbufused;
u_int32_t traffic_class;
u_int64_t upid;
u_int32_t pid;
char pname[64];
#ifndef PRE_37xx
u_int64_t eupid;
u_int32_t epid;
uuid_t uuid;
uuid_t euuid;
uuid_t vuuid;
uint16_t ifnet_properties;
#endif
} nstat_udp_descriptor;
//
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> // malloc
#include <arpa/inet.h> // inet_ntop
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include "lsock.h"
#include <ncurses.h>
int wantCurses =1;
int wantHumanReadable = 0;
int wantPackets = 0;
int wantOnce = 0;
int wantUDP = 1;
int wantListen = 1;
int wantTCP = 1;
int wantColors = 1;
int wantRefresh = 0;
#define DELAY 100000// 0.1 second
/**
* lsock.c : A TCPView inspired netstat(1)-clone (in text mode) for OS X/iOS
*
* Coded by Jonathan Levin - http://www.newosxbook.com/
*
* Consider this the missing source of nettop (with a few improvements):
* AAPL uses the com.apple.network.statistics socket through the NetworkStatistics PrivateFramework.
* This basically shows the same usage, without the dispatch queues and with direct access to the
* system socket.
*
* No (C). Distribute at will.
*
* The makefile, universal binary and iOS CLI compilation script can be found in the tar this came in.
*
* Note you need "lsock.h" to compile (these are the ntstat headers from kernel mode, which AAPL
* never bothered to publish). You can find the .h in the same URL you found this, or in the tar file.
*
* ----------------------------------------------------------------------------------------------
* Arguments: "nc" - disable full-screen (will automatically disable curses if piping output)
* "once" - one shot - automatically disables curses
* "tcp" - start in TCP-only mode
* "udp" - start in UDP-only mode
*
* Commands for full screen mode: 'T' - TCP, 'U' - UDP, 'L' - Listeners, 'C' - Colors
*
* In CLI/nc mode, delimiters are tabs - this makes it useful to use this command as a continuous
* source for cut -d'(tab)' and select only the fields which interest you.
*
* Possible improvements
*
* - GUI: Make this more like Windows' TCPView and Procmon
* - Remote monitoring
* - Add routing provider (prov #1)
* - Show provider statistics, too
*
* ChangeLog:
*
* 06/14/2014 - Plenty of improvements, including:
* Fixed bug in the public version which prevented binding if any other ntstat
* client was registered.. (e.g. UserEventAgent - This one's for you, Jason)
*
* Ordered requests to control socket so that all descriptors are shown, always (no race, etc)
*
* Updated for Yosemite and Mavericks (tested on ML, Yosemite, iOS 7, but not on Mavericks or iOS8)
* When the folks @ Cupertino share the xnu 27 headers, I'll update again
*
* Added packet/byte counts (makes this tool so much more useful)
*
* Added "once" mode (basically, an improved, colorful netstat(1) - run and exit)
*
* Added 'K' (display in K/M/G), 'P' (display packets/bytes), and more.
*
*
* TODO: When network activity is high, I poll counts (1004) frequently, which leads to high CPU
* utilization (but accurate results :-)
*
* better colors?
*
* use gai to resolve port numbers
*
* Format IPv6 better (full addresses mess up tabulation)
*
* State of TCP connections (especially in nc mode)
*
* No toggle of udp/tcp/packets for "once" (in fact, just one command line argument)
*
* Assimilate nettop entirely
*
* (I'm integrating nettop into procexp, so no promises about maintaining this - but
* your emails and comments are always welcome)
*
*/
int g_OsVer = 7; // Lion by default
void
getOsVer()
{
struct utsname name;
uname (&name);
// ugly hack, for now.. This works with iOS as well, since the structs are common
if (strstr(name.version, "xnu-24"))
{
g_OsVer = 9;
}
else if (strstr(name.version, "xnu-20"))
{
g_OsVer = 8;
}
else if (strstr(name.version, "xnu-27"))
{
g_OsVer = 10;
}
}
int fd;
void
print_header (WantCurses)
{
char *header = "Time PID Name \tLocal Addr \tRemote Addr \tIf\tState \tRX\t TX";
if (WantCurses) {
attr
set(COLOR_PAIR(0));
attrset(A_UNDERLINE| A_BOLD);
mvwaddstr(stdscr, 1,0 ,header);
attroff(A_UNDERLINE); attron(A_NORMAL);
attrset(COLOR_PAIR(0));
}
else printf ("%s\n", header);
}
int setupSocket(void)
{
struct sockaddr_ctl sc;
struct ctl_info ctlInfo;
int fd;
memset(&ctlInfo, 0, sizeof(ctlInfo));
if (strlcpy(ctlInfo.ctl_name, "com.apple.network.statistics", sizeof(ctlInfo.ctl_name)) >=
sizeof(ctlInfo.ctl_name)) {
fprintf(stderr,"CONTROL NAME too long");
return -1;
}
if ((fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) == -1) {
fprintf(stderr,"socket(SYSPROTO_CONTROL): %s", strerror(errno));
return -1;
}
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
fprintf(stderr,"ioctl(CTLIOCGINFO): %s", strerror(errno));
close(fd);
return -1;
}
sc.sc_id = ctlInfo.ctl_id;
sc.sc_len = sizeof(sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
// The open source had a bug here, asking for num +1.
// which meant that it refused to work unless no other clients were
// connected.
sc.sc_unit = 0 ; /* zero means unspecified */
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
fprintf(stderr,"connect(AF_SYS_CONTROL): %s\n", strerror(errno));
if (errno == EBUSY )
{
printf("Apparently some other process is using the system socket. "
"On iOS, this is usually UserEventAgent. Kill it (-9!) and immediately start this program\n");
}
close(fd);
return -1;
}
return fd;
}
const char *
stateToText(int State)
{
switch(State)
{
case TCPS_CLOSED: return ("CLOSED");
case TCPS_LISTEN: return ("LISTENING");
case TCPS_ESTABLISHED: return ("ESTABLISHED");
case TCPS_CLOSING: return ("CLOSING");
case TCPS_SYN_SENT: return ("SYN_SENT");
case TCPS_LAST_ACK: return ("LAST_ACK");
case TCPS_CLOSE_WAIT: return ("CLOSE_WAIT");
case TCPS_TIME_WAIT: return ("TIME_WAIT");
case TCPS_FIN_WAIT_1 : return ("FIN_WAIT_1");
case TCPS_FIN_WAIT_2 : return ("FIN_WAIT_2");
default:
return("?");
}
} // stateToText
#define MAX_DESC 2000
char *descriptors[MAX_DESC];
nstat_counts descriptorCounts[MAX_DESC];
int maxDesc = 0;
char *message = " ";
void setMessage (char *Message)
{
message = Message;
}
void
print_descriptors()
{
int i;
int j = 3;
if (!wantUDP && !wantTCP) { setMessage ( "Warning: Both TCP and UDP are filtered");}
if (wantCurses) {
attrset(COLOR_PAIR(0) | A_BOLD);
mvwaddstr(stdscr, 2,0 ,message);
attroff(A_BOLD);
clrtoeol();
}
if (wantCurses && wantRefresh)
{
wantRefresh = 0; erase();print_header(wantCurses);
}
for (i = 0; i < MAX_DESC; i++)
{
if (descriptors[i] && strcmp(descriptors[i], "reading") !=0)
{
// This is a quick and dirty example - So instead of mucking around with
// descriptor data structures, I look at the text output of each, and figure
// out from it whether or not it's UDP, or the TCP state, etc..
if (strstr(descriptors[i], "N/A")) // UDP have a state of "N/A"
{
if ( ! wantUDP) continue;
if (wantColors) {
if (wantCurses) { init_pair(4,COLOR_CYAN, COLOR_BLACK); attrset(COLOR_PAIR(4)); }
else
printf (GREEN);
}
}
else // TCP (not showing route for now)
{
if (!wantTCP) continue;
if (wantColors)
{
if (wantCurses) attrset(COLOR_PAIR(0)); else printf(NORMAL);
}
if (strstr(descriptors[i], "ESTABLISHED") ) {
if (wantColors)
{
if (wantCurses) { init_pair(3, COLOR_GREEN, COLOR_BLACK); attrset(COLOR_PAIR(3));}
else printf(GREEN);
}
} // ESTABLISHED
if (strstr(descriptors[i], "LISTEN") ) {
if (!wantListen) continue;
if (wantColors)
{
if (wantCurses) { init_pair(2, COLOR_WHITE, COLOR_BLACK); attrset(COLOR_PAIR(2));}
else printf(BLUE);
}
} // ESTABLISHED
if (strstr(descriptors[i], "SYN_SENT") ) {
if (wantColors) {
if ( wantCurses) { init_pair(5, COLOR_RED, COLOR_BLACK); attrset(COLOR_PAIR(5));}
else printf(RED);
}
} // SYN_SENT
} // TCP
if (wantCurses) {
mvwaddstr(stdscr, j,0 ,descriptors[i]);
clrtoeol();
}
else
{
printf( "%s\n", descriptors[i]);
}
j++;
if (wantColors) {
if ( wantCurses) { attrset(COLOR_PAIR(0));}
else printf(NORMAL);
}
}
}
clrtobot();
refresh();
}
char *
print_udp_descriptor (char *desc, int prov, int num)
{
nstat_udp_descriptor *nud = (nstat_udp_descriptor *) desc;
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
const char *localPrint, *remotePrint;
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
memset(timeBuf,'\0', 30);
strftime(timeBuf, 29, "%H:%M:%S", n);
// sprintf(timeBuf, "%d......", num);
sprintf (returned + strlen(returned), "%-8s ", timeBuf);
sprintf(returned + strlen(returned), "%6d\t", nud->pid);
if (nud->pname[0]) { sprintf(returned + strlen(returned), "%-16s\t", nud->pname );}
else { sprintf(returned + strlen(returned), " \t");}
if (nud->local.v4.sin_family == AF_INET6) {
ipv6++; }
if (!ipv6)
{
localPort = ntohs(nud->local.v4.sin_port);
remotePort = ntohs(nud->remote.v4.sin_port);
}
else {
localPort = ntohs(nud->local.v6.sin6_port);
remotePort = ntohs(nud->remote.v6.sin6_port);
}
if (nud->remote.v6.sin6_family == AF_INET6) {
localPrint = inet_ntop(nud->local.v6.sin6_family,&(nud->local.v6.sin6_addr), localAddrPrint, 40);
remotePrint = inet_ntop(nud->remote.v6.sin6_family,&(nud->remote.v6.sin6_addr), remoteAddrPrint, 40);
}
if (nud->remote.v4.sin_family == AF_INET) {
localPrint = inet_ntop(nud->local.v4.sin_family,&(nud->local.v4.sin_addr), localAddrPrint, 40);
remotePrint = inet_ntop(nud->remote.v4.sin_family,&(nud->remote.v4.sin_addr), remoteAddrPrint, 40);
}
if (localAddrPrint) {
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-21s %-5hu\t", localAddrPrint, remoteAddrPrint, (unsigned short) remotePort);
}
sprintf(returned + strlen(returned), "%d\tN/A \t", nud->ifindex);
if (wantPackets)
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
else
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);
return (returned);
}
void refreshSrc (int Prov, int Num)
{
int rc ;
nstat_msg_get_src_description gsdreq;
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=Num;
gsdreq.hdr.context = Prov; ;//;nmsa->provider;
rc = write (fd, &gsdreq, sizeof(gsdreq));
}
// @TODO: Merge common into print_descriptor, then udp/tcp specifics in each of these
// functions..
char *
print_tcp_descriptor (char *desc, int prov, int num)
{
nstat_tcp_descriptor *ntd = (nstat_tcp_descriptor *) desc;
#if 0
int i = 0;
fprintf(stderr, "\nDESC:\n 0: ");
for (i = 0; i < 160; i++)
{
unsigned char *debug = (char *) ntd;
fprintf (stderr, "%x ", debug[i]);
if (i %16 == 15) { fprintf(stderr, "\n%d: ", i+1);}
}
#endif
int ipv6 = 0;
char localAddrPrint[40];
char remoteAddrPrint[40];
const char *localPrint, *remotePrint;
unsigned short localPort, remotePort;
char timeBuf[30];
time_t now;
struct tm *n;
char *returned = (char *) malloc(1024);
returned[0] ='\0'; // Dont need AF_ because we have this already in the address, right?
time(&now);
n = localtime(&now);
strftime(timeBuf, 29, "%H:%M:%S", n);
//sprintf(timeBuf, "%d......", num);
sprintf (returned + strlen(returned), "%-8s ", timeBuf);
// This was changed in ML - this might change again..
char *pname;
int pid;
switch (g_OsVer)
{
case 7:
pid = ntd->pid;
pname = (char *)&ntd->pname;
break;
case 8: case 9:
pid = ((nstat_tcp_descriptor_10_8 *) ntd)->pid;
pname = ((nstat_tcp_descriptor_10_8 *) ntd)->pname;
break;
case 10:
pid = ((nstat_tcp_descriptor_10_10 *) ntd)->pid;
pname = ((nstat_tcp_descriptor_10_10 *) ntd)->pname;
break;
default:
// When we get to 10.11, we'll deal with it.. for now , stay silent
pname = NULL;
pid = -1;
break;
}
sprintf(returned + strlen(returned), "%6d\t", pid);
if (pname[0]) { sprintf(returned + strlen(returned), "%-16s\t", pname );}
else { sprintf(returned + strlen(returned), " \t");}
// sprintf(returned+strlen(returned), "%d\t", ntd->sndbufused);
if (ntd->local.v4.sin_family == AF_INET6) {
//printf("IPv6\t");
ipv6++; }
if (!ipv6)
{
localPort = ntohs(ntd->local.v4.sin_port);
remotePort = ntohs(ntd->remote.v4.sin_port);
}
else {
localPort = ntohs(ntd->local.v6.sin6_port);
remotePort = ntohs(ntd->remote.v6.sin6_port);
}
if (ntd->remote.v6.sin6_family == AF_INET6) {
localPrint = inet_ntop(ntd->local.v6.sin6_family,&(ntd->local.v6.sin6_addr), localAddrPrint, 40);
remotePrint = inet_ntop(ntd->remote.v6.sin6_family,&(ntd->remote.v6.sin6_addr), remoteAddrPrint, 40);
}
if (ntd->remote.v4.sin_family == AF_INET) {
localPrint = inet_ntop(ntd->local.v4.sin_family,&(ntd->local.v4.sin_addr), localAddrPrint, 40);
remotePrint = inet_ntop(ntd->remote.v4.sin_family,&(ntd->remote.v4.sin_addr), remoteAddrPrint, 40);
}
if (remoteAddrPrint)
{
sprintf (remoteAddrPrint + strlen(remoteAddrPrint),":%-5hu", (unsigned short) remotePort);
}
else sprintf (remoteAddrPrint, "*.*");
if (localAddrPrint) {
sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
if (remotePort == 0)
{
sprintf(returned + strlen(returned),"%-21s\t*.* \t ", localAddrPrint);
}
else
sprintf(returned + strlen(returned), "%-21s\t%-25s\t", localAddrPrint, remoteAddrPrint);
}
sprintf(returned + strlen(returned), "%d\t%-10s\t", ntd->ifindex, stateToText(ntd->state));
if (wantPackets)
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
else
sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);
if ( strstr(returned, "CLOSED") && strstr(returned,"("))
{
refreshSrc(prov,num);
}
return (returned);
}
void
humanReadable (uint64_t number, char *output)
{
char unit = 'B';
float num = (float) number;
if (num < 1024) {}
else if ( num < 1024*1024) { unit='K'; num /=1024;}
else if ( num < 1024*1024*1024) { unit='M'; num /=(1024*1024);}
else if ( num < 1024UL*1024UL*1024UL*1024UL) { unit='G'; num /=(1024*1024 *1024);}
else { // let's not get carried away here...
}
sprintf (output, "%5.2f%c", num, unit);
}
void
update_provider_statistics (int prov)
{
// the descriptors array holds a textual printable output. I'm piggybacking on that fact
// and doing minor string parsing, rather than keep all the src_description structs in
// memory. Let's not forget this is PoC code
char *desc = descriptors[prov];
if (desc)
{
// get last tab..
char *col = strrchr (desc,'\t');
// get penultimate tab
if (col)
{
*col ='\0';
col = strrchr (desc,'\t');
if (col)
{
*(col) = '\0';
char humanReadableRX [15];
char humanReadableTX [15];
if (wantPackets)
sprintf(desc + strlen(desc), "\t%7lld\t%7lld", descriptorCounts[prov].nstat_rxpackets, descriptorCounts[prov].nstat_txpackets);
else
if (wantHumanReadable)
{
humanReadable(descriptorCounts[prov].nstat_rxbytes, humanReadableRX);
humanReadable(descriptorCounts[prov].nstat_txbytes, humanReadableTX);
sprintf(desc + strlen(desc), "\t%s\t%s", humanReadableRX, humanReadableTX);
}
else
{
sprintf(desc + strlen(desc), "\t%lld\t%lld", descriptorCounts[prov].nstat_rxbytes, descriptorCounts[prov].nstat_txbytes);
}
}
}
}
}
int
process_nstat_msg (void *msg, int size)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) msg;
nstat_msg_get_src_description gsdreq;
int rc = 0;
switch (ns->type)
{
case NSTAT_MSG_TYPE_SRC_ADDED : // 10001:
{
nstat_msg_src_added *nmsa = (nstat_msg_src_added *) (ns);
// Initialize counts for a new source
memset(&descriptorCounts[nmsa->srcref], '\0', sizeof(nstat_counts));
descriptors[nmsa->srcref] = "reading";
gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
gsdreq.srcref=nmsa->srcref;
gsdreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources
rc = write (fd, &gsdreq, sizeof(gsdreq));
break;
}
case NSTAT_MSG_TYPE_SRC_REMOVED: // = 10002
{
//struct nstat_msg_src_removed *rem;
nstat_msg_src_removed *nmsr = (nstat_msg_src_removed *) (ns);
if (descriptors[nmsr->srcref])
{
// force a refresh
if (wantCurses)
{
print_descriptors();
}
}
descriptors[nmsr->srcref] = NULL; // @TODO: free..
break;
}
case NSTAT_MSG_TYPE_SRC_COUNTS: // = 10004
{
nstat_msg_src_counts *nmsc = (nstat_msg_src_counts *) (ns );
memcpy (&(descriptorCounts[nmsc->srcref]), &(nmsc->counts), sizeof (nstat_counts));
update_provider_statistics (nmsc->srcref);
}
break;
case NSTAT_MSG_TYPE_SRC_DESC: // = 10003
{
nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
switch (nmsd->provider)
{
case NSTAT_PROVIDER_TCP:
descriptors[nmsd->srcref] = print_tcp_descriptor((char *)nmsd->data, nmsd->provider,nmsd->srcref);
break;
case NSTAT_PROVIDER_UDP:
descriptors[nmsd->srcref] = print_udp_descriptor((char *) nmsd->data, nmsd->provider, nmsd->srcref);
break;
}
}
break;
case 1:
{
if (ns->context < MAX_DESC && !descriptors[ns->context]) break;
printf ("ERROR for context %lld - should be available\n", ns->context); break;
}
case 0: // printf("Success\n"); break;
break;
default:
printf("Type : %d\n", ns->type);
}
fflush(NULL);
return (ns->type) ;
}
int
addAll(fd, provider)
{
nstat_msg_add_all_srcs aasreq;
aasreq.provider = provider ;//
aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
aasreq.hdr.context = 3;
int rc = write (fd, &aasreq, sizeof(aasreq));
return rc;
}
void
printHelp()
{
fprintf (stderr,"Usage:\n");
fprintf (stderr,"\tlsock \tRun in curses (full screen) mode\n");
fprintf (stderr,"\tlsock tcp \tRun in curses mode, but filter on TCP only (can change with 'T')\n");
fprintf (stderr,"\tlsock udp \tRun in curses mode, but filter on UDP only (can change with 'U')\n");
fprintf (stderr,"\tlsock nc \tRun in filter mode, ongoing\n");
fprintf (stderr,"\tlsock once \tRun in filter mode, and exit\n");
fprintf (stderr,"\tNote ncurses is broken on iOS - either install from cydia, or use supplied 78/x256-color file (TERMINFO=.)\n");
fprintf (stderr,"\nIn curses mode, you can try the following keys:\n");
fprintf (stderr,"\tC\tToggle colors on/off (recommended: on)\n");
fprintf (stderr,"\tK\tDisplay byte counts in K/M/G\n");
fprintf (stderr,"\tP\tToggle byte or packet counts\n");
fprintf (stderr,"\tT\tToggle TCP on/off\n");
fprintf (stderr,"\tU\tToggle UDP on/off\n");
}
int
main (int argc, char **argv)
{
int rc = 0;
char c[4096];
getOsVer();
memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
fd =setupSocket ();
if (fd == -1)
{
fprintf(stderr,"Unable to continue without socket\n"); exit(1);
}
struct stat stbuf;
fstat(1, &stbuf);
if (stbuf.st_mode & S_IFCHR) { wantColors = 1; wantCurses = 1;} else {wantColors = 0; wantCurses = 0;}
int arg = 1;
for (arg = 1; arg < argc; arg++)
{
if (strcmp(argv[arg], "nc") == 0) wantCurses = 0;
else if (strcmp(argv[arg], "once") == 0) {wantCurses = 0; wantOnce = 1; wantColors = 0;}
else if (strcmp(argv[arg], "udp") == 0) { wantUDP = 1; wantTCP = 0;}
else if (strcmp(argv[arg], "tcp") == 0) { wantTCP = 1; wantUDP = 0;}
else { printHelp(); exit(2);}
}
nstat_msg_query_src_req qsreq;
char ch;
// Want both - we'll just filter
int udpAdded = 0;
int tcpAdded = 0 ;
int gettingCounts = 0 ;
int gotCounts = 0;
addAll (fd, NSTAT_PROVIDER_TCP);
if (wantCurses)
{
initscr(); cbreak();
noecho();
start_color();
nodelay (stdscr,1);
}
print_header(wantCurses);
while (1) { //rc > 0) {
if (wantCurses || wantOnce)
{
fd_set fds;
struct timeval to;
to.tv_sec = 0;
to.tv_usec = DELAY;
FD_ZERO (&fds);
FD_SET (fd, &fds);
// select on socket, rather than read..
rc = select(fd +1, &fds, NULL, NULL, &to);
if (rc > 0) rc = read(fd,c,2048);
else
{
// Timed out on select: now we can print
if (wantOnce ) { print_descriptors(); exit(0);}
else {
qsreq.hdr.type= NSTAT_MSG_TYPE_QUERY_SRC ; // 1004
qsreq.srcref= 0xffffffff; //NSTAT_SRC_REF_ALL;
qsreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources
rc = write (fd, &qsreq, sizeof(qsreq));
}
}
}
else
rc = read (fd, c, 2048);
// check rc. Meh.
if (rc > 0)
{
nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
switch (ns->type)
{
case 10001: case 10002: case 10003: case 10004:
rc = process_nstat_msg(c,rc);
break;
case 0:
// Got all sources, or all counts
// if we were getting sources, ask now for all descriptions
if (!tcpAdded)
{ tcpAdded++;
addAll (fd, NSTAT_PROVIDER_UDP);
}
else { if (!udpAdded) udpAdded++; }
if (tcpAdded && udpAdded )
{
if (!gettingCounts)
{
qsreq.hdr.type= NSTAT_MSG_TYPE_QUERY_SRC ; // 1004
qsreq.srcref= 0xffffffff; //NSTAT_SRC_REF_ALL;
qsreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources
rc = write (fd, &qsreq, sizeof(qsreq));
gettingCounts++;
}
else gotCounts++;
}
break;
case 1:
//Error message - these are usually for dead sources (context will be 1005)
break;
default:
break;
}
}
if (wantCurses)
{
ch = getch(); // could do lowercase instead of 'H'/'h', etc...
if (ch != ERR) {
if (ch == 'H' || ch =='h') printf("Too late now :-)\n");
if (ch == 'U' || ch =='u') {wantUDP = !wantUDP; setMessage(wantUDP ? "Showing UDP Sockets" :"Not showing UDP sockets"); }
if (ch == 'T' || ch == 't') { wantTCP = !wantTCP; setMessage(wantTCP ? "Showing TCP Sockets" : "Not showing TCP sockets"); }
if (ch == 'L' || ch == 'l') { wantListen= !wantListen; setMessage("Toggling TCP listeners"); }
if (ch == 'P' || ch == 'p') { wantPackets = !wantPackets; setMessage(wantPackets ? "Showing Packets" : "Showing Bytes"); }
if (ch == 'C' || ch == 'c') { wantColors = !wantColors; setMessage("Toggling color display"); }
if (ch == 'K' || ch == 'k') { wantHumanReadable = !wantHumanReadable; }
if (ch =='Q' || ch == 'q') { if (wantCurses) {endwin();}exit(0);}
}
}
if (wantOnce > 1) exit(0);
if (!wantOnce && gotCounts) {
print_descriptors();
if (!wantCurses) memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
}
} // end while
return (0);
}
#include <mach/mach.h>
#include <stdio.h>
#include <stdlib.h>
#include <libproc.h>
//#include <mach/thread_info.h>
//#include <mach/task_info.h>
int main(int argc, char **argv)
{
host_t myhost = mach_host_self();
host_t host_priv;
mach_port_t psDefault;
mach_port_t psDefault_control;
task_array_t tasks;
mach_msg_type_number_t numTasks;
int i;
thread_array_t threads;
thread_info_data_t tInfo;
kern_return_t kr;
//hook("/usr/lib/libSystem.B.dylib", "mach_msg", mach_msg_hook);
host_get_host_priv_port(mach_host_self(), &host_priv);
kr = processor_set_default(host_priv, &psDefault);
processor_set_name_array_t *psets = malloc(1024);
mach_msg_type_number_t psetCount;
kr = host_processor_sets (
host_priv, // host_priv_t host_priv,
psets, // processor_set_name_array_t *processor_sets,
&psetCount); // mach_msg_type_number_t *processor_setsCnt
printf("COUNT: %d\n", psetCount);
kr = host_processor_set_priv(host_priv, psDefault, &psDefault_control);
if (kr != KERN_SUCCESS) { fprintf(stderr, "host_processor_set_priv failed with error %x\n", kr); mach_error("host_processor_set_priv",kr); exit(1);}
printf("So far so good\n");
numTasks=1000;
kr = processor_set_tasks(psDefault_control, &tasks, &numTasks);
if (kr != KERN_SUCCESS) { fprintf(stderr,"processor_set_tasks failed with error %x\n",kr); exit(1); }
for (i = 0; i < numTasks; i++)
{
char name[128];
int pid;
pid_for_task(tasks[i], &pid);
int rc= proc_name(pid, name, 128);
printf("PID: %d %s\n",pid,name);
}
return (MACH_PORT_NULL);
}
#include <mach/mach.h>
#include <mach/task.h> // mach_task_self()
#include <stdio.h>
#include <stdlib.h> // exit?
#include <unistd.h> // exit?
int main (int argc, char **argv){
mach_port_t myPort = MACH_PORT_NULL;
kern_return_t kr = mach_port_allocate
(mach_task_self(), // WHERE? ipc_space_t task,
MACH_PORT_RIGHT_RECEIVE, // mach_port_right_t right,
&myPort); // mach_port_name_t *name
if (KERN_SUCCESS != kr) {
fprintf(stderr,"FUD! What does a guy have to do to get a port created?!\n");
exit(1);
}
printf("Hoorah! Habemus Portum - 0x%x\n", myPort);
printf("Incidentally, my self is 0x%x, and bootstrap is 0x%x\n",
mach_task_self(),
bootstrap_port);
// Step II: Enumerate my port space (ipc_space_t)
ipc_info_space_t space_info;
ipc_info_name_array_t table_info;
mach_msg_type_number_t table_infoCnt;
ipc_info_tree_name_array_t tree_info;
mach_msg_type_number_t tree_infoCnt;
kr = mach_port_space_info (mach_task_self(), // ipc_space_inspect_t task,
&space_info, //ipc_info_space_t *space_info,
&table_info, // ipc_info_name_array_t *table_info,
&table_infoCnt, // mach_msg_type_number_t *table_infoCnt,
&tree_info, // ipc_info_tree_name_array_t *tree_info,
&tree_infoCnt); // mach_msg_type_number_t *tree_infoCnt
if (KERN_SUCCESS != kr){
fprintf(stderr,"FUD!\n");
exit(1);
}
#if 0
typedef struct ipc_info_name {
mach_port_name_t iin_name; /* port name, including gen number */
/*boolean_t*/ integer_t iin_collision; /* collision at this entry? */
mach_port_type_t iin_type; /* straight port type */
mach_port_urefs_t iin_urefs; /* user-references */
natural_t iin_object; /* object pointer/identifier */
natural_t iin_next; /* marequest/next in free list */
natural_t iin_hash; /* hash index */
} ipc_info_name_t;
#endif
int p;
for (p = 0; p < table_infoCnt; p++)
{
printf("Port name: 0x%x, Type: 0x%x, Urefs: 0x%x, object: 0x%x\n",
table_info[p].iin_name,
table_info[p].iin_type,
table_info[p].iin_urefs,
table_info[p].iin_object);
}
return 0;
}
#include <mach/mach.h>
#include <mach/mach_port.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main (int argc, char **argv) {
mach_port_t myPort;
mach_port_t target = mach_task_self();
kern_return_t kr;
if (argc > 1) {
uint32_t pid = atoi(argv[1]);
kr = task_for_pid(mach_task_self(),
pid,
&target);
if (kr !=0) {
fprintf(stderr,"No such luck my friend!\n"); exit(1);}
}
kr = mach_port_allocate
(target, // ipc_space_t,
MACH_PORT_RIGHT_RECEIVE, // from port.h
&myPort); // mach_port *
if (kr != KERN_SUCCESS) { exit(1);}
printf("My New Port is 0x%x\n",myPort);
// port identifier returned is a mix of
// an index and a generational number
// e..g 0x707 e.g. 0x1203
// step 2: Enumerating target's port space:
ipc_info_space_t space_info;
ipc_info_name_array_t table_info;
mach_msg_type_number_t table_infoCnt = sizeof(ipc_info_name_array_t);
ipc_info_tree_name_array_t tree_info;
mach_msg_type_number_t tree_infoCnt;
// NOTE: Normally Cnt ("counts") are initialized to sizeof(...)
// upon entry. And - if by ref - will be actual sizeof (in case
// of variable length) on output.
kr = mach_port_space_info ( target, // ipc_space_inspect_t task,
&space_info, // ipc_info_space_t *space_info,
&table_info, // ipc_info_name_array_t *table_info,
&table_infoCnt, // mach_msg_type_number_t *table_infoCnt,
&tree_info, // ipc_info_tree_name_array_t *tree_info,
&tree_infoCnt); // mach_msg_type_number_t *tree_infoCnt
// Tree_info : deprecated (not even set) as of 10.7-8(?)
// we can only care about table and space, and - for this humble sample
// only table
#if 0
iin_name
/*boolean_t*/ integer_t iin_collision; /* collision at this entry? */
mach_port_type_t iin_type; /* straight port type */
mach_port_urefs_t iin_urefs; /* user-references */
natural_t iin_object; /* object pointer/identifier */
natural_t iin_next; /* marequest/next in free list */
natural_t iin_hash; /* hash index */
} ipc_info_name_t;
#endif
int port_index= 0;
for (port_index = 0;
port_index < table_infoCnt;
port_index++) {
printf("Name @%d: is 0x%x (object: 0x%x), Type: 0x%x, urefs: %d\n",
port_index,
table_info[port_index].iin_name,
table_info[port_index].iin_object,
table_info[port_index].iin_type,
table_info[port_index].iin_urefs);
};
sleep(200);
}
arm64:
gcc-arm64 sb.c -DSB459 -lsandbox -o sb.arm64
jtool --sign --inplace sb.arm64
sbtool:
gcc-arm64 -DSB459 liblaunchd.c sbtool.c -o sbtool.arm64
jtool --sign --ent sb.ent --inplace sbtool.arm64
gcc-armv7 -DSB459 liblaunchd.c sbtool.c -o sbtool.armv7
jtool --sign --inplace --ent sb.ent sbtool.armv7
gcc liblaunchd.c sbtool.c -o sbtool.x86_64
lipo -create -arch arm64 sbtool.arm64 -arch x86_64 sbtool.x86_64 -arch armv7 sbtool.armv7 -o sbtool
sbexec:
gcc sandbox_exec.c -o sbexec.x86_64 -lsandbox
gcc-armv7 sandbox_exec.c -o sbexec.armv7 -lsandbox -DSB459
jtool --sign --inplace sbexec.armv7
gcc-arm64 sandbox_exec.c -o sbexec.arm64 -lsandbox -DSB459
jtool --sign --inplace sbexec.arm64
lipo -create -arch arm64 sbexec.arm64 -arch x86_64 sbexec.x86_64 -arch armv7 sbexec.armv7 -o sandbox_exec
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <sys/stat.h>
typedef void *MISProfileRef;
typedef void (^block_handler_t)(MISProfileRef);
// From libmis.h - the missing (still partial) header
// libmis exports some 65 functions in 9.3.1. This shows a dozen.
// some are accessors. But others (notably blacklist, UPP and
// profile creation) will be included in a future version.
#define MIS_ERROR_BASE 0xe8008000
// The #1 function in the library ...
extern int MISValidateSignature (void *File, CFDictionaryRef Opts);
// which is really just a pass through to :
extern int MISValidateSignatureAndCopyInfo (CFStringRef File, CFDictionaryRef Opts, void **Info);
extern CFStringRef MISCopyErrorStringForErrorCode(int Error);
extern int MISEnumerateInstalledProvisioningProfiles (int flags,
block_handler_t);
extern CFStringRef MISProfileGetValue (MISProfileRef, CFStringRef Key);
extern CFStringRef MISProvisioningProfileGetUUID (MISProfileRef);
extern CFStringRef MISProvisioningProfileGetName (MISProfileRef);
extern int MISProvisioningProfileGetVersion (MISProfileRef);
extern CFDictionaryRef MISProvisioningProfileGetEntitlements (MISProfileRef);
extern CFStringRef MISProvisioningProfileGetTeamIdentifier (MISProfileRef);
extern CFArrayRef MISProvisioningProfileGetProvisionedDevices(MISProfileRef);
extern int MISProfileIsMutable(MISProfileRef);
extern int MISProvisioningProfileIsAppleInternalProfile(MISProfileRef);
extern int MISProvisioningProfileIsForLocalProvisioning(MISProfileRef);
extern int MISProvisioningProfileProvisionsAllDevices(MISProfileRef);
extern int MISProvisioningProfileGrantsEntitlement(MISProfileRef, CFStringRef ,void *);
// Validation options - actually CFStringRefs. but whatever
extern void *kMISValidationOptionRespectUppTrustAndAuthorization;
extern void *kMISValidationOptionValidateSignatureOnly;
extern void *kMISValidationOptionUniversalFileOffset;
extern void *kMISValidationOptionAllowAdHocSigning;
extern void *kMISValidationOptionOnlineAuthorization; // triggers online validation of cert
// end header part
/**
* MIStool - A CLI client for libmis.dylib and friends
*
* 04/25/2016
*
* By Jonathan Levin, http://www.NewOSXBook.com/
*
* Part of the free sources accompanying "Mac OS X and *OS Internals"
*
* Free to use and abuse, but give credit where credit is due,
* instead of just copying into GitHub and claiming ownership of
* code you didn't really took the effort to either reverse or rebuild.
*
* To compile: gcc-arm64 mistool.c -o mistool -lmis -framework CoreFoundation -Wall;
* jtool --sign --inplace mistool;
*
* Then scp to your device. Supports "list", "validate" and "errors" command line actions.
*
* At this point, allows you to enumerate/dump profiles. I'd add Insert/Remove, but
* insertion requires a valid dev profile to work with - and I got none.
*
* Expect the reversed source of libmis.dylib (and its nefarious hench-daemons,
* misagent and online-auth-agent) around the same time as MOX*I II - and that's soon -
* I promise. This should give you an idea of just how thorough I'm going to get.
*
*/
// Utility function
char *
dumpCFArray(CFArrayRef Arr)
{
CFIndex count = CFArrayGetCount(Arr);
int v = 0;
const void *val;
static char entVal [8192];
entVal [0] = '\0';
sprintf (entVal , "(%ld values) ", count);
for (v = 0; v < count; v++)
{
val = CFArrayGetValueAtIndex(Arr, v);
// Assuming string here..
const char *printed = CFStringGetCStringPtr (val, // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
// yeah, possible buffer overflow here on malicious profile :-)
strcat (entVal, printed);
if (v < count -1) strcat(entVal,",");
}
return (entVal);
}
// Utility function
void
printDictKey (const void* key, const void* value, void* context) {
// You could also use CFShow() here, with the dup2() trick
// to get CFShow to print to stdout..
int indent = (int) context;
const char *entName = CFStringGetCStringPtr (key , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
const char *entValue = "?";
// Why AAPL doesn't have constants for the typeID (so you could switch) still
// eludes me.
if (CFGetTypeID(value) == CFStringGetTypeID())
{
entValue = CFStringGetCStringPtr (value , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
}
else if (CFGetTypeID(value) == CFBooleanGetTypeID())
{
entValue = CFBooleanGetValue(value) ? "true" : "false";
}
else if (CFGetTypeID(value) == CFArrayGetTypeID())
{
entValue = dumpCFArray(value);
}
printf("\t%s: %s\n", entName,entValue);
}
void
dumpProfile(MISProfileRef Prof)
{
CFStringRef cfsProfName= MISProvisioningProfileGetName(Prof);
const char *profName = CFStringGetCStringPtr (cfsProfName , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
CFStringRef cfsUuid = MISProvisioningProfileGetUUID(Prof);
const char *uuid = CFStringGetCStringPtr (cfsUuid , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
CFDictionaryRef dictEnts = MISProvisioningProfileGetEntitlements(Prof);
CFStringRef cfsTeamID = MISProvisioningProfileGetTeamIdentifier(Prof);
const char *teamID = CFStringGetCStringPtr (cfsTeamID , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
CFArrayRef arrayDevs = MISProvisioningProfileGetProvisionedDevices(Prof);
int profVersion = MISProvisioningProfileGetVersion(Prof);
int mutable = MISProfileIsMutable(Prof);
// TeamName is apparently not accessible from MISProvisioningProfile* APIs,
// so use MISProfileGetValue(...) instead. For a list of values, q.v. MOX*I
// Vol. III, Chapter 6 (AMFI), Table 6-5.
CFStringRef cfsTeamName = MISProfileGetValue(Prof, CFSTR("TeamName"));
const char *teamName = CFStringGetCStringPtr (cfsTeamName , // CFStringRef theString,
kCFStringEncodingUTF8); // CFStringEncoding encoding );
int provisionsAll = MISProvisioningProfileProvisionsAllDevices(Prof);
int localProvision = MISProvisioningProfileIsForLocalProvisioning(Prof);
int appleInternal = MISProvisioningProfileIsAppleInternalProfile(Prof);
// There is also a MISProvisioningProfileGrantsEntitlement,
// which takes the profile and an entitlement (CFStringRef), and tells
// you if it's in the entitlement array.
// Dump out
printf("Profile Name: %s\n", profName);
printf("Version: %d\n", profVersion);
printf("AppleInternal: %s\n", (appleInternal ? "yes" : "no")); // I wish, but no :-(
printf("Local Provisioning: %s\n", (localProvision ? "yes" : "no"));
printf("Mutable: %s\n", (mutable ? "yes" : "no"));
printf("UUID: %s\n", uuid);
printf("Team ID: %s\n", teamID);
printf("Team Name: %s\n", teamName);
printf("Entitlements:\n");
CFDictionaryApplyFunction(dictEnts, printDictKey, NULL);
if (provisionsAll) { printf("Devices: all (Enterprise)\n"); }
else { printf("Device list:\n");
printf("\t%s\n",dumpCFArray( arrayDevs));
}
}; // dumpProf
int
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr,"%s list - to list all installed profiles\n", getprogname());
fprintf(stderr,"%s errors - to list all error codes\n", getprogname());
fprintf(stderr,"%s validate _path_ - Validate a file signature (\"what would AMFI do?\")\n", getprogname());
exit(1);
}
if (strcmp(argv[1], "validate") == 0)
{
if (argc != 3) {
fprintf(stderr, "Work with me here.. I need a path to validate!\n");
exit(1);}
// MVS is stupid and returns e8008001 ("An unknown error has occurred")
// when passed a null file name. Spare it the shame and check existence first.
struct stat stbuf;
if (stat (argv[2], &stbuf)) {
fprintf(stderr,"File %s not found! Why are you wasting my time, little man?!\n", argv[2]);
exit(5);
}
void * copiedInfo = NULL;
CFMutableDictionaryRef optionsDict =
CFDictionaryCreateMutable(kCFAllocatorDefault, // CFAllocatorRef allocator,
0, // CFIndex capacity
&kCFTypeDictionaryKeyCallBacks, // const CFDictionaryKeyCallBacks *keyCallBacks,
&kCFTypeDictionaryValueCallBacks); // const CFDictionaryValueCallBacks *valueCallBacks );
// Now, here's what AMFI really would do:
#ifdef IOS_9
// In 9.x:
CFDictionarySetValue(optionsDict,kMISValidationOptionRespectUppTrustAndAuthorization, kCFBooleanTrue);
#endif
// CFDictionarySetValue(optionsDict,kMISValidationOptionValidateSignatureOnly,kCFBooleanTrue);
// CFDictionarySetValue(optionsDict,kMISValidationOptionExpectedCDHash,CFData of CDhash here..);
// CFDictionarySetValue(optionsDict,kMISValidationOptionUniversalFileOffset, CFNumber...);
// Me, I just try the ad-hoc validation, or defaults, which validates App store too.
CFDictionarySetValue(optionsDict,kMISValidationOptionAllowAdHocSigning, kCFBooleanTrue);
// $%#$%$#%# CFStrings
CFStringRef FileName =
CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, // CFAllocatorRef alloc,
argv[2], // const char *cStr,
kCFStringEncodingUTF8, // CFStringEncoding encoding,
kCFAllocatorDefault); // CFAllocatorRef contentsDeallocator );
// Using MISValidateSignatureAndCopyInfo because on JB devices MISValidateSignature
// is re-exported to return 0 in any case...
// change this to MVS if you want to check if your code signature bypass works:
int rc = MISValidateSignatureAndCopyInfo(FileName, optionsDict,NULL);
if (rc)
{
fprintf(stderr,"Error %d (0x%x) - ",rc, rc);
CFShow(MISCopyErrorStringForErrorCode(rc));
}
else {printf("Valid!\n"); }
return (rc);
}
if (strcmp(argv[1], "list") == 0)
{
block_handler_t miscbb =
^(MISProfileRef prof) {
// Stupid Blocks.
dumpProfile (prof);
};
int rc = MISEnumerateInstalledProvisioningProfiles (0, miscbb);
if (rc !=0) { fprintf(stderr,"Err.. Something's not right?\n");
// CFShow prints to stderr anyway, so why not?
CFShow(MISCopyErrorStringForErrorCode(rc));
}
exit(rc);
} // for list
if (strcmp(argv[1], "errors") == 0)
{
int i =0;
for (i = MIS_ERROR_BASE;
i < MIS_ERROR_BASE + 30; // CopyErrorString handles 29, just to be safe, I +1
i++)
{
fprintf(stderr,"Error 0x%x: ", i);
CFStringRef err =MISCopyErrorStringForErrorCode(i);
if (err) { CFShow(err);}
}
exit(0);
} // for error
fprintf(stderr,"*Sigh*\n");
exit(1);
}
#include <mach/mach.h>
#include <mach/mach_port.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char **argv) {
// Sample for mach messaging:
//
// demonstrating using mach_msg to send and possibly receive a message
//
kern_return_t kr;
mach_port_t portA, portB = MACH_PORT_NULL;
kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&portA);
kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&portB);
printf("Sending message from port A (0x%x) to port B (0x%x)\n", portA, portB);
mach_msg_header_t *msg;
size_t bufSize = 128;
char *buf = alloca(bufSize);
memset (buf, 'J', 128);
#if 0
// Should insert SEND right.. (optional, if we use MIG style "19":
kr = mach_port_insert_right (mach_task_self(), //...
portB, // remote port I will send to
portB,
MACH_MSG_TYPE_MAKE_SEND ); // new right name (on old right)
#endif
printf("KR: %x\n", kr);
///
msg = (mach_msg_header_t *) buf;
/////////////////
msg->msgh_id = 0x24242424;
msg->msgh_remote_port = portB;
msg->msgh_local_port = MACH_PORT_NULL;
msg->msgh_size = 128;
msg->msgh_voucher_port = MACH_PORT_NULL;
msg->msgh_reserved = 0;
// "19" is COPY_SEND - which is what MIG would do. BUT WE NEED TO ENSURE WE HAVE A SEND!
// Use insert_right, above, OR... easier, instruct kernel to MAKE_SEND on the fly.
msg->msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_MAKE_SEND, 0) ;;
//
mach_msg_return_t mmr = mach_msg( msg, // mach_msg_header_t *msg,
MACH_SEND_MSG, // | MACH_RCV_MSG -- mach_msg_option_t option,
128, // mach_msg_size_t send_size,
0, // mach_msg_size_t rcv_size,
MACH_PORT_NULL, // mach_port_name_t rcv_name,
MACH_MSG_TIMEOUT_NONE, // mach_msg_timeout_t timeout,
MACH_PORT_NULL); // mach_port_name_t notify);
printf("MMR: 0x%x\n", mmr);
if (mmr == MACH_MSG_SUCCESS) { sleep(10);}
}
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/kern_memorystatus.h>
#if 0
// from kern_memorystatus.h
// OS X definition
typedef struct memorystatus_priority_entry {
pid_t pid;
int32_t priority;
uint64_t user_data;
int32_t limit;
uint32_t state;
} memorystatus_priority_entry_t;
#endif
#ifdef IOS6
// iOS 6.x definition
typedef struct memorystatus_priority_entry_ios6 {
pid_t pid;
uint32_t flags;
int32_t hiwat_pages;
int32_t priority;
int32_t reserved;
//int32_t reserved2;
} memorystatus_priority_entry_ios6_t;
#define memorystatus_priority_entry memorystatus_priority_entry_ios6
#endif
#define NUM_ENTRIES 1024
char *state_to_text(int State)
{
// Convert kMemoryStatus constants to a textual representation
static char returned[80];
sprintf (returned, "0x%02x ",State);
if (State & kMemorystatusSuspended) strcat(returned,"Suspended,");
if (State & kMemorystatusFrozen) strcat(returned,"Frozen,");
if (State & kMemorystatusWasThawed) strcat(returned,"WasThawed,");
if (State & kMemorystatusTracked) strcat(returned,"Tracked,");
if (State & kMemorystatusSupportsIdleExit) strcat(returned,"IdleExit,");
if (State & kMemorystatusDirty) strcat(returned,"Dirty,");
if (returned[strlen(returned) -1] == ',')
returned[strlen(returned) -1] = '\0';
return (returned);
}
int main (int argc, char **argv)
{
struct memorystatus_priority_entry memstatus[NUM_ENTRIES];
size_t count = sizeof(struct memorystatus_priority_entry) * NUM_ENTRIES;
// call memorystatus_control
int rc = memorystatus_control (MEMORYSTATUS_CMD_GET_PRIORITY_LIST, // 1 - only supported command on OS X
0, // pid
0, // flags
memstatus, // buffer
count); // buffersize
if (rc < 0) { perror ("memorystatus_control"); exit(rc);}
int entry = 0;
for (; rc > 0; rc -= sizeof(struct memorystatus_priority_entry))
{
#ifdef IOS6
printf ("PID: %5d\tFlags: %x\tHiWat Pages:%2d\tReserved:%d\n",
memstatus[entry].pid,
memstatus[entry].priority,
memstatus[entry].hiwat_pages,
memstatus[entry].reserved);
#else
printf ("PID: %5d\tPriority:%2d\tUser Data: %llx\tLimit:%2d\tState:%s\n",
memstatus[entry].pid,
memstatus[entry].priority,
memstatus[entry].user_data,
memstatus[entry].limit,
state_to_text(memstatus[entry].state));
#endif
entry++;
}
}
#include <dispatch/dispatch.h>
#include <CoreFoundation/CoreFoundation.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/**
* Netbottom: A nettop(1) clone without the curses that are curses.
* ----------
*
* Jonathan Levin, http://NewOSXBook.com/
*
* This is a simple program meant to accompany the bonus networking chapter I'm putting
* into MOXiI 2 Volume I (read http://NewOSXBook.com/ChangeLog.html for the story behind that)
*
* This is essentially doing what my old lsock(j) did, but I no longer maintain lsock because
* A) AAPL keeps breaking the APIs with every single version and B) some people have closed source
* my sample and actually started selling it!
*
* The advantage in this example is that it's using the private NetworkStatistics.framework, so that
* hides all the underlying com.apple.network.statistics #%$@#% that keeps breaking on me.
*
*
* Variables:
*
* JCOLOR = 0 to disable color (which is on by default)
* RESOLV = 1 to add DNS resolving (may be slower)
*
* This compiles on both MacOS and iOS cleanly:
* To compile for MacOS:
* gcc netbottom.c -o /tmp/netbottom.x86 -framework CoreFoundation -framework NetworkStatistics -framework CoreFoundation -F /System/Library/PrivateFrameworks
*
*
* To compile on *OS you'll first need to create the "tbd" file for NetworkStatistics, since it's a private framework and there
* are no TBDs for it. You'll need to first use jtool's --tbd option (in Alpha 4 and later) on the dyld_shared_cache_arm64, like so:
*
*
* mkdir -p /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/PrivateFrameworks/NetworkStatistics.framework/
*
* jtool2 --tbd /Volumes/PeaceB16B92.N102OS/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64:NetworkStatistics \
* > /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/PrivateFrameworks/NetworkStatistics.framework/NetworkStatistics.tbd
*
* and then,
*
* gcc-arm64 netbottom.c -o /tmp/netbottom.x86 -framework CoreFoundation -framework NetworkStatistics -framework CoreFoundation -F /System/Library/PrivateFrameworks
*
* On iOS you'll also need an entitlement
<pre>
#if 0
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.private.network.statistics</key>
<true/>
<key>platform-application</key>
<true/>
</dict>
</plist>
#endif
</pre>
* and/or sysctl net.statistics_privcheck=0
*
*
*
* PoC ONLY. There is a LOT that can be fixed here - for one, DNS lookups in main queue may block for a long while!
* also because I don't listen on events I don't get all the TCP state changes.
*
* License: FREE, ABSE, But again - if you plan on close sourcing this, it would be nice if you A) let me know and B) give credit
* where due (That means AAPL, for awesome (though Block-infested) APIs, and this sample.
*
*/
// The missing NetworkStatistics.h...
typedef void *NStatManagerRef;
typedef void *NStatSourceRef;
NStatManagerRef NStatManagerCreate (const struct __CFAllocator *,
dispatch_queue_t,
void (^)(void *, void *));
int NStatManagerSetInterfaceTraceFD(NStatManagerRef, int fd);
int NStatManagerSetFlags(NStatManagerRef, int Flags);
int NStatManagerAddAllTCPWithFilter(NStatManagerRef, int something, int somethingElse);
int NStatManagerAddAllUDPWithFilter(NStatManagerRef, int something, int somethingElse);
void *NStatSourceQueryDescription(NStatSourceRef);
extern CFStringRef kNStatProviderInterface;
extern CFStringRef kNStatProviderRoute;
extern CFStringRef kNStatProviderSysinfo;
extern CFStringRef kNStatProviderTCP;
extern CFStringRef kNStatProviderUDP;
extern CFStringRef kNStatSrcKeyAvgRTT;
extern CFStringRef kNStatSrcKeyChannelArchitecture;
extern CFStringRef kNStatSrcKeyConnProbeFailed;
extern CFStringRef kNStatSrcKeyConnectAttempt;
extern CFStringRef kNStatSrcKeyConnectSuccess;
extern CFStringRef kNStatSrcKeyDurationAbsoluteTime;
extern CFStringRef kNStatSrcKeyEPID;
extern CFStringRef kNStatSrcKeyEUPID;
extern CFStringRef kNStatSrcKeyEUUID;
extern CFStringRef kNStatSrcKeyInterface;
extern CFStringRef kNStatSrcKeyInterfaceCellConfigBackoffTime;
extern CFStringRef kNStatSrcKeyInterfaceCellConfigInactivityTime;
extern CFStringRef kNStatSrcKeyInterfaceCellUlAvgQueueSize;
extern CFStringRef kNStatSrcKeyInterfaceCellUlMaxQueueSize;
extern CFStringRef kNStatSrcKeyInterfaceCellUlMinQueueSize;
extern CFStringRef kNStatSrcKeyInterfaceDescription;
extern CFStringRef kNStatSrcKeyInterfaceDlCurrentBandwidth;
extern CFStringRef kNStatSrcKeyInterfaceDlMaxBandwidth;
extern CFStringRef kNStatSrcKeyInterfaceIsAWD;
extern CFStringRef kNStatSrcKeyInterfaceIsAWDL;
extern CFStringRef kNStatSrcKeyInterfaceIsCellFallback;
extern CFStringRef kNStatSrcKeyInterfaceIsExpensive;
extern CFStringRef kNStatSrcKeyInterfaceLinkQualityMetric;
extern CFStringRef kNStatSrcKeyInterfaceName;
extern CFStringRef kNStatSrcKeyInterfaceThreshold;
extern CFStringRef kNStatSrcKeyInterfaceType;
extern CFStringRef kNStatSrcKeyInterfaceTypeCellular;
extern CFStringRef kNStatSrcKeyInterfaceTypeLoopback;
extern CFStringRef kNStatSrcKeyInterfaceTypeUnknown;
extern CFStringRef kNStatSrcKeyInterfaceTypeWiFi;
extern CFStringRef kNStatSrcKeyInterfaceTypeWired;
extern CFStringRef kNStatSrcKeyInterfaceUlBytesLost;
extern CFStringRef kNStatSrcKeyInterfaceUlCurrentBandwidth;
extern CFStringRef kNStatSrcKeyInterfaceUlEffectiveLatency;
extern CFStringRef kNStatSrcKeyInterfaceUlMaxBandwidth;
extern CFStringRef kNStatSrcKeyInterfaceUlMaxLatency;
extern CFStringRef kNStatSrcKeyInterfaceUlMinLatency;
extern CFStringRef kNStatSrcKeyInterfaceUlReTxtLevel;
extern CFStringRef kNStatSrcKeyInterfaceWifiConfigFrequency;
extern CFStringRef kNStatSrcKeyInterfaceWifiConfigMulticastRate;
extern CFStringRef kNStatSrcKeyInterfaceWifiDlEffectiveLatency;
extern CFStringRef kNStatSrcKeyInterfaceWifiDlErrorRate;
extern CFStringRef kNStatSrcKeyInterfaceWifiDlMaxLatency;
extern CFStringRef kNStatSrcKeyInterfaceWifiDlMinLatency;
extern CFStringRef kNStatSrcKeyInterfaceWifiScanCount;
extern CFStringRef kNStatSrcKeyInterfaceWifiScanDuration;
extern CFStringRef kNStatSrcKeyInterfaceWifiUlErrorRate;
extern CFStringRef kNStatSrcKeyLocal;
extern CFStringRef kNStatSrcKeyMinRTT;
extern CFStringRef kNStatSrcKeyPID;
extern CFStringRef kNStatSrcKeyProbeActivated;
extern CFStringRef kNStatSrcKeyProcessName;
extern CFStringRef kNStatSrcKeyProvider;
extern CFStringRef kNStatSrcKeyRcvBufSize;
extern CFStringRef kNStatSrcKeyRcvBufUsed;
extern CFStringRef kNStatSrcKeyReadProbeFailed;
extern CFStringRef kNStatSrcKeyRemote;
extern CFStringRef kNStatSrcKeyRouteDestination;
extern CFStringRef kNStatSrcKeyRouteFlags;
extern CFStringRef kNStatSrcKeyRouteGateway;
extern CFStringRef kNStatSrcKeyRouteGatewayID;
extern CFStringRef kNStatSrcKeyRouteID;
extern CFStringRef kNStatSrcKeyRouteMask;
extern CFStringRef kNStatSrcKeyRouteParentID;
extern CFStringRef kNStatSrcKeyRxBytes;
extern CFStringRef kNStatSrcKeyRxCellularBytes;
extern CFStringRef kNStatSrcKeyRxDupeBytes;
extern CFStringRef kNStatSrcKeyRxOOOBytes;
extern CFStringRef kNStatSrcKeyRxPackets;
extern CFStringRef kNStatSrcKeyRxWiFiBytes;
extern CFStringRef kNStatSrcKeyRxWiredBytes;
extern CFStringRef kNStatSrcKeySndBufSize;
extern CFStringRef kNStatSrcKeySndBufUsed;
extern CFStringRef kNStatSrcKeyStartAbsoluteTime;
extern CFStringRef kNStatSrcKeyTCPCCAlgorithm;
extern CFStringRef kNStatSrcKeyTCPState;
extern CFStringRef kNStatSrcKeyTCPTxCongWindow;
extern CFStringRef kNStatSrcKeyTCPTxUnacked;
extern CFStringRef kNStatSrcKeyTCPTxWindow;
extern CFStringRef kNStatSrcKeyTrafficClass;
extern CFStringRef kNStatSrcKeyTrafficMgtFlags;
extern CFStringRef kNStatSrcKeyTxBytes;
extern CFStringRef kNStatSrcKeyTxCellularBytes;
extern CFStringRef kNStatSrcKeyTxPackets;
extern CFStringRef kNStatSrcKeyTxReTx;
extern CFStringRef kNStatSrcKeyTxWiFiBytes;
extern CFStringRef kNStatSrcKeyTxWiredBytes;
extern CFStringRef kNStatSrcKeyUPID;
extern CFStringRef kNStatSrcKeyUUID;
extern CFStringRef kNStatSrcKeyVUUID;
extern CFStringRef kNStatSrcKeyVarRTT;
extern CFStringRef kNStatSrcKeyWriteProbeFailed;
extern CFStringRef kNStatSrcTCPStateCloseWait;
extern CFStringRef kNStatSrcTCPStateClosed;
extern CFStringRef kNStatSrcTCPStateClosing;
extern CFStringRef kNStatSrcTCPStateEstablished;
extern CFStringRef kNStatSrcTCPStateFinWait1;
extern CFStringRef kNStatSrcTCPStateFinWait2;
extern CFStringRef kNStatSrcTCPStateLastAck;
extern CFStringRef kNStatSrcTCPStateListen;
extern CFStringRef kNStatSrcTCPStateSynReceived;
extern CFStringRef kNStatSrcTCPStateSynSent;
extern CFStringRef kNStatSrcTCPStateTimeWait;
CFStringRef NStatSourceCopyProperty (NStatSourceRef , CFStringRef);
void NStatSourceSetDescriptionBlock (NStatSourceRef arg, void (^)(CFDictionaryRef));
void NStatSourceSetRemovedBlock (NStatSourceRef arg, void (^)(void));
/// End NetworkStatistics.h
// Text Color support
#define BOLD "\033[1;1m"
#define RED "\033[0;31m"
#define M0 "\e[0;30m"
#define CYAN "\e[0;36m"
#define M1 "\e[0;31m"
#define GREY "\e[0;37m"
#define M8 "\e[0;38m"
#define M9 "\e[0;39m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[0;33m"
#define BLUE "\e[0;34m"
#define PINK "\e[0;35m"
#define NORMAL "\e[0;0m"
#define DELETED "\e[2;31m"
int g_Color = 1;
struct tcpStateColors {
char state[16];
char color[16];
} tcpStateColors[] = {
{ "Listen", BOLD },
{ "Established", CYAN },
{ "CloseWait", GREY },
{ "Closed", DELETED},
{ "", ""},
};
const char *getColor(char *State) {
// Fine to return a color ref here since we are only
// called from a single thread
int i = 0;
for (i = 0 ; tcpStateColors[i].state[0]; i++) {
if (strcmp(tcpStateColors[i].state, State) == 0) return tcpStateColors[i].color;
}
return NORMAL;
}
int g_resolveNames = 0;
//
// Utility
///
struct pidNetworkInfo {
int pid;
int af;
char procName[1024];
char provider[128];
char tcpState[16] ; //tcp only
int printed;
} *pidNetworkInfos;
int maxProcNum = 0;
int findSlotForPid(pid_t Pid){
int p = 0;
for ( p = 0; p < maxProcNum; p++) {
if ( pidNetworkInfos[p].pid == Pid) return p;
}
// If still here, assign
pidNetworkInfos[maxProcNum].pid = Pid;
maxProcNum++;
return (maxProcNum - 1);
}
//
// Blocks...
//
void (^removal_callback_block) (void) = ^(void) {
// I'm not using this, but you can implement a callback here on source removal
};
void (^description_callback_block) (CFDictionaryRef) = ^(CFDictionaryRef Desc) {
// Called when another API asks for a source description, which this
// sample does on source addition..
CFStringRef pName = CFDictionaryGetValue(Desc, kNStatSrcKeyProcessName);
CFNumberRef pIdentifier = CFDictionaryGetValue(Desc, kNStatSrcKeyPID);
int pid;
CFNumberGetValue (pIdentifier, kCFNumberSInt32Type, &pid);
// Got PID, put in slot
int pslot = findSlotForPid(pid);
// You can query any of the multiple kNStatSrcKeys above, I demonstrate the following
// UDP or TCP?
CFStringRef pProvider = CFDictionaryGetValue(Desc, kNStatSrcKeyProvider);
CFStringGetCString(pProvider, // CFStringRef theString,
pidNetworkInfos[pslot].provider, // char *buffer,
128, //CFIndex bufferSize,
kCFStringEncodingUTF8) ; // CFStringEncoding encoding)
if (pidNetworkInfos[pslot].provider[0] == 'T') {
CFStringRef tcpState = CFDictionaryGetValue(Desc, kNStatSrcKeyTCPState);
CFStringGetCString(tcpState, // CFStringRef theString,
pidNetworkInfos[pslot].tcpState, // char *buffer,
16, //CFIndex bufferSize,
kCFStringEncodingUTF8) ; // CFStringEncoding encoding)
}
CFStringGetCString(pName, // CFStringRef theString,
pidNetworkInfos[pslot].procName, // char *buffer,
1024, //CFIndex bufferSize,
kCFStringEncodingUTF8) ; // CFStringEncoding encoding)
pidNetworkInfos[pslot].printed = 0;
//void CFDataGetBytes(CFDataRef theData, CFRange range, UInt8 *buffer);
CFDataRef addr = (CFDictionaryGetValue(Desc, kNStatSrcKeyRemote));
CFIndex len = CFDataGetLength(addr);
struct sockaddr *remoteSA = alloca (len); // enough
CFDataGetBytes(addr, // CFDataRef theData,
CFRangeMake(0,len), // CFRange range,
(UInt8 *)remoteSA); //UInt8 *buffer);
pidNetworkInfos[pslot].af = remoteSA->sa_family;
addr = (CFDictionaryGetValue(Desc, kNStatSrcKeyLocal));
len = CFDataGetLength(addr);
struct sockaddr *localSA = alloca (len); // enough
CFDataGetBytes(addr, // CFDataRef theData,
CFRangeMake(0,len), // CFRange range,
(UInt8*)localSA); //UInt8 *buffer);
int i = 0;
int ll = strlen( pidNetworkInfos[pslot].provider);
for (i = 0 ; i < ll; i ++)
{
pidNetworkInfos[pslot].provider[i] = tolower( pidNetworkInfos[pslot].provider[i]);
}
pidNetworkInfos[pslot].provider[ll] = (remoteSA->sa_family == AF_INET6? '6': '4');
char hostName[256];
hostName[0] = '\0';
int rc = 0;
int flags = 0;
if (!g_resolveNames) { flags = NI_NUMERICHOST;}
rc = getnameinfo(remoteSA, // const struct sockaddr *sa,
remoteSA->sa_len, // socklen_t salen,
hostName, // char *host,
256, // socklen_t hostlen,
NULL, // char *serv,
0, //socklen_t servlen,
flags); // flags
time_t ltime;
time( &ltime );
struct tm *ttt = localtime (&ltime);
char timestamp[32];
strftime(timestamp, // char *restrict s,
32, // size_t maxsize,
"%H:%M:%S", //const char *restrict format,
ttt); //const struct tm *restrict timeptr);
char *l = strdup(inet_ntoa(((struct sockaddr_in *) localSA)->sin_addr));
if (g_Color)
{
}
const char *color = "";
char *uncolor = "";
if (g_Color )
{
uncolor = NORMAL;
color = getColor(pidNetworkInfos[pslot].tcpState);
}
if (hostName[0]) {
// Always true because of NI_NUMERICHOST up there
printf ("%s%s %s\t%20s:%hu\t%30s:%-5hu %-11s\t%d/%s%s\n",
color,
timestamp,
pidNetworkInfos[pslot].provider,
l,
ntohs(((struct sockaddr_in *) localSA)->sin_port),
hostName,
ntohs(((struct sockaddr_in *) remoteSA)->sin_port),
(pidNetworkInfos[pslot].provider[0] == 't' ? pidNetworkInfos[pslot].tcpState : ""),
pidNetworkInfos[pslot].pid,
pidNetworkInfos[pslot].procName,
uncolor
);
}
else {
// The other way:
if (remoteSA->sa_family == AF_INET) {
printf ("%s %s\t%20s:%hu\t%20s:%-5hu %-11s\t%d/%s\n",
timestamp,
pidNetworkInfos[pslot].provider,
l,
ntohs(((struct sockaddr_in *) localSA)->sin_port),
inet_ntoa (((struct sockaddr_in *) remoteSA)->sin_addr),
ntohs(((struct sockaddr_in *) remoteSA)->sin_port),
(pidNetworkInfos[pslot].provider[0] == 't' ? pidNetworkInfos[pslot].tcpState : ""),
pidNetworkInfos[pslot].pid,
pidNetworkInfos[pslot].procName );
}
if (remoteSA->sa_family == AF_INET6) {
printf ("%s %s\t%20s:%hu\t%30s:%-5hu %-11s\t%d/%s\n",
timestamp,
pidNetworkInfos[pslot].provider,
l,
ntohs(((struct sockaddr_in *) localSA)->sin_port),
inet_ntoa (((struct sockaddr_in *) remoteSA)->sin_addr),
ntohs(((struct sockaddr_in *) remoteSA)->sin_port),
(pidNetworkInfos[pslot].provider[0] == 't' ? pidNetworkInfos[pslot].tcpState : ""),
pidNetworkInfos[pslot].pid,
pidNetworkInfos[pslot].procName );
}
} // Not resolve
free (l);
fflush(NULL);
};
void (^events_callback_block) (NStatSourceRef) = ^(NStatSourceRef Src) {
// Not using this either - but you can if you want. Left as an exercise
};
void (^callback_block) (void *, void *) = ^(NStatSourceRef Src, void *arg2){
// Arg is NWS[TCP/UDP]Source
//NStatSourceSetRemovedBlock (Src, removal_callback_block);
//NStatSourceSetEventsBlock (Src, events_callback_block);
NStatSourceSetDescriptionBlock (Src, description_callback_block);
(void) NStatSourceQueryDescription(Src);
};
int sorted = 0;
// Not really sorting..
int
pidComp (struct pidNetworkInfo *a, struct pidNetworkInfo *b) {
if (!a || !b) return 0;
if (a->pid > b->pid ) return 1;
if (a->pid < b->pid) return -1;
return 0;
}
#ifdef WANT_TIMER
void
refreshList (void *ignored) {
// Display list every second. Not using this, left for the interested reader
// to implement
return;
if (!sorted) {
qsort (pidNetworkInfos, maxProcNum, sizeof(struct pidNetworkInfo), pidComp);
}
int p = 0;
for (p = 0; p < maxProcNum; p++) {
if (!pidNetworkInfos[p].printed) {
if (pidNetworkInfos[p].procName[0]) {
printf("%d:%s\n", pidNetworkInfos[p].pid, pidNetworkInfos[p].procName);
pidNetworkInfos[p].printed++;
}
}
}
}
#endif
// Main
int
main (int argc, char **argv) {
int rc;
char *cc = getenv("JCOLOR");
if (cc && cc[0] == '0') g_Color = 0;
g_resolveNames =( getenv("RESOLVE") != NULL);
if (argc < 2) {
fprintf(stderr,"%s [tcp/udp/all]\n", argv[0]);
exit(1);
}
int wantTCP = 0;
int wantUDP = 0;
if (strcmp (argv[1],"tcp") == 0) { wantTCP++;}
if (strcmp (argv[1],"udp") == 0) { wantUDP++;}
if (strcmp (argv[1],"all") == 0) { wantTCP++; wantUDP++;}
if (!wantTCP && !wantUDP) {
fprintf(stderr,"I'll ignore anything you put on my argument list except 'tcp' and 'udp'\n"); exit(2);
}
// Simple PoC, so not doing any interesting hashes or PID lookup logic
#define PID_MAX 1000000
pidNetworkInfos = (struct pidNetworkInfo *) calloc (sizeof (struct pidNetworkInfo) , PID_MAX);
NStatManagerRef nm = NStatManagerCreate (kCFAllocatorDefault,
&_dispatch_main_q, // DISPATCH_TARGET_QUEUE_DEFAULT,
callback_block);
rc = NStatManagerSetFlags(nm, 0);
// This is a really cool undocumented feature of NetworkStatistics.framework
// Which will give you the actual control socket data output.
int fd = open ("/tmp/nettop.trace", O_RDWR| O_CREAT | O_TRUNC);
rc = NStatManagerSetInterfaceTraceFD(nm, fd);
if (wantUDP) { rc = NStatManagerAddAllUDPWithFilter (nm, 0 , 0);}
if (wantTCP) { rc = NStatManagerAddAllTCPWithFilter (nm, 0 , 0); }
#if WANT_TIMER
dispatch_source_t dst = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, // dispatch_source_type_t type,
0 , // uintptr_t handle,
0, // unsigned long mask,
&_dispatch_main_q); // dispatch_queue_t queue);
dispatch_source_set_event_handler_f (dst, refreshList);
dispatch_source_set_timer(dst, // dispatch_source_t source,
dispatch_time (DISPATCH_TIME_NOW,0), // dispatch_time_t start,
1000000000, // uint64_t interval,
0);//uint64_t leeway);
dispatch_resume(dst);
#endif
dispatch_main();
}
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define _GNU_SOURCE 1
#include <string.h>
#include <sys/stat.h> // for mkdir
#include <sys/mman.h> // for mmap
#undef ntohl
#undef ntohs
#define RED "\033[0;31m"
#define M0 "\e[0;30m"
#define CYAN "\e[0;36m"
#define M1 "\e[0;31m"
#define GREY "\e[0;37m"
#define M8 "\e[0;38m"
#define M9 "\e[0;39m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[0;33m"
#define BLUE "\e[0;34m"
#define PINK "\e[0;35m"
#define NORMAL "\e[0;0m"
#ifdef LINUX
typedef unsigned long uint64_t;
typedef unsigned short uint16_t;
extern void *memmem (const void *__haystack, size_t __haystacklen,
const void *__needle, size_t __needlelen);
#endif
/**
* Apple iOS OTA/PBZX expander/unpacker/lister/searcher - by Jonathan Levin,
*
* http://NewOSXBook.com/
*
* Free for anyone (AISE) to use, modify, etc. I won't complain :-), but I'd appreciate a mention
*
* Changelog: 02/08/16 - Replaced alloca with malloc () (full OTAs with too many files would have popped stack..)
*
* 02/17/16 - Increased tolerance for corrupt OTA - can now seek to entry in a file
*
* 08/31/16 - Added search in OTA.
*
* 02/28/18 - It's been a while - and ota now does diff!
* Also tidied up and made neater
*
* 12/03/18 - Added -S to search for string null terminated
*
* The last OTA: (seriously, I'm done :-)
*
* 08/06/19 - Integrated @S1guza's symlink fix (Thanks, man!)
* Added pbzx built-in so you don't have to use pbzx first
* Added multiple file processing, compatible with shell expansion
* Can now ota ...whatever... payload.0?? to iterate over all files!
* Added -H to generate SHA-1 hashes for all ('*') or specific files in OTA
* (SHA-1 code taken from public domain, as was lzma)
*
* Really, the last OTA (because this %$%#$ brought me out of retirement!)
*
* 10/19/21 - YAA support (for iOS 15 and maybe earlier OTAs?)
*
* To compile: now use attached makefile, since there are lzma dependencies
* Remember to add '-DLINUX' if on Linux
*
*
*/
typedef unsigned int uint32_t;
uint64_t pos = 0;
void processFileInner( char *mmapped, uint32_t pos, char *name, uint32_t fileSize, uint32_t perms);
#ifndef NOSHA
#include "sha1.c"
#endif // NOSHA
#pragma pack(1)
struct entry
{
unsigned int usually_0x210_or_0x110;
unsigned short usually_0x00_00; //_00_00;
unsigned int fileSize;
unsigned short whatever;
unsigned long long timestamp_likely;
unsigned short _usually_0x20;
unsigned short nameLen;
unsigned short uid;
unsigned short gid;
unsigned short perms;
char name[0];
// Followed by file contents
};
#pragma pack()
extern int ntohl(int);
extern short ntohs(short);
uint32_t
swap32(uint32_t arg)
{
return (ntohl(arg));
}
int g_list = 0;
int g_verbose = 0;
char *g_extract = NULL;
char *g_search = NULL;
char *g_hash = NULL;
int g_nullTerm = 0;
// Since I now diff and use open->mmap(2) on several occasions, refactored
// into its own function
//
void *mmapFile(char *FileName, uint64_t *FileSize)
{
int fd = open (FileName, O_RDONLY);
if (fd < 0) { perror (FileName); exit(1);}
// 02/17/2016 - mmap
struct stat stbuf;
int rc = fstat(fd, &stbuf);
char *mmapped = mmap(NULL, // void *addr,
stbuf.st_size , // size_t len,
PROT_READ, // int prot,
MAP_PRIVATE, // int flags,
fd, // int fd,
0); // off_t offset);
if (mmapped == MAP_FAILED) { perror (FileName); exit(1);}
if (FileSize) *FileSize = stbuf.st_size;
close (fd);
return (mmapped);
}
void hashFile (char *File, char *Name, uint32_t Size, short Perms, char *HashCriteria)
{
if (!HashCriteria) return;
if ((HashCriteria[0] != '*') && ! strstr(Name, HashCriteria)) return ;
#define HASH_SIZE 20
uint8_t Message_Digest[SHA1HashSize];
doSHA1((void*)File, Size, Message_Digest);
int i = 0;
printf("%s (%d bytes): ", Name, Size);
for (i = 0; i < HASH_SIZE; i++)
{
printf("%02X", Message_Digest[i]);
}
printf("\n");
}
void
extractFile (char *File, char *Name, uint32_t Size, short Perms, char *ExtractCriteria)
{
// MAYBE extract file (depending if matches Criteria, or "*").
// You can modify this to include regexps, case sensitivity, what not.
// presently, it's just strstr()
if (!ExtractCriteria) return;
if ((ExtractCriteria[0] != '*') && ! strstr(Name, ExtractCriteria)) return;
uint16_t type = Perms & S_IFMT;
Perms &= ~S_IFMT;
if (type == S_IFDIR) {
mkdir (Name, 0755);
return;
}
if(type != S_IFREG && type != S_IFLNK)
{
fprintf(stderr, "Unknown file type: %o\n", type);
// return;
}
// Ok. Extract . This is simple - just dump the file contents to its directory.
// What we need to do here is parse the '/' and mkdir(2), etc.
char *dirSep = strchr (Name, '/');
while (dirSep)
{
*dirSep = '\0';
mkdir(Name,0755);
*dirSep = '/';
dirSep+=1;
dirSep = strchr (dirSep, '/');
}
if(type == S_IFLNK)
{
/* @s1guza's support for symlinks! */
/* http://newosxbook.com/forum/viewtopic.php?f=3&t=19513 */
char *target = strndup(File, Size);
if(g_verbose)
{
fprintf(stderr, "Symlinking %s to %s\n", Name, target);
}
symlink(target, Name);
fchmodat(AT_FDCWD, Name, Perms, AT_SYMLINK_NOFOLLOW);
free(target);
}
else {
// at this point we're out of '/'s
// go back to the last /, if any
if (g_verbose)
{
fprintf(stderr, "Dumping %d bytes to %s\n", Size, Name);
}
int fd = open (Name, O_WRONLY| O_CREAT);
fchmod (fd, Perms);
write (fd, File, Size);
close (fd);
}
} // end extractFile
void showPos()
{
fprintf(stderr, "POS is %lld\n", pos);
}
struct entry *getNextEnt (char *Mapping, uint64_t Size, uint64_t *Pos)
{
// Return entry at Mapping[Pos],
// and advance Pos to point to next one
int pos = 0;
struct entry *ent =(struct entry *) (Mapping + *Pos );
if (*Pos > Size) return (NULL);
*Pos += sizeof(struct entry);
uint32_t entsize = swap32(ent->fileSize);
uint32_t nameLen = ntohs(ent->nameLen);
// Get Name (immediately after the entry)
//char *name = malloc (nameLen+1);
// strncpy(name, Mapping+ *Pos , nameLen);
//name[nameLen] = '\0';
//printf("NAME %p IS %s, Size: %d\n", Mapping, name, entsize);
//free (name);
*Pos += nameLen;
*Pos += entsize;
return (ent);
} // getNextEnt
int doDiff (char *File1, char *File2, int Exists)
{
// There are two ways to do diff:
// look at both files as archives, find diffs, then figure out diff'ing entry,
// or look at file internal entries individually, then compare each of them
// I chose the latter. This also (to some extent) survives file ordering
// Note I'm still mmap(2)ing BOTH files. This contributes to speed, but does
// have the impact of consuming lots o'RAM. That said, this is to be run on a
// Linux/MacOS, and not on an i-Device, so we should be ok.
uint64_t file1Size = 0;
char *file1Mapping = mmapFile(File1, &file1Size);
uint64_t file2Size = 0;
char *file2Mapping = mmapFile(File2, &file2Size);
uint64_t file1pos = 0;
uint64_t file2pos = 0;
struct entry *file1ent = getNextEnt (file1Mapping, file1Size, &file1pos);
struct entry *file2ent = getNextEnt (file2Mapping,file2Size, &file2pos);
uint64_t lastFile1pos, lastFile2pos = 0;
while (file1ent && file2ent) {
lastFile1pos = file1pos;
lastFile2pos = file2pos;
file1ent = getNextEnt (file1Mapping, file1Size, &file1pos);
file2ent = getNextEnt (file2Mapping,file2Size, &file2pos);
char *ent1Name = file1ent->name;
char *ent2Name = file2ent->name;
// Because I'm lazy: skip last entry
if (file1pos > file1Size - 1000000) break;
int found = 1;
char *n1 = strndup(file1ent->name, ntohs(file1ent->nameLen));
if (strncmp(ent1Name, ent2Name, ntohs(file1ent->nameLen)))
{
// Stupid names aren't NULL terminated (AAPL don't read my comments,
// apparently), so we have to copy both names in:
// But that's the least of our problems: We don't know if n1 has been removed
// from n2, or n2 is a new addition:
uint64_t seekpos = file2pos;
// seek n1 in file2:
found = 0;
int i = 0;
struct entry *seek2ent;
while (1) {
seek2ent = getNextEnt (file2Mapping,file2Size, &seekpos);
if (!seek2ent) { break; } // {printf("EOF\n");break;}
if (memcmp(seek2ent->name,file1ent->name, ntohs(seek2ent->nameLen)) == 0) {
found++; break;
}
else {
/*
i++;
if (i < 200) {
char *n2 = strndup(seek2ent->name, ntohs(seek2ent->nameLen));
printf("check: %s(%d) != %s(%d) -- %d\n",n2, ntohs(seek2ent->nameLen),n1, strlen(n1),
memcmp(seek2ent->name,file1ent->name, ntohs(seek2ent->nameLen) ));
free(n2);
}
*/
}
} // end while
if (!found) {
printf("%s: In file1 but not file2\n", n1);
// rewind file2pos so we hit the entry again..
file2pos = lastFile2pos;
}
else {
// Found it - align (all the rest to this point were not in file1)
file2pos = seekpos;
}
} // name mismatch
if (found) {
// Identical entries - check for diffs unless we're only doing existence checks
// if the sizes diff, obviously:
if (!Exists) {
if (file1pos - lastFile1pos != file2pos - lastFile2pos)
{ fprintf(stdout,"%s (different sizes)\n", n1); }
else
// if sizes are identical, maybe - but ignore timestamp!
if (memcmp (((unsigned char *)file1ent) + sizeof(struct entry),
((unsigned char *)file2ent) + sizeof(struct entry), file1pos - lastFile1pos - sizeof(struct entry)))
{ fprintf(stdout,"%s\n", n1); }
}
free (n1);
}
} // end file1pos
return 0;
}
void processFile(char *fileName);
int
main(int argc ,char **argv)
{
char *filename ="p";
int i = 0;
if (argc < 2) {
fprintf (stderr,"Usage: %s [-v] [-l] [...] _filename[s]_ \nWhere: -l: list files in update payload\n"
"Where: [...] is one of:\n"
" -e _file: extract file from update payload (use \"*\" for all files)\n"
" -s _string _file: Look for occurences of _string_ in file\n"
" -S _string _file: Look for occurences of _string_, NULL terminated in file\n"
" -H [_file]: get hash digest of specific file (use \"*\" for all files)\n"
" [-n] -d _file1 _file2: Point out differences between OTA _file1 and _file2\n"
" -n to only diff names\n", argv[0]);
exit(10);
}
int exists = 0;
for (i = 1;
(i < argc -1) && (argv[i][0] == '-');
i++)
{
// This is super quick/dirty. You might want to rewrite with getopt, etc..
if (strcmp(argv[i], "-n") == 0) {
exists++;
}
else
if (strcmp (argv[i] , "-d") == 0) {
// make sure we have argv[i+1] and argv[i+2]...
if (i != argc - 3)
{
fprintf(stderr,"-d needs exactly two arguments - two OTA files to compare\n");
exit(6);
}
// that the files exist...
if (access (argv[i+1], F_OK)) { fprintf(stderr,"%s: not a file\n", argv[i+1]); exit(11); }
if (access (argv[i+2], F_OK)) { fprintf(stderr,"%s: not a file\n", argv[i+2]); exit(12); }
// then do diff
return ( doDiff (argv[i+1],argv[i+2], exists));
}
else
if (strcmp (argv[i], "-l") == 0) { g_list++;}
else
if (strcmp (argv[i] , "-v") == 0) { g_verbose++;}
#ifndef NOSHA
else
if (strcmp(argv[i], "-H") == 0) {
if (i == argc -1) { fprintf(stderr, "-H: Option requires an argument (what to extract)\n");
exit(5); }
g_hash = argv[i+1]; i++;
}
#endif
else
if (strcmp (argv[i], "-e") == 0) {
if (i == argc -1) { fprintf(stderr, "-e: Option requires an argument (what to extract)\n");
exit(5); }
g_extract = argv[i+1]; i++;
}
// Added 08/31/16:
// and modified 12/01/2018
else
if ((strcmp (argv[i], "-s") == 0) || (strcmp (argv[i], "-S") == 0)) {
if (i == argc - 2) { fprintf(stderr, "%s: Option requires an argument (search string)\n", argv[i]);
exit(5); }
g_search = argv[i+1];
if (argv[i][1] == 'S') g_nullTerm++;
i++;
}
else {
fprintf(stderr,"Unknown option: %s\n", argv[i]);
return 1;
}
}
// Another little fix if user forgot filename, rather than try to open
if (argv[argc-1][0] == '-') {
fprintf(stderr,"Must supply filename\n"); exit(5);
}
// Loop over filenames:
for (; i < argc; i++)
{
if (strstr(argv[i],".ecc")) continue;
processFile(argv[i]);
}
}
#define PBZX_MAGIC "pbzx"
char *doPBZX (char *pbzxData, int Size, int *ExtractedSize) {
#ifndef NO_PBZX
#define OUT_BUFSIZE 16*1024*1024 // Largest chunk I've seen is 8MB. This is double that.
char * decompressXZChunk(char *buf, int size, char *Into, int *IntoSize);
uint64_t length = 0, flags = 0;
char *returned = malloc(OUT_BUFSIZE);
int returnedSize = OUT_BUFSIZE;
int available = returnedSize;
int pos = strlen(PBZX_MAGIC);
flags = *((uint64_t *) pbzxData + pos);
// read (fd, &flags, sizeof (uint64_t));
pos += sizeof(uint64_t);
flags = __builtin_bswap64(flags);
// fprintf(stderr,"Flags: 0x%llx\n", flags);
int i = 0;
int off = 0;
int warn = 0 ;
int skipChunk = 0;
int rc = 0;
// 03/09/2016 - Fixed for single chunks (payload.0##) files, e.g. WatchOS
// and for multiple chunks. AAPL changed flags on me..
//
// New OTAs use 0x800000 for more chunks, not 0x01000000.
// 08/06/2019 - dang it. it's not flags - it's uncomp chunk size.
uint64_t totalSize = 0;
uint64_t uncompLen = flags;
while (pos < Size){
i++;
//printf("FLAGS: %llx\n", flags);
// rc= read (fd, &flags, sizeof (uint64_t)); // check retval..
flags = *((uint64_t *) (pbzxData +pos));
pos+= sizeof(uint64_t);
flags = __builtin_bswap64(flags);
//printf("FLAGS: %llx\n", flags);
length = *((uint64_t *) (pbzxData +pos));
//rc = read (fd, &length, sizeof (uint64_t));
pos+= sizeof(uint64_t);
length = __builtin_bswap64(length);
skipChunk = 0; // (i < minChunk);
if (getenv("JDEBUG") != NULL) fprintf(stderr,"Chunk #%d (uncomp: %lld, comp length: %lld bytes) %s\n",i, flags,length, skipChunk? "(skipped)":"");
// Let's ignore the fact I'm allocating based on user input, etc..
//char *buf = malloc (length);
//int bytes = read (fd, buf, length);
char *buf = pbzxData + pos;
pos += length;
// flags = *((uint64_t *) (pbzxData +pos));
#if 0
// 6/18/2017 - Fix for WatchOS 4.x OTA wherein the chunks are bigger than what can be read in one operation
int bytes = length;
int totalBytes = bytes;
while (totalBytes < length) {
// could be partial read
bytes = read (fd, buf +totalBytes, length -totalBytes);
totalBytes +=bytes;
}
#endif
// We want the XZ header/footer if it's the payload, but prepare_payload doesn't have that,
// so just warn.
if (memcmp(buf, "\xfd""7zXZ", 6)) { warn++;
fprintf (stderr, "Warning: Can't find XZ header at offset 0x%x. Instead have 0x%x(?).. This is likely not XZ data.\n",
(buf - pbzxData),
(* (uint32_t *) buf ));
// Treat as uncompressed
// UNCOMMENT THIS to handle uncomp XZ too..
memcpy(returned + (returnedSize - available), buf, length);
totalSize += length;
available -= length;
}
else // if we have the header, we had better have a footer, too
{
if (strncmp(buf + length - 2, "YZ", 2)) { warn++; fprintf (stderr, "Warning: Can't find XZ footer at 0x%llx (instead have %x). This is bad.\n",
(length -2),
*((unsigned short *) (buf + length - 2)));
}
// if (1 && !skipChunk)
{
// Uncompress chunk
int chunkExpandedSize = available;
char *ptrTo = returned + (returnedSize - available);
decompressXZChunk(buf, length, returned + (returnedSize - available),&chunkExpandedSize);
// printf("DECOMPRESSING to %p - %p\n", ptrTo , ptrTo + chunkExpandedSize);
totalSize += chunkExpandedSize;
available -= chunkExpandedSize;
if (available < OUT_BUFSIZE)
{
returnedSize += 10 * OUT_BUFSIZE;
available += 10 * OUT_BUFSIZE;
// Can't use realloc!
char *new = malloc(returnedSize);
if (getenv("JDEBUG") != NULL)printf("REALLOCING from %p to %p ,%x, AVAIL: %x\n", returned, new, returnedSize, available);
if (new) {
memcpy(new, returned, returnedSize - available);
free(returned);
returned = new;
}
else { fprintf(stderr,"ERROR!\n"); exit(1);}
}
}
warn = 0;
// free (buf); // Not freeing anymore, @ryandesign :-)
}
}
//printf("Total size: %d\n", totalSize);
*ExtractedSize = totalSize;
if (getenv("JDEBUG") != NULL)
{
int f = open ("/tmp/out1", O_WRONLY |O_CREAT);
write (f, returned, totalSize);
close(f);
}
return (returned);
#else
fprintf(stderr,"Not compiled with PBZX support!\n");
return (NULL);
#endif
} // pbzx
static int64_t getULEB128(const uint8_t* p, int Max, int *len)
{
int64_t result = 0;
int bit = 0;
*len = 0;
do {
(*len)++;
uint64_t slice = *p & 0x7f;
if ( /* len == Max || */ bit >= 64 || slice << bit >> bit != slice)
{ printf("ULEB128 malformed? (Len %d, Max : %d, bit: %d)\n", *len, Max, bit);
return 0;
}
else {
result |= (slice << bit);
bit += 7;
}
}
while (*p++ & 0x80);
return result;
}
#define YAA "YAA1"
int YAAright(unsigned char * mmapped, int Pos, uint64_t extractedSize) {
int i = 0;
if (memcmp(mmapped + pos, YAA, strlen(YAA)) != 0)
{ fprintf(stderr," NOT YAA\n");
return -1;
}
#if 0
D UID=0 GID=0 MOD=00755 FLG=0x00000000 MTM=1633578838.288343149 PAT=.
0615e6f56|0x112fc46d
MAGIC |ULEB | T Y P| 'D'| PAT '.'
00000000 59 41 41 31 36 00 54 59 50 31 44 50 41 54 50 00 |YAA16.TYP1DPATP.|
| U_I_D_ __0__ __GID____ __0__|
00000010 00 55 49 44 31 00 47 49 44 31 00 4d 4f 44 32 ed |.UID1.GID1.MOD2.|
00000020 01 46 4c 47 31 00 4d 54 4d 54|56 6f 5e 61 00 00 |.FLG1.MTMTVo^a..|
00000030 00 00 6d c4 2f 11 59 41 41 31 35 00 54 59 50 31 |..m./.
#endif
// nasty because I originalkly passed mmapped + pos. *Sigh*
//
mmapped = mmapped +Pos;
unsigned int yaaLen = mmapped[strlen(YAA)] ;
memcpy(&yaaLen, mmapped + strlen(YAA), sizeof(short));
uint32_t pos = strlen(YAA) + 2;
char* symlinkP = NULL;
int next = 0;
int dataLen = 0;
int aftLen = 0;
uint64_t attrVal = 0;
char *attrValText = NULL;
char *pat = NULL;
char typ = 0;
uint32_t mod = 0;
int p = (!g_extract) && (!g_search);
while (pos < yaaLen) {
uint32_t attr = 0;
//printf("READING ATTR FROM POS %d\n", pos);
memcpy (&attr, mmapped + pos, 4);
int len = (attr & 0xff000000) >> 24;
attr &= attr & 0x00ffffff;
if (strcmp(&attr, "AFT") == 0) {
//printf("LEN: %d", len);
len -='0';
memcpy(&aftLen, mmapped + pos + 4, len);
// printf("AFTLEN: %d", aftLen);
}
else
if (strcmp(&attr, "DAT") == 0) {
if (len =='A') {memcpy (&dataLen, mmapped + pos + 4, sizeof(short)); len = 2;}
if (len =='B') {memcpy (&dataLen, mmapped + pos + 4, sizeof(uint32_t)); len = 4;}
}
else
if (strcmp(&attr, "MTM") == 0) {
if (len == 'T') len =12;
if (len == 'S') len = 8;
}
else
if (strcmp(&attr,"LNK") == 0) {
next = 3;
int l = 0;
len = 0;
memcpy (&len, mmapped + pos + 4, sizeof(short));
symlinkP = alloca(len+1);
memcpy(symlinkP, mmapped+ pos +4 + sizeof(short), len);
symlinkP[len] = '\0';
len+=2;
}
else
if (strcmp(&attr,"PAT") == 0) {
// Null terminated attributes
next = 3;
int l = 0;
len = 0;
memcpy (&len, mmapped + pos + 4, sizeof(short));
pat = alloca(len+1);
memcpy(pat, mmapped+ pos +4 + sizeof(short), len);
pat[len] = '\0';
len +=2;
}
else {
if ((len >= '0') && (len <= '9')) {
len -= '0'; // :-)
}
else { fprintf(stderr,"Wrong length for attr %s ('%c' = 0x%x) at pos 0x%x\n", &attr, len, len, pos); }
if (len > 8) { fprintf(stderr,"wrong length at pos %d\n", pos); return -3;}
// Still here, so we're good:
attrVal = 0;
memcpy (&attrVal, mmapped + pos + 4, len);
if (strcmp(&attr, "TYP") == 0) { if (p) fprintf(stdout, "TYP: %c\t", attrVal); typ = mmapped [pos + 4]; }
else if (strcmp(&attr, "MOD") == 0) { if(p) fprintf(stdout, "MOD: 0%o ", attrVal); mod = attrVal; }
// else if (strcmp(&attr, "UID") == 0) { }
// else if (strcmp(&attr, "GID") == 0) { }
// else if (strcmp(&attr, "FLG") == 0) { }
else
if (p) fprintf(stdout, "%s: %-3d ", (char *)&attr, attrVal);
}
pos+=len + 4;
}
if (p) {
if (pat) fprintf(stdout,"PAT: %s (%lld bytes)",pat, dataLen);
fprintf(stdout,"\n");
}
if (pos > yaaLen) { fprintf(stderr,"POS 0x%x > YAALEN 0x%x\n", pos, yaaLen); exit(1);}
//printf("Got to right pos (0x%x), data Len : %d aft: %d\n", pos,dataLen, aftLen);
if (g_extract && symlinkP) {
// Handling symlinks, @S1guza :-)
symlink(symlinkP,pat);
}
else
processFileInner (mmapped, pos, pat, dataLen, (typ == 'D' ? S_IFDIR: S_IFREG) | mod);
return yaaLen + dataLen ; // + aftLen;;
}
void processFileInner( char *mmapped, uint32_t pos, char *name, uint32_t fileSize, uint32_t perms)
{
if (g_extract) { extractFile(mmapped +pos, name, fileSize, perms, g_extract);}
// Added 08/05/19 - Hash
if (g_hash) { hashFile (mmapped +pos, name, fileSize, perms, g_hash); }
if (g_search){
char *found = memmem (mmapped+pos, fileSize, g_search, strlen(g_search) + (g_nullTerm ? 1 : 0));
while (found != NULL)
{
int relOffset = found - mmapped - pos;
fprintf(stdout, "Found in Entry: %s, relative offset: 0x%x (Absolute: %lx)",
name,
relOffset,
found - mmapped);
// 12/01/18
if (g_verbose) {
fputc(':', stdout);
fputc(' ', stdout);
char *begin = found;
int i = 0 ;
#define BACK_LIMIT -20
#define FRONT_LIMIT 20
while(begin[i] && i > BACK_LIMIT) { i--;}
for (;begin +i < found; i++) {
if (isprint(begin[i])) putc (begin[i], stdout); else putc ('.', stdout); }
printf("%s%s%s",RED, g_search, NORMAL);
for (i+= strlen(g_search); begin[i] &&( i < FRONT_LIMIT); i++) {
if (isprint(begin[i])) putc (begin[i], stdout); else putc ('.', stdout); }
}
fprintf(stdout,"\n");
// keep looking..
found = memmem (found + 1, fileSize - relOffset , g_search, strlen(g_search) +( g_nullTerm ? 1: 0));
} // end while
} // end g_search
} // end processFile
void processFile(char *FileName)
{
int color = (getenv("JCOLOR")!= NULL);
fprintf(stderr, "%sProcessing %s%s\n", color ? RED: "", FileName, color ? NORMAL :"");
//unsigned char buf[4096];
uint64_t fileSize;
uint64_t mappedSize;
char *actualMmapped = mmapFile(FileName, &mappedSize);
fileSize = mappedSize;
if (actualMmapped == MAP_FAILED) { perror (FileName); return ;}
char *mmapped = actualMmapped;
char *extracted = NULL;
// File could be a PBZX :-)
if (memcmp(mmapped, PBZX_MAGIC, strlen(PBZX_MAGIC)) ==0)
{
// DO PBZX first!
int extractedSize = 0;
extracted = doPBZX (mmapped, mappedSize, &extractedSize);
mmapped = extracted;
fileSize = extractedSize;
printf("EXTRACTED: %p, size: 0x%llx\n",mmapped, fileSize);
int o = open ("/tmp/out", O_WRONLY|O_TRUNC| O_CREAT);
fchmod (o, 0644);
write (o, mmapped, extractedSize);
close(o);
}
if (memcmp(mmapped, YAA, strlen(YAA)) == 0) {
int pos = 0;
while (pos < fileSize) {
if (! g_extract && !g_search) printf("POS 0x%04x: ", pos);
int rc = YAAright(mmapped, pos, fileSize);
if (rc > 0) pos += rc;
else {
fprintf(stderr,"Position 0x%x: Not YAA or corrupt - skipping\n", pos);
// Seek to next YAA
char *next = memmem(mmapped+pos, fileSize -pos, YAA, strlen(YAA));
if (next) {
pos = next -mmapped;
}
}
}
return;
}
int i = 0;
struct entry *ent = alloca (sizeof(struct entry));
pos = 0;
while(pos + 3*sizeof(struct entry) < fileSize) {
ent = (struct entry *) (mmapped + pos );
pos += sizeof(struct entry);
if ((ent->usually_0x210_or_0x110 != 0x210 && ent->usually_0x210_or_0x110 != 0x110 &&
ent->usually_0x210_or_0x110 != 0x310) ||
ent->usually_0x00_00)
{
fprintf (stderr,"Corrupt entry (0x%x at pos %llu@0x%llx).. skipping\n", ent->usually_0x210_or_0x110,
pos, (uint64_t)(mmapped+pos));
int skipping = 1;
while (skipping)
{
ent = (struct entry *) (mmapped + pos ) ;
while (ent->usually_0x210_or_0x110 != 0x210 && ent->usually_0x210_or_0x110 != 0x110)
{
// #@$#$%$# POS ISN'T ALIGNED!
pos ++;
ent = (struct entry *) (mmapped + pos ) ;
}
// read rest of entry
int nl = ntohs(ent->nameLen);
if (ent->usually_0x00_00 || !nl) {
// fprintf(stderr,"False positive.. skipping %d\n",pos);
pos+=1;
}
else { skipping =0;
pos += sizeof(struct entry); }
if (pos > fileSize) return;
}
}
uint32_t size = swap32(ent->fileSize);
// fprintf(stdout," Here - ENT at pos %d: %x and 0 marker is %x namelen: %d, fileSize: %d\n", pos, ent->usually_0x210_or_0x110, ent->usually_0x00_00, ntohs(ent->nameLen), size);
uint32_t nameLen = ntohs(ent->nameLen);
// Get Name (immediately after the entry)
//
// 02/08/2016: Fixed this from alloca() - the Apple jumbo OTAs have so many files in them (THANKS GUYS!!)
// that this would exceed the stack limits (could solve with ulimit -s, or also by using
// a max buf size and reusing same buf, which would be a lot nicer)
// Note to AAPL: Life would have been a lot nicer if the name would have been NULL terminated..
// What's another byte per every file in a huge file such as this?
// char *name = (char *) (mmapped+pos);
char *name = alloca (nameLen+1);
strncpy(name, mmapped+pos , nameLen);
name[nameLen] = '\0';
//printf("NAME IS %s\n", name);
pos += ntohs(ent->nameLen);
uint32_t fileSize = swap32(ent->fileSize);
uint16_t perms = ntohs(ent->perms);
if (g_list){
if (g_verbose) {
printf ("Entry @0x%d: UID: %d GID: %d Mode: %o Size: %d (0x%x) Namelen: %d Name: ", i,
ntohs(ent->uid), ntohs(ent->gid),
perms, size, size,
ntohs(ent->nameLen));
}
printf ("%s\n", name);}
// Get size (immediately after the name)
if (fileSize)
{
// Added 08/31/16 - And I swear I should have this from the start.
// So darn simple and sooooo useful!
processFileInner (mmapped, pos, name, fileSize, perms);
pos +=fileSize;
}
} // Back to loop
if (extracted) { /*printf("FREEing %p\n", extracted);*/ free (extracted);}
munmap(actualMmapped, mappedSize);
}
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h> // for mkdir
/**
* Apple iOS OTA unpacker - by Jonathan Levin,
* http://NewOSXBook.com/
*
* Free for anyone to use, modify, etc. I won't complain :-), but I'd appreciate a mention
*
*/
typedef unsigned int uint32_t;
#pragma pack(1)
struct entry
{
unsigned int usually_0x10_01_00_00;
unsigned short usually_0x00_00; //_00_00;
unsigned int fileSize;
unsigned short whatever;
unsigned long long timestamp_likely;
unsigned short _usually_0x20;
unsigned short nameLen;
unsigned short uid;
unsigned short gid;
unsigned short perms;
char name[0];
// Followed by file contents
};
#pragma pack()
uint32_t
swap32(uint32_t arg)
{
return (ntohl(arg));
}
int g_list = 0;
int g_verbose = 0;
char *g_extract = NULL;
void
extractFile (char *File, char *Name, uint32_t Size, char *ExtractCriteria)
{
// MAYBE extract file (depending if matches Criteria, or "*").
// You can modify this to include regexps, case sensitivity, what not.
// presently, it's just strstr()
if (!ExtractCriteria) return;
if ((ExtractCriteria[0] != '*') && ! strstr(Name, ExtractCriteria)) return;
// Ok. Extract . This is simple - just dump the file contents to its directory.
// What we need to do here is parse the '/' and mkdir(2), etc.
char *dirSep = strchr (Name, '/');
while (dirSep)
{
*dirSep = '\0';
mkdir(Name,0755);
*dirSep = '/';
dirSep+=1;
dirSep = strchr (dirSep, '/');
}
// at this point we're out of '/'s
// go back to the last /, if any
if (g_verbose)
{
fprintf(stderr, "Dumping %d bytes to %s\n", Size, Name);
}
int fd = open (Name, O_WRONLY| O_CREAT);
fchmod (fd, 0644);
write (fd, File, Size);
close (fd);
} // end extractFile
int
main(int argc ,char **argv)
{
char *filename ="p";
int i = 0;
if (argc < 2) {
fprintf (stderr,"Usage: %s [-v] [-l] [-e file] _filename_\nWhere: -l: list files in update payload\n -e _file: extract file from update payload (use \"*\" for all files)\n", argv[0]);
exit(10);
}
for (i = 1;
i < argc -1;
i++)
{
// This is super quick/dirty. You might want to rewrite with getopt, etc..
if (strcmp (argv[i], "-l") == 0) { g_list++;}
if (strcmp (argv[i] , "-v") == 0) { g_verbose++;}
if (strcmp (argv[i], "-e") == 0) { g_extract = argv[i+1]; i++;}
}
filename =argv[argc-1];
//unsigned char buf[4096];
int fd = open (filename, O_RDONLY);
if (fd < 0) { perror (filename); exit(1);}
#if 0
// Don't need this anymore :-)
// Cheat - actually use 0x00 0x50 0x81 0xb4 as an anchor
read (fd, buf, 4096);
for (i = 0 ; i < 4096; i++)
{
if ((buf[i] == 0x00) && (buf[i+1] == 0x50) && (buf[i + 2] == 0x81) && (buf[i+3] == 0xb4))
{
printf("Got anchor\n");
lseek(fd, i-26, SEEK_SET);
break;
}
}
if (i == 4096) { fprintf (stderr, "Unable to find anchor\n"); exit(5);}
// Otherwise, we can start reading entries from here:
#endif
i = 0;
int bytes_read = 1;
struct entry *ent = alloca (sizeof(struct entry));
while(bytes_read ){
bytes_read = read(fd, (void *) ent, sizeof(struct entry));
if (bytes_read <= 0) { // fprintf (stderr, "Error reading next entry or EOF (this could be OK if it's the last entry, @TODO implement == 0 or fstat on filesize..)\n");
exit(1);}
i+= bytes_read;
uint32_t size = swap32(ent->fileSize);
// Get Name (immediately after the entry)
//
// 02/08/2016: Fixed this from alloca() - the Apple jumbo OTAs have so many files in them (THANKS GUYS!!)
// that this would exceed the stack limits (could solve with ulimit -s, or also by using
// a max buf size and reusing same buf)
char *name = malloc(ntohs(ent->nameLen) + 1);
bytes_read = read (fd , (void *) name, ntohs(ent->nameLen));
if (bytes_read <= 0) { fprintf (stderr, "Error reading Name. This is not ok\n"); exit(2);}
i+= bytes_read;
name[ntohs(ent->nameLen)] = '\0';
if (g_list){
if (g_verbose) {
printf ("Entry @0x%d: UID: %d GID: %d Size: %d (0x%x) Namelen: %d Name: ", i,
ntohs(ent->uid), ntohs(ent->gid),
size, size,
ntohs(ent->nameLen));
}
printf ("%s\n", name);}
// Get size (immediately after the name)
uint32_t fileSize = swap32(ent->fileSize);
if (fileSize)
{
char *file = malloc (fileSize);
bytes_read = read (fd, (void *) file, fileSize);
// Zero length file sizes are apparently ok..
if (bytes_read <= 0) { fprintf (stderr, "Error reading file. This is not ok \n"); exit(3);}
i+= bytes_read;
if (g_extract) { extractFile(file, name, fileSize, g_extract);}
free(file);
}
free (name);
} // Back to loop
close(fd);
}
#include <stdio.h>
#include <string.h>
#include <unistd.h> // access(2)
#include <fcntl.h>
#include <libgen.h>
#include <stdlib.h>
/*
* ojtool (?)
*
* What: A simple filter for otool(1), to resolve %rip relative references
* to absolute addresses.
*
* Why: Because IDA is too cumbersome, and Hopper isn't light. Or free. Sometimes you need a
* simple command line disassembler, and jtool doesn't support i386/x86_64.
*
*
* Usage: otool -tV _x86_64_Mach_O_ | ojtool -- DEPRECATED
* or
* ojtool _x86_64_Mach_O_ (Use this for full functionality)
*
* How: This tool reads and buffers lines from otool(1) output. Each line is passed through
* verbatim, unless it contains a "(%rip)" string, in which case:
*
* - If the string is prefixed by a symbol, nothing
*
* - If the string is prefixed by a positive or negative hex number, the line is buffered
* and only printed once the next line is processed, and its address is obtained, so as
* to resolve the simple match of adding the value of RIP.
*
* If the resolved address is found in jtool -d __TEXT.__cstring or __TEXT.__cfstring,
* it will also print out the string :-) - for this you need to supply the MachO name.
*
* (otool used to do that, but now doesn't, on some binaries. Go figure. But then, AAPL
* rewrote it to itself invoke llvm-* internally. Ugh. Guys, why'd you ruin further what
* is/was an already half crummy tool?
*
* - If the address in the binary is a function (recognized by function_starts) - print it
* - If the address in a call instruction is in stubs, try to resolve stub
*
* Comments: It's a super simple tool. Nothing fancy. I cobbled this together because I find myself
* reversing with otool(1) quite a bit, and got tired of perl -e 'printf...'. This could
* probably be nicer in Python. But I loathe Python.
*
* YOU NEED JTOOL IN YOUR PATH IF YOU WANT [CF]STRING RESOLUTION
*
* For fat binaries, remember to export ARCH=x86_64
*
* Now in color :-)
*
* Disclaimer: Might be a bug or two here. Works and gets the math right for the binaries I've tested,
* x86_64 kernel included. If I ever add x86_64 to jtool (which isn't happening, since only
* about five people asked...), this will be obsolete
*
* Greets: Apple. Because otool still disassembles Intel and ARM32 better than I ever could hope to.
* But disassembly aside (and ARM64), jtool trumps otool any time.
* Who knows - maybe Apple will integrate this super simple logic into otool in 2400 :-)
*
* @JeremyAgost - here's another alternative to those of us who just need a CLI. (11/05/2016)
* Luca - because otool sucks, and jtool still can't do x86_64, but maybe this hybrid
* approach will work in the meanwhile ?
*
*/
#define RED "\033[0;31m"
#define PINK "\e[0;35m"
#define GREEN "\e[0;32m"
#define CYAN "\e[0;36m"
#define NORMAL "\033[0;0m"
int g_Color = 0;
char *ohMyGOT = "";
char *JToolData = "";
char *JToolObjCData = "";
char *JToolFuncStarts = "";
char *JToolComp = "";
#define MAX_LAZY_BINDS 16384
char *lazyBinds[MAX_LAZY_BINDS] = { };
int numLazyBinds = 0;
uint64_t stubsStart, stubsEnd = 0;
void getJToolLazyBindData (char *File)
{
if (!File) return;
char cmd[1024];
// YES THERE IS COMMAND INJECTION HERE. SO WHAT.
snprintf (cmd, 1024, "jtool -lazy_bind %s 2>/dev/null", File);
FILE *jtoolInput = popen (cmd,"r");
// present limit is 4MB of string data...
char *data = calloc (1*1024*1024,1);
size_t readBytes = fread(data, // void *restrict ptr,
2*1024*1024, // size_t nitems,
1,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
char *newline = NULL;
// skip two first lines
newline = strchr(data, '\n') ;
if (!newline) { fprintf(stderr,"Unable to get lazy_bind data from jtool?! No first newline\n");
return ;}
newline = strchr(newline +1, '\n') ;
if (!newline) { fprintf(stderr,"Unable to get lazy_bind data from jtool?! No second newline\n");
return ;}
// Ok. Now let's talk.
int l = 0;
// Iterate over lazy bind data. Look only for pattern of " _[^ ]\n"
while ((newline = strchr(newline + 1, '\n')) != NULL) {
// Go back from the new line to find first space.
*newline ='\0';
int back = -1;
while (newline[back] != ' ')
{
back--;
}
// verify that next is "_"
if (newline[back+1] != '_') {
fprintf(stderr,"Warning: Ignoring entry %s\n",
newline + back + 1);
}
lazyBinds[numLazyBinds] = newline+back+1;
if (getenv ("JDEBUG")) {fprintf(stderr,"Added lazy bind: %s\n",
lazyBinds[numLazyBinds]); }
numLazyBinds++;
}
} // getJToolLazyBindData
void getJToolStubData (char *File)
{
// Ok - this is quick, filthy, but it works:
// The observation is that the compiler always creates the __TEXT.__stubs
// (which jtool presently can't read) to contain JMP instructions to the __la_symbol_ptr
// (which jtool can read, through -lazy_bind)
// So:
// A) We find the beginning and size of __stubs using strstr on jtool -l.
//
// B) We dump all -lazy_bind symbols using jtool into an array
//
// C) We calculate the stubs by taking *offset* into __stubs,
// then dividing by six (due to jmp), and checking array entry :-)
if (!File) return;
char cmd[1024];
// YES THERE IS COMMAND INJECTION HERE. SO WHAT.
snprintf (cmd, 1024, "jtool -l %s 2>/dev/null", File);
FILE *jtoolInput = popen (cmd,"r");
// present limit is 4MB of string data...
char *data = calloc (1*1024*1024,1);
size_t readBytes = fread(data, // void *restrict ptr,
2*1024*1024, // size_t nitems,
1,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
// Look for this pattern: Mem: 0x10004b0fe-0x10004b2e4 __TEXT.__stubs (Symbol Stubs)
// if (readBytes <0) { fprintf(stderr,"Error popen(2)ing jtool for stubs\n"); return; }
char *stubs = strstr (data, "(Symbol Stubs)");
if (!stubs) { fprintf(stderr,"Warning - can't get stubs. Wait till J puts a better mechanism in place..\n");
return; // not fatal
}
// seek back "Mem: "
int i = (stubs - data);
while (i > 0)
{
if (memcmp (data +i, "Mem: ",5) == 0)
{
// got it! sscanf will work here because that's the way jtool printf()ed it..
int rc = sscanf(data+i + 5,"0x%llx-0x%llx", &stubsStart, &stubsEnd);
// but we'll still check
if (rc !=2) {
fprintf(stderr,"Warning - Can't figure out stubs from jtool output.. \n");
return;
}
//printf("HERE! stubsStart: %lx, End %lx\n", stubsStart,stubsEnd);
// Now get lazy bind data
getJToolLazyBindData(File);
return;
}
else i--;
}
pclose (jtoolInput);
}
// @TODO: Just work with companion files!
//
void getJToolFuncStarts(char *File)
{
// AAPL: Why %$#%$ do LC_FUNCTION_STARTS if otool (now objdump) ignores it?!
if (!File) return;
char cmd[1024];
FILE *jtoolInput = popen (cmd,"r");
// present limit is 4MB of string data...
char *data = calloc (4*1024*1024,1);
size_t readBytes = 0;
// 05/26 Prefer companion file
char compFile[10240];
char *jtooldir = getenv("JTOOLDIR");
if (jtooldir) { strncpy(compFile,
jtooldir, 1024);}
else strcpy(compFile,".");
strcat (compFile,"/");
// YES THERE IS COMMAND INJECTION HERE. SO WHAT.
snprintf (cmd, 1024, "jtool -l %s | grep UUID| cut -d':' -f3", File);
jtoolInput = popen (cmd,"r");
readBytes = fread(data, // void *restrict ptr,
2*1024*1024, // size_t nitems,
1,
jtoolInput); // FILE *restrict stream);
int x = 0;
int y = 0;
while ((!data[x]) || data[x] == ' ') x++;
//data [x] now points to uuid
y = x;
while (((data[y] != '\n') && data[y] != ' ')) y++;
data[y] = '\0';
char *bn = basename(File);
if (!bn) { perror("basename"); bn = File;}
strcat (compFile, bn);
strcat (compFile, ".");
strcat (compFile, "x86_64.");
strcat (compFile, data+x);
fprintf(stderr,"Trying companion file : %s\n", compFile);
if (access(compFile, R_OK) == 0)
{
int fd = open (compFile, O_RDONLY);
read (fd, data, 2*1024*1024);
JToolComp = data;
return;
}
else
{
fprintf(stderr,"Tip: Consider creating a companion file using jtool --jtooldir . -d __DATA.__const > /dev/null\n");
}
pclose(jtoolInput);
snprintf (cmd, 1024, "jtool -function_starts %s 2>/dev/null", File);
jtoolInput = popen (cmd,"r");
// present limit is 4MB of string data...
readBytes = fread(data, // void *restrict ptr,
2*1024*1024, // size_t nitems,
1,
jtoolInput); // FILE *restrict stream);
if (readBytes == 2*1024*1024) {
fprintf(stderr,"WHOA. More than 2MB of jtool -function_starts ... data. Yikes. Recompile otoolfilt with a larger limit.\n");
exit(1);
}
data[readBytes]='\0'; //important :-)
JToolFuncStarts = data;
pclose (jtoolInput);
};
void getJToolData(char *File)
{
/**
* Jtool can't disassemble x86_64; otool can.
* otool can't do anything BUT disassemble x86_64, and often ignores string data. Jtool does so much more.
*
* Combine the two by running jtool -d __... on the binary (for now, only __TEXT.__cstring)
* and capture the data.
*/
if (!File) return;
char cmd[1024];
// YES THERE IS COMMAND INJECTION HERE. SO WHAT.
snprintf (cmd, 1024, "jtool -d __TEXT.__cstring %s 2>/dev/null", File);
FILE *jtoolInput = popen (cmd,"r");
// present limit is 4MB of string data...
char *data = calloc (4*1024*1024,1);
size_t readBytes = fread(data, // void *restrict ptr,
2*1024*1024, // size_t nitems,
1,
jtoolInput); // FILE *restrict stream);
if (readBytes == 2*1024*1024) {
fprintf(stderr,"WHOA. More than 2MB of jtool -d ... data. Yikes. Recompile otoolfilt with a larger limit.\n");
}
pclose(jtoolInput);
// Append CFString data
snprintf (cmd, 1024, "jtool -d __DATA.__cfstring %s 2>/dev/null", File);
jtoolInput = popen (cmd,"r");
readBytes = fread(data +strlen(data), // void *restrict ptr,
1, // size_t size,
2*1024*1024, // size_t nitems,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
// Get the GOT
snprintf(cmd, 1024, "JCOLOR=0 jtool -d __DATA.__got %s 2> /dev/null", File);
jtoolInput = popen(cmd,"r");
jtoolInput = popen (cmd,"r");
ohMyGOT = calloc (2,1024*1024);
readBytes = fread(ohMyGOT, // void *restrict ptr,
1, // size_t size,
2*1024*1024, // size_t nitems,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
JToolData = data;
}
void getJToolObjCData(char *File)
{
char *data = calloc (4*1024*1024,1);
if (!File) return;
char cmd[1024];
// Append CFString data
snprintf (cmd, 1024, "jtool -d __DATA.__objc_classrefs %s 2>/dev/null", File);
FILE *jtoolInput = popen (cmd,"r");
size_t readBytes = fread(data +strlen(data), // void *restrict ptr,
1, // size_t size,
2*1024*1024, // size_t nitems,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
snprintf (cmd, 1024, "jtool -d __DATA.__objc_superrefs %s 2>/dev/null", File);
jtoolInput = popen (cmd,"r");
readBytes = fread(data +strlen(data), // void *restrict ptr,
1, // size_t size,
2*1024*1024, // size_t nitems,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
snprintf (cmd, 1024, "jtool -d __DATA.__objc_selrefs %s 2>/dev/null", File);
jtoolInput = popen (cmd,"r");
readBytes = fread(data +strlen(data), // void *restrict ptr,
1, // size_t size,
2*1024*1024, // size_t nitems,
jtoolInput); // FILE *restrict stream);
pclose(jtoolInput);
JToolObjCData = data;
}
int main (int argc, char **argv)
{
g_Color = (getenv ("JCOLOR") != NULL);
char line[1024];
int fail = 0;
int hexNum = 0;
int rc = 0;
int otoolKnewThis = 0;
unsigned long long rip = 0;
char *saved = NULL;
char *savedRR = NULL;
char *ripRef = NULL;
FILE *input = stdin;
if (argc > 1) {
// then our next (and only expected) argument is the name of the binary
// run otool -tV on the binary.
// NOTE THIS IS NOT MEANT TO BE SECURE. All I want is to run /usr/bin/otool -tV.
// Don't you go issuing CVEs on command injection and all that crap.
char cmd[1024];
rc = access("/usr/bin/otool", X_OK);
if (rc < 0) { fprintf(stderr,"I can't find and/or execute otool(1). Is xcode installed?\n"); exit(1);}
snprintf (cmd, 1024, "/usr/bin/otool -tV %s", argv[1]);
input = popen (cmd, "r");
// Get jtool -d __TEXT.__cstring
getJToolData (argv[1]);
getJToolObjCData (argv[1]);
getJToolFuncStarts (argv[1]);
getJToolStubData (argv[1]);
}
fgets (line, 1024, input);
while (!feof(input))
{
rc = sscanf (line, "%llx", &rip);
if (saved)
{
// flush any lines from a previous instance, now that we have IP
fputs (saved, stdout);
// hexNum + pc
char resolved[1024];
sprintf (resolved, "0x%llx:", rip + hexNum);
printf("0x%llx", rip+hexNum);
hexNum = 0;
// But wait - maybe this is a string or something jtool has resolved!
// The strstr is crude, but efficient - I'm assuming here jtool -d
// returns data formatted as 0x........: .......
// Could be GOT
int otoolKnowsThis = ( strstr(savedRR,"literal") || strstr(savedRR,"Objc"));
char *jtoolKnowsThis =NULL;
char *jtoolKnowsObjC = NULL;
char *isGOT = NULL;
if (!otoolKnowsThis)
{ jtoolKnowsThis = strstr (JToolData,resolved);
jtoolKnowsObjC = strstr(JToolObjCData,resolved);
isGOT = strstr(ohMyGOT, resolved);
}
else {
isGOT = jtoolKnowsObjC = jtoolKnowsThis = NULL;
}
if (isGOT)
{
char *newline = strchr (savedRR, '\n');
if (newline) *newline = '\0';
fprintf (stdout, "%s",savedRR);
// Now print what jtool knows...
newline = strchr (isGOT, '\n');
*newline = '\0';
int l = strlen(isGOT) ;
// GOT is 0000. Curses #@$@#$ a single 0
// sometimes. so workaround
while ((l > 0) && (isGOT[l] !='0')) {
l--;
}
printf("\t ; %s%s%s!\n",
g_Color ? GREEN :"",
isGOT +l +1,
g_Color ? NORMAL :"");
*newline = '\n';
}
if (jtoolKnowsObjC)
{
// get the \n from savedRR
char *newline = strchr (savedRR, '\n');
if (newline) *newline = '\0';
fprintf (stdout, "%s",savedRR);
// Now print what jtool knows...
newline = strchr (jtoolKnowsObjC, '\n');
*newline = '\0';
// for sure we have that jtool will display a : somewhere in line
// Go back from the newline to the first ' '
int l = strlen(jtoolKnowsObjC) -3;
while (l > 0 && jtoolKnowsObjC[l] !=' ') l--;
jtoolKnowsObjC+=l;
printf("\t ; %s%s%s\n",
g_Color ? PINK :"",
jtoolKnowsObjC,
g_Color ? NORMAL :"");
*newline = '\n';
}
else
if (jtoolKnowsThis)
{
// get the \n from savedRR
char *newline = strchr (savedRR, '\n');
if (newline) *newline = '\0';
fprintf (stdout, "%s",savedRR);
// Now print what jtool knows...
newline = strchr (jtoolKnowsThis, '\n');
*newline = '\0';
// for sure we have that jtool will display a : somewhere in line
jtoolKnowsThis = strchr (jtoolKnowsThis , ':') + 2;
printf("\t ; \"%s%s%s\"\n",
g_Color ? RED :"",
jtoolKnowsThis,
g_Color ? NORMAL :"");
*newline = '\n';
// if (getenv("JCOLOR") ) printf("\e[0;0m");
}
else
fprintf (stdout, "%s", savedRR);
free (saved); // no mem leak here :)
saved = savedRR = NULL;
}
ripRef = strstr (line, "(%rip)");
if (ripRef)
{
// get the actual number
*ripRef = '\0';
char *hexRef = strrchr (line, 'x');
if (!hexRef || (hexRef[-1] != '0')) {
// We can fail if this a symbol(%rip) reference
// in which case just restore what was before
*ripRef = '(';
fail++;
}
else {
hexRef--;
rc = sscanf (hexRef, "%x", &hexNum);
if (rc !=1) fail++;
ripRef += 6;
if (hexRef[-1] == '-')
{
hexNum = 0 - hexNum;
hexRef--;
}
*hexRef = '\0';
saved = strdup(line);
savedRR = strdup(ripRef);
}
}
if (!saved)
{
// 4/4/17: Is this is a known funcstart?
// want to do this if previous line doesn't end with ":", meaning otool knew it
// as an exported symbol
char temp[16];
sprintf(temp, ": 0x%llx ", rip);
char *jtoolKnowsThis = NULL;
if (!otoolKnewThis)
{
if (JToolComp)
{
sprintf(temp, "0x%llx:", rip);
jtoolKnowsThis = strstr(JToolComp,temp);
}
else
jtoolKnowsThis = strstr(JToolFuncStarts,temp);
}
if (jtoolKnowsThis) {
// Got func starts - get from end of match to \n..
char *func = jtoolKnowsThis + strlen(temp);
char *end = strchr(func, 0xa);
end[0] = '\0';
fprintf(stdout, "%s%s%s:\n",
g_Color ? PINK: "", func,
g_Color ? NORMAL : "");
end[0] = 0xa;
}
// 9/15/17 - much better - look for address rather than instruction
uint64_t callAddr = 0;
char *addr = strstr (line, "0x");
char *otoolKnowsThis = strstr(line, "##");
int rc = 0;
if (addr && !otoolKnowsThis)
rc = sscanf (addr, "0x%llx", &callAddr);
// 4/16/17 - is this a callq?
if (callAddr)
{
// Try to resolve stub..
if (rc == 1)
{
// First lookup in cached symbols
char addrAgain[32] = { 0};
sprintf(addrAgain, "0x%llx:", callAddr);
jtoolKnowsThis = strstr(JToolComp,addrAgain);
if (jtoolKnowsThis) {
char *newline = strchr (jtoolKnowsThis, 0xa);
jtoolKnowsThis = strstr(jtoolKnowsThis,":") +1;
//char save = newline[0];
*addr = '\0';
*newline = '\0';
char *name = strdup (jtoolKnowsThis);
printf ("%s %s%s%s \n",
line, g_Color? CYAN : "",
name,
g_Color ? NORMAL : "");
free(name);
*newline = 0xa;
}
else // We have a call, maybe a stub
if (callAddr >= stubsStart && callAddr <= stubsEnd)
{
// remove the \n
char *newline = strchr (line,'\n');
if (newline ) *newline ='\0';
int off = ( callAddr - stubsStart)/6;
//@TODO : SANITY: multiple of 6 and not over lazyBinds[l]
if ((off < 0) || (off > numLazyBinds)) {
printf("%s ; Error: not a stub (offset %x)\n", line, off);
}
else {
// replace the whole callq part of line!
printf ("%s %s%s%s (stub #%d)\n",
line, g_Color? CYAN : "",
lazyBinds[off],
g_Color ? NORMAL : "",
off);
}
}
else { // not a stub, but maybe in funcstarts?
fputs(line,stdout);
}
} // rc == 1
else {
// This could be a GOT entry - those have 000000000
// sprintf(temp,"0x%llx: 0x0000000000000000", callAddr);
fputs(line,stdout);
}
}
else
{
fputs (line, stdout);
if (strstr (line, ":\n")) { otoolKnewThis = 1; }
else otoolKnewThis = 0;
}
}
fail = 0;
fgets (line, 1024, input);
}
} // end main
#include <mach/mach.h>
#include <mach/mach_port.h>
#include <bootstrap.h> // for extern bootstrap_port
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv){
printf ("0x%x\n", bootstrap_port);
// A port 'name' is the uint32_t opaque identifier of a port
// right in user mode.
mach_port_name_t myPort;
ipc_space_inspect_t target = mach_task_self();
kern_return_t kr = mach_port_allocate(target, // ipc_space_t task,
MACH_PORT_RIGHT_RECEIVE, // mach_port_right_t right,
&myPort); // mach_port_name_t *name
printf("my port: 0x%x\n", myPort);
kern_return_t kr2 = mach_port_insert_right(target, // ( ipc_space_t task,
myPort, // mach_port_name_t name,
myPort, // mach_port_t poly,
MACH_MSG_TYPE_MAKE_SEND); // mach_msg_type_name_t polyPoly
printf("KR2 was: %d\n", kr2);
ipc_info_space_t space_info;
ipc_info_name_array_t table_info;
mach_msg_type_number_t table_infoCnt;
ipc_info_tree_name_array_t tree_info;
mach_msg_type_number_t tree_infoCnt;
kern_return_t kr1 = mach_port_space_info (target, // ipc_space_inspect_t task,
&space_info, // ipc_info_space_t *space_info,
&table_info, // ipc_info_name_array_t *table_info,
&table_infoCnt, // mach_msg_type_number_t *table_infoCnt,
// UNUSED
&tree_info , // ipc_info_tree_name_array_t *tree_info,
&tree_infoCnt); // mach_msg_type_number_t *tree_infoCnt
if (kr1 != KERN_SUCCESS) {
// fprintf(stderr,"ERROR: %s\n", mach_error_string(mach_error));
exit(1);
}
#if 0
typedef struct ipc_info_name {
mach_port_name_t iin_name; /* port name, including gen number */
/*boolean_t*/ integer_t iin_collision; /* collision at this entry? */
mach_port_type_t iin_type; /* straight port type */
mach_port_urefs_t iin_urefs; /* user-references */
natural_t iin_object; /* object pointer/identifier */
natural_t iin_next; /* marequest/next in free list */
natural_t iin_hash; /* hash index */
} ipc_info_name_t;
#endif
int p = 0;
for (p =0; p < table_infoCnt; p++) {
printf("%d: 0x%x, Type: 0x%x, urefs: %d, Object: 0x%x\n",
p,
table_info[p].iin_name,
table_info[p].iin_type,
table_info[p].iin_urefs,
table_info[p].iin_object);
}
}
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
// Changelog:
// 02/17/2016 - Fixed so it works with Apple TV OTA PBZX
// 07/28/2016 - Fixed to handle uncompressed chunks and integrate XZ (via liblzma)
// 03/09/2016 - Fixed for single chunks (payload.0##) files, e.g. WatchOS
// To compile: gcc pbzx.c 02_decompress.c -o pbzx -llzma
// On Linux, make sure to install xz-devel first
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
#define PBZX_MAGIC "pbzx"
// This is in 02_decompress.c, modified from liblzma's examples
// I intentionally left that external since it's not my code.
extern void decompressXZChunkToStdout(char *buf, int length);
int main(int argc, const char * argv[])
{
// Dumps a pbzx to stdout. Can work as a filter if no argument is specified
char buffer[1024];
int fd = 0;
int minChunk = 0;
if (argc < 2) { fd = 0 ;}
else { fd = open (argv[1], O_RDONLY);
if (fd < 0) { perror (argv[1]); exit(5); }
}
if (argc ==3) {
minChunk = atoi(argv[2]);
fprintf(stderr,"Starting from Chunk %d\n", minChunk);
}
read (fd, buffer, 4);
if (memcmp(buffer, PBZX_MAGIC, 4)) { fprintf(stderr, "Can't find pbzx magic\n"); exit(0);}
// Now, if it IS a pbzx
uint64_t length = 0, flags = 0;
read (fd, &flags, sizeof (uint64_t));
flags = __builtin_bswap64(flags);
fprintf(stderr,"Flags: 0x%llx\n", flags);
int i = 0;
int off = 0;
int warn = 0 ;
int skipChunk = 0;
int rc = 0;
// 03/09/2016 - Fixed for single chunks (payload.0##) files, e.g. WatchOS
// and for multiple chunks. AAPL changed flags on me..
//
// New OTAs use 0x800000 for more chunks, not 0x01000000.
while (flags & (0x800000 | 0x01000000)) { // have more chunks
i++;
rc= read (fd, &flags, sizeof (uint64_t)); // check retval..
flags = __builtin_bswap64(flags);
rc = read (fd, &length, sizeof (uint64_t));
length = __builtin_bswap64(length);
skipChunk = (i < minChunk);
fprintf(stderr,"Chunk #%d (flags: %llx, length: %lld bytes) %s\n",i, flags,length,
skipChunk? "(skipped)":"");
// Let's ignore the fact I'm allocating based on user input, etc..
char *buf = malloc (length);
int bytes = read (fd, buf, length);
int totalBytes = bytes;
// 6/18/2017 - Fix for WatchOS 4.x OTA wherein the chunks are bigger than what can be read in one operation
while (totalBytes < length) {
// could be partial read
bytes = read (fd, buf +totalBytes, length -totalBytes);
totalBytes +=bytes;
}
// We want the XZ header/footer if it's the payload, but prepare_payload doesn't have that,
// so just warn.
if (memcmp(buf, "\xfd""7zXZ", 6)) { warn++;
fprintf (stderr, "Warning: Can't find XZ header. Instead have 0x%x(?).. This is likely not XZ data.\n",
(* (uint32_t *) buf ));
// Treat as uncompressed
write (1, buf, length);
}
else // if we have the header, we had better have a footer, too
{
if (strncmp(buf + length - 2, "YZ", 2)) { warn++; fprintf (stderr, "Warning: Can't find XZ footer at 0x%llx (instead have %x). This is bad.\n",
(length -2),
*((unsigned short *) (buf + length - 2)));
}
if (1 && !skipChunk)
{
// Uncompress chunk
decompressXZChunkToStdout(buf, length);
}
warn = 0;
free (buf); // Thanks ryandesign (again :-)
}
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
// Changelog:
// 02/17/2016 - Fixed so it works with Apple TV OTA PBZX
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
#define PBZX_MAGIC "pbzx"
int main(int argc, const char * argv[])
{
// Dumps a pbzx to stdout. Can work as a filter if no argument is specified
char buffer[1024];
int fd = 0;
int minChunk = 0;
if (argc < 2) { fd = 0 ;}
else { fd = open (argv[1], O_RDONLY);
if (fd < 0) { perror (argv[1]); exit(5); }
}
if (argc ==3) {
minChunk = atoi(argv[2]);
fprintf(stderr,"Starting from Chunk %d\n", minChunk);
}
read (fd, buffer, 4);
if (memcmp(buffer, PBZX_MAGIC, 4)) { fprintf(stderr, "Can't find pbzx magic\n"); exit(0);}
// Now, if it IS a pbzx
uint64_t length = 0, flags = 0;
read (fd, &flags, sizeof (uint64_t));
flags = __builtin_bswap64(flags);
fprintf(stderr,"Flags: 0x%llx\n", flags);
int i = 0;
int off = 0;
int warn = 0 ;
int skipChunk = 0;
while (flags & 0x01000000) { // have more chunks
i++;
read (fd, &flags, sizeof (uint64_t));
flags = __builtin_bswap64(flags);
read (fd, &length, sizeof (uint64_t));
length = __builtin_bswap64(length);
skipChunk = (i < minChunk);
fprintf(stderr,"Chunk #%d (flags: %llx, length: %lld bytes) %s\n",i, flags,length,
skipChunk? "(skipped)":"");
// Let's ignore the fact I'm allocating based on user input, etc..
char *buf = malloc (length);
int bytes = read (fd, buf, length);
int totalBytes = bytes;
// 6/18/2017 - Fix for WatchOS 4.x OTA wherein the chunks are bigger than what can be read in one operation
while (totalBytes < length) {
// could be partial read
bytes = read (fd, buf +totalBytes, length -totalBytes);
totalBytes +=bytes;
}
fprintf(stderr,"Total Bytes: %ld\n", totalBytes);
// We want the XZ header/footer if it's the payload, but prepare_payload doesn't have that,
// so just warn.
if (memcmp(buf, "\xfd""7zXZ", 6)) { warn++;
fprintf (stderr, "Warning: Can't find XZ header. Instead have 0x%x(?).. This is likely not XZ data.\n",
(* (uint32_t *) buf ));
}
else // if we have the header, we had better have a footer, too
if (strncmp(buf + length - 2, "YZ", 2)) { warn++; fprintf (stderr, "Warning: Can't find XZ footer at 0x%llx (instead have %x). This is bad.\n",
(length -2),
*((unsigned short *) (buf + length - 2))); }
if (1 && !skipChunk)
{
write (1, buf, length);
}
warn = 0;
}
return 0;
}
#!/usr/sbin/dtrace -s
#pragma D option quiet
BEGIN
{
self->boot_args = ((struct boot_args*)(`PE_state).bootArgs);
self->deviceTreeHead = ((struct boot_args*)(`PE_state).deviceTreeHead);
self->video = ((PE_Video ) (`PE_state).video);
printf("EFI: %d-bit\n", self->boot_args->efiMode);
printf("Video: Base Addr: %p\n", self->video.v_baseAddr);
printf("Video is in %s mode\n", (self->video.v_display == 1 ? "Graphics" : "Text"));
printf ("Runtime services is %p\n", (self->boot_args->efiRuntimeServicesPageStart));
printf("Video resolution: %dx%dx%d\n",
self->video.v_width, self->video.v_height, self->video.v_depth);
printf ("Kernel command line : %s\n", self->boot_args->CommandLine);
printf ("Kernel begins at physical address 0x%x and spans %d bytes\n",
self->boot_args->kaddr, self->boot_args->ksize);
printf ("Device tree begins at physical address 0x%x and spans %d bytes\n",
self->boot_args->deviceTreeP, self->boot_args->deviceTreeLength);
printf ("Memory Map of %d bytes resides in physical address 0x%x\n",
self->boot_args->MemoryMapSize,
self->boot_args->MemoryMap);
// KASLR? Not when you have DTrace..
printf ("Kernel Slide: %x\n", self->boot_args->kslide);
printf ("Physical memory size: %d\n",self->boot_args->PhysicalMemorySize);
printf("FSB Frequency: %d\n",self->boot_args->FSBFrequency);
exit(0);
}
#!/usr/sbin/dtrace -s
#pragma D option quiet
BEGIN
{
self->boot_args = ((struct boot_args*)(`PE_state).bootArgs);
self->deviceTreeHead = ((struct boot_args*)(`PE_state).deviceTreeHead);
self->video = ((PE_Video ) (`PE_state).video);
printf("EFI: %d-bit\n", self->boot_args->efiMode);
printf("Video: Base Addr: %p\n", self->video.v_baseAddr);
printf("Video is in %s mode\n", (self->video.v_display == 1 ? "Graphics" : "Text"));
printf ("Runtime services is %p\n", (self->boot_args->efiRuntimeServicesPageStart));
printf("Video resolution: %dx%dx%d\n", self->video.v_width, self->video.v_height, self->video.v_depth);
printf ("Kernel command line : %s\n", self->boot_args->CommandLine);
printf ("Kernel begins at physical address 0x%x and spans %d bytes\n",
self->boot_args->kaddr, self->boot_args->ksize);
printf ("Device tree begins at physical address 0x%x and spans %d bytes\n",
self->boot_args->deviceTreeP, self->boot_args->deviceTreeLength);
printf ("Memory Map of %d bytes resides in physical address 0x%x\n",
self->boot_args->MemoryMapSize,
self->boot_args->MemoryMap);
printf ("Kernel Slide: %x\n", self->boot_args->kslide);
printf("Physical memory size: %d\n",self->boot_args->PhysicalMemorySize);
printf("FSB Frequency: %d\n",self->boot_args->FSBFrequency);
printf("CSR (SIP) capabilities: 0x%x\t ActiveConfig: 0x%x\n", self->boot_args->csrCapabilities,self->boot_args->csrActiveConfig);
printf("APFS Data: 0x%x-0x%x\n", self->boot_args->apfsDataStart, self->boot_args->apfsDataStart + self->boot_args->apfsDataSize);
exit(0);
}
#include <mach/mach_port.h>
#include "QiLin.h"
#include <mach/kern_return.h>
#include <stdio.h>
void nullFunc() {}; // suppress debug
int main (int argc, char **argv)
{
setDebugReporter(nullFunc);
mach_port_t kernel_task;
kern_return_t host_get_special_port(task_t, int node, int which, mach_port_t *);
kern_return_t kr=host_get_special_port(mach_host_self(), 0, 4, &kernel_task);
int slide = 0 ;
FILE *ss = fopen("/tmp/slide.txt","r");
if (ss) { fscanf (ss, "0x%x", &slide); fclose(ss); }
int rc = initQiLin (kernel_task, 0xfffffff007004000 + slide);
if (rc) { fprintf(stderr,"Qilin Initialization failed!\n"); return rc;}
int spawnAndPlatformize (char *AmfidebPath, char *Arg1, char *Arg2, char *Arg3 , char *Arg4, char *Arg5);
rc = spawnAndPlatformize (argv[1], argv[2], NULL, NULL, NULL,NULL);
}
#include <CoreFoundation/CoreFoundation.h>
// Power Mgmt Stuff
// from IOKitUser-755.18.10/ps.subproj/IOPowerSources.h
CFTypeRef IOPSCopyPowerSourcesInfo(void);
CFArrayRef IOPSCopyPowerSourcesList(CFTypeRef blob);
CFDictionaryRef IOPSGetPowerSourceDescription(CFTypeRef blob, CFTypeRef ps);
void dumpDict (CFDictionaryRef Dict)
{
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)Dict);
if (xml) { write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml)); CFRelease(xml); }
}
char *getPowerDetails(int Debug)
{
CFTypeRef powerInfo;
CFArrayRef powerSourcesList;
CFDictionaryRef powerSourceInformation;
static char returned[80];
powerInfo = IOPSCopyPowerSourcesInfo();
if(! powerInfo) return ("Error: IOPsCopyPowerSourcesInfo()");
powerSourcesList = IOPSCopyPowerSourcesList(powerInfo);
if(!powerSourcesList) {
CFRelease(powerInfo);
return ("Error: IOPSCopyPowerSourcesList()");
}
typedef struct sbParams sbParams_t;
extern sbParams_t *sandbox_create_params(void);
int sandbox_set_param (sbParams_t *params, char *param, char *value);
typedef struct sbProfile {
char *name;
void *blob;
int32_t len;
} sbProfile_t;
extern sbProfile_t *sandbox_compile_file(char *filename, sbParams_t *params, char **err);
extern sbProfile_t *sandbox_compile_string(char *profile_string, sbParams_t *params, char **err);
extern sbProfile_t *sandbox_compile_entitlements(char *ents, sbParams_t *params, char **err);
extern sbProfile_t *sandbox_compile_named(char *profile_name, sbParams_t *params, char **err);
#ifdef SB459
extern int sandbox_set_trace_path (sbProfile_t *, char *Path) __attribute__((weak_import));;
extern int sandbox_vtrace_enable(void);
extern char *sandbox_vtrace_report(void);
#endif
extern void sandbox_free_profile(sbProfile_t *);
extern int sandbox_apply_container(sbProfile_t *, uint32_t);
char *operation_names[] = {
"default",
"appleevent-send",
"authorization-right-obtain",
"device*",
"device-camera",
"device-microphone",
"distributed-notification-post",
"file*",
"file-chroot",
"file-ioctl",
"file-issue-extension",
"file-map-executable",
"file-mknod",
"file-mount",
"file-read*",
"file-read-data",
"file-read-metadata",
"file-read-xattr",
"file-revoke",
"file-search",
"file-unmount",
"file-write*",
"file-write-create",
"file-write-data",
"file-write-flags",
"file-write-mode",
"file-write-owner",
"file-write-setugid",
"file-write-times",
"file-write-unlink",
"file-write-xattr",
"generic-issue-extension",
"qtn-user",
"qtn-download",
"qtn-sandbox",
"hid-control",
"iokit*",
"iokit-issue-extension",
"iokit-open",
"iokit-set-properties",
"iokit-get-properties",
"ipc*",
"ipc-posix*",
"ipc-posix-issue-extension",
"ipc-posix-sem",
"ipc-posix-shm*",
"ipc-posix-shm-read*",
"ipc-posix-shm-read-data",
"ipc-posix-shm-read-metadata",
"ipc-posix-shm-write*",
"ipc-posix-shm-write-create",
"ipc-posix-shm-write-data",
"ipc-posix-shm-write-unlink",
"ipc-sysv*",
"ipc-sysv-msg",
"ipc-sysv-sem",
"ipc-sysv-shm",
"job-creation",
"load-unsigned-code",
"lsopen",
"mach*",
"mach-bootstrap",
"mach-issue-extension",
"mach-lookup",
"mach-per-user-lookup",
"mach-priv*",
"mach-priv-host-port",
"mach-priv-task-port",
"mach-register",
"mach-task-name",
"network*",
"network-inbound",
"network-bind",
"network-outbound",
"user-preference*",
"user-preference-read",
"user-preference-write",
"process*",
"process-exec*",
"process-exec-interpreter",
"process-fork",
"process-info*",
"process-info-listpids",
"process-info-pidinfo",
"process-info-pidfdinfo",
"process-info-pidfileportinfo",
"process-info-setcontrol",
"process-info-dirtycontrol",
"process-info-rusage",
"pseudo-tty",
"signal",
"sysctl*",
"sysctl-read",
"sysctl-write",
"system*",
"system-acct",
"system-audit",
"system-chud",
"system-debug",
"system-fsctl",
"system-info",
"system-kext*",
"system-kext-load",
"system-kext-unload",
"system-lcid",
"system-mac-label",
"system-nfssvc",
"system-privilege",
"system-reboot",
"system-sched",
"system-set-time",
"system-socket",
"system-suspend-resume",
"system-swap",
"system-write-bootstrap",
NULL}; // important :-)
// The vararg definition is important because args x3 and above go on the stack
extern int sandbox_check (int Pid, char *Op, int flags, ...);
extern int sandbox_container_path_for_pid (int Pid, char *Buf, int Len);
extern int sandbox_suspend(int Pid);
extern int sandbox_unsuspend(int Pid);
// Constants figured out by reversing authd and comparing with open source of Security.framework
#define SANDBOX_FILTER_PATH 0x1
#define SANDBOX_FILTER_RIGHT_NAME 0x2
extern int SANDBOX_CHECK_NO_REPORT;
extern int __sandbox_ms(char *Label, int Op, void *ptr,...);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h> // fchmod
#include <sandbox.h>
#include "sandbox.h" // My sandbox.h
typedef int bool;
#include <dlfcn.h>
void usage(void)
{
fprintf(stderr, "Usage: sandbox-exec [options] command [args]\nOptions:\n -f profile-file Read profile from file.\n -n profile-name Use pre-defined profile.\n -p profile-string Specify profile on the command line.\n -D key=value Define a profile parameter.\n -t trace_file Trace operations to trace_file\n Exactly one of -f, -n, -p must be specified.\n");
exit(0x40);
} // usage
int debug = 0;
#define dprintf if (debug) printf
int main (int argc, char **argv)
{
debug = (getenv ("JDEBUG") != NULL);
// This is as close as possible to the decompilation of OS X's (10.11.4) sandbox-exec
// including calling sandbox_create_params() before processing arguments.
sbProfile_t *compiled_profile;
char *err = NULL;
sbParams_t *params=sandbox_create_params();
if (!params) { fprintf(stderr,"Can't create params!\n"); exit(1);}
if (argc < 2) { usage(); }
int opt;
char *profile = NULL;
int cmd = 0;
char *profileName = NULL;
char *profileString = NULL;
char *tracePath = NULL;
while ((opt = getopt(argc,argv,"D:c:de:f:n:p:t:")) > -1)
{
switch (opt)
{
case 'f': profile = optarg; break;
case 'n': profileName = optarg; break;
case 'p': profileString = optarg; break;
case 't': tracePath = optarg; break;
default: usage();
}
}
cmd = optind;
if (tracePath && profileName)
{
fprintf(stderr, "tracing isn't implemented for named profiles; use -f or -p to specify a profile\n");
exit(0x40);
}
//compiled_profile = sandbox_compile_entitlements ("no-internet", params, &err);
if (profile) compiled_profile = sandbox_compile_file (profile, params, &err);
// The built-in profiles:
// ----------------------
// kSBXProfileNoInternet (no-internet)
// kSBXProfileNoWriteExceptTemporary (no-write-except-temporary)
// kSBXProfileNoWrite (no-write)
// kSBXProfileNoNetwork (no-network)
// kSBXProfilePureComputation (pure-computation)
if (profileName) compiled_profile = sandbox_compile_named (profileName, params, &err);
if (profileString) compiled_profile = sandbox_compile_string (profileString, params, &err);
//sandbox_set_param (params, "x", "y");
if(!compiled_profile) { fprintf(stderr, "No compiled profile. Error: %s\n", err); exit(2); }
int dump= 1;
if (dump && compiled_profile->blob)
{
fprintf(stderr,"Profile: %s, Blob: %p Length: %d\n",
(compiled_profile->name? compiled_profile->name : "(custom)" ),
compiled_profile->blob, compiled_profile->len);
int fd = open("/tmp/out.bin", O_WRONLY | O_TRUNC| O_CREAT);
fchmod (fd, 0666);
write(fd, compiled_profile->blob, compiled_profile->len);
fprintf(stderr,"dumped compiled profile to /tmp/out.bin\n");
}
int flags = 0;
int rc = 0;
if (tracePath) {
#ifdef SB459
rc = sandbox_set_trace_path(compiled_profile,tracePath);
#else
void *sblibhandle = dlopen ("libsandbox.dylib", RTLD_GLOBAL);
typedef int sstp(void *, char *);
sstp * sandbox_set_trace_path = dlsym (sblibhandle, "sandbox_set_trace_path");
if (!sandbox_set_trace_path)
fprintf(stderr,"Warning: Tracing not supported in this sandbox version (can't get set_trace_path - %p)\n", sblibhandle);
else {
rc = sandbox_set_trace_path(compiled_profile,tracePath);
}
#endif
if (rc == 0) fprintf(stderr,"Tracing to %s\n", tracePath);
else fprintf(stderr,"Tracing error - Unable to trace to %s\n", tracePath);
}
fprintf(stderr,"Applying container\n");
rc = sandbox_apply_container (compiled_profile, flags);
if (rc != 0) { perror("sandbox_apply_container"); }
fflush(NULL);
if (compiled_profile) sandbox_free_profile(compiled_profile);
fprintf (stderr, "EXECING %s\n", argv[optind]);
execvp (argv[optind], argv+optind);
perror("execvp");
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <signal.h> // kill(2)
#include <string.h> // strcmp, etc.
#include "sandbox.h" // Mine
#include <dlfcn.h>
extern char **get_launchd_endpoints(int);
void *g_sblibHandle = NULL;
void usage()
{
fprintf(stderr,"Johnny's Sandbox Utility, v0.1\n");
fprintf(stderr,"Usage: %s _pid_ [what]...\n", getprogname());
fprintf(stderr,"Where: what = all: All operations (not ready yet)\n");
fprintf(stderr," mach: Check all known mach ports\n");
fprintf(stderr," file: Check file access (Requires dir or file argument)\n");
fprintf(stderr," inspect: Call syscall_inspect on this PID\n");
fprintf(stderr," vtrace: Call syscall_vtrace on this PID\n");
fprintf(stderr,"\n");
}
int checkAll (pid_t Pid)
{
int op = 0;
while (operation_names[op])
{
int denied = sandbox_check (Pid, operation_names[op], SANDBOX_CHECK_NO_REPORT, NULL);
printf("%s: %s\n", operation_names[op], denied ? "deny" : "allow");
op++;
}
return 0;
}
int main(int argc, char **argv)
{
// First open libsandbox.1.dylib. Yes, we're linked with it,
// but No, versions change significantly between 3xx and 459+
// If we can't find it, no biggy - the inspect feature will just be disabled.
char *libsb = getenv ("LIBSB");
if (!libsb) libsb = "libsandbox.1.dylib";
g_sblibHandle = dlopen (libsb, RTLD_GLOBAL);
if (argc < 2) { usage(); exit(1);}
// If first argument is a PID, go for sandbox_check
uint32_t pid = 0;
int rc = 0;
char container[1024];
char *path = "/tmp";
rc = sscanf (argv[1], "%d", &pid);
if (rc != 1)
{
fprintf(stderr,"Expecting a PID as the first argument. \"%s\" sure as heck isn't\n",
argv[1]);
usage();
exit(5);
}
char buf[4096];
// Does PID exist? a simple check
rc = kill (pid, 0);
if (rc < 0) { fprintf (stderr, "%d: No such process\n", pid); exit(2);}
// Do a quick check to see if the process is at all sandboxed
if (pid)
{
rc = sandbox_check (pid, NULL, 0);
if (rc == 0) { fprintf(stderr,"Process %d is not sandboxed. All further checks are moot (everything allowed).\n",pid);
if (strcmp(argv[2], "inspect")) exit(0);
}
// If we have a PID, maybe it is contained?
container[0] = '\0';
int len = 0x339;
uint64_t pid64 = pid;
rc = sandbox_container_path_for_pid (pid, container, 1024);
if (rc == 0) {
printf("PID %d Container: %s\n", pid, container);
}
else printf("PID %d is sandboxed, but not containerized\n",pid);
}
// If no more arguments, then we're done.
if (argc == 2) { return 0; };
if (argc == 3) {
if (strcmp(argv[2],"file") == 0)
{
fprintf(stderr,"file: options requires an argument (file or folder to check)\n");
exit(5);
}
#ifdef SB459
if (strncmp(argv[2], "vtrace", strlen(argv[2])) == 0)
{
int rc = sandbox_vtrace_enable();
fprintf(stderr,"RC: %d\n",rc);
char *rep = sandbox_vtrace_report();
fprintf(stderr,"rep: %p\n",rep);
exit(rc);
}
#endif
if (strncmp(argv[2], "inspect", strlen(argv[2])) == 0)
{
//int rc = __sandbox_ms("Sandbox",0x10, &pid2,pid2,buf, 4096);
//printf("RC: %d, Buf: %s\n", rc,buf);
typedef int sbip_func(int, char **buf, int *size);
sbip_func *sandbox_inspect_pid = dlsym(g_sblibHandle, "sandbox_inspect_pid");
if (sandbox_inspect_pid) {
int size = 0;
char *buf = NULL;
rc = sandbox_inspect_pid(pid,&buf,&size);
if (rc == 0) fprintf(stdout,"%s\n", buf);
else
fprintf(stderr,"sandbox_inspect_pid failed (RC: %d)\n", rc);
}
else { fprintf (stderr," Cant find sandbox_inspect_pid in libsandbox.\nLikely iOS version too old or too new (though you can try __sandbox_ms (0x10) directly)\n"); }
}
if (strcmp(argv[2],"mach") == 0)
{
fprintf(stderr,"Checking Mach services for %d....\n",pid);
char **services = get_launchd_endpoints(1);
int s = 0;
for (s = 0;
services[s];
s++)
{
int mach_check = sandbox_check (pid,
"mach-lookup",
SANDBOX_FILTER_RIGHT_NAME | SANDBOX_CHECK_NO_REPORT,
services[s]);
printf("%s: %s\n", services[s], mach_check ?"Nope": "Yep");
}
return 0;
}
if (strcmp(argv[2], "all") == 0) { exit(checkAll(pid)); }
path = realpath(argv[2],NULL);
}
// Check if the path exists, otherwise sandbox_check will fail (for the wrong reason)
if (strcmp(argv[2],"file") == 0)
{
path = argv[3];
if (!path) { fprintf(stderr,"Path %s doesn't exist\n", argv[2]); exit(1); }
int read_denied = sandbox_check (pid, "file-read-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, path);
int write_denied = sandbox_check (pid, "file-write-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, path);
// I like verbose messages. And tertiary operators. Even if nested
printf("The sandbox is %s restricting PID %d from reading %s writing to %s\n",
read_denied ? "" :"not",
pid,
write_denied ? (read_denied ? "and" : "but is restricting") :
(read_denied ? "but not restricting" : "or"),
path);
// For those of you wanting to script this, the return value says all
exit ( (read_denied ? 0x10 : 0) + (write_denied ? 0x20 : 0));
}
}
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
// gcc seedutil.m -o /tmp/sui -framework CoreFoundation -framework Foundation -framework Seeding -F /System/Library/PrivateFrameworks/
@interface SDBuildInfo : NSObject
+ (int)currentBuildIsSeed;
@end
@interface SDSeedProgramMigrator : NSObject
+ (void)migrateSeedProgramSettings;
+ (int)fixupSeedProgramSettings;
@end
@interface SDCatalogUtilities : NSObject
+ (void *)_currentCatalog;
@end
@interface SDSeedProgramManager : NSObject
+ enrollInSeedProgram:id;
+ unenrollFromSeedProgram;
+ (id)currentSeedProgram;
+ (NSString *)_stringForSeedProgram:id;
+ _seedProgramForString:NSString;
@end
void usage (void) {
#if 0
00000001000016bf pushq %rbp
00000001000016c0 movq %rsp, %rbp
#endif
puts ("usage: seedutil enroll SEED_PROGRAM");
#if 0
00000001000016c3 leaq 0x626(%rip), %rdi ## literal pool for: "usage: seedutil enroll SEED_PROGRAM"
00000001000016ca callq 0x100001b6e ## symbol stub for: _puts
#endif
puts (" seedutil unenroll");
#if 0
00000001000016cf leaq 0x64a(%rip), %rdi ## literal pool for: " seedutil unenroll"
00000001000016d6 callq 0x100001b6e ## symbol stub for: _puts
#endif
puts(" seedutil current");
#if 0
00000001000016db leaq 0x65e(%rip), %rdi ## literal pool for: " seedutil current"
00000001000016e2 callq 0x100001b6e ## symbol stub for: _puts
#endif
puts(" seedutil migrate OLD_VERSION NEW_VERSION");
#if 0
00000001000016e7 leaq 0x672(%rip), %rdi ## literal pool for: " seedutil migrate OLD_VERSION NEW_VERSION"
00000001000016ee callq 0x100001b6e ## symbol stub for: _puts
#endif
puts(" seedutil fixup");
#if 0
00000001000016f3 leaq 0x696(%rip), %rdi ## literal pool for: " seedutil fixup"
00000001000016fa popq %rbp
00000001000016fb jmp 0x100001b6e ## symbol stub for: _puts
#endif
}
int printProgram() {
#if 0
0000000100001700 pushq %rbp
0000000100001701 movq %rsp, %rbp
0000000100001704 pushq %r15
0000000100001706 pushq %r14
0000000100001708 pushq %r13
000000010000170a pushq %r12
000000010000170c pushq %rbx
000000010000170d pushq %rax
#endif
CFPropertyListRef sp = CFPreferencesCopyValue(CFSTR("SeedProgram"),
CFSTR("com.apple.seeding"),
kCFPreferencesAnyUser,
kCFPreferencesCurrentHost);
#if 0
000000010000170e movq 0x90b(%rip), %rax ## literal pool symbol address: _kCFPreferencesAnyUser
0000000100001715 movq (%rax), %r15
0000000100001718 movq 0x909(%rip), %rax ## literal pool symbol address: _kCFPreferencesCurrentHost
000000010000171f movq (%rax), %rbx
0000000100001722 leaq 0x95f(%rip), %rdi ## Objc cfstring ref: @"SeedProgram"
0000000100001729 leaq 0x978(%rip), %rsi ## Objc cfstring ref: @"com.apple.seeding"
0000000100001730 movq %r15, %rdx
0000000100001733 movq %rbx, %rcx
0000000100001736 callq 0x100001b3e ## symbol stub for: _CFPreferencesCopyValue
000000010000173b movq %rax, %r14
#endif
NSString *sdcat = [SDCatalogUtilities _currentCatalog];
#if 0
000000010000173e movq 0xaf3(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDCatalogUtilities
0000000100001745 movq 0xa84(%rip), %rsi ## Objc selector ref: _currentCatalog
000000010000174c callq *0x8de(%rip) ## Objc message: +[SDCatalogUtilities _currentCatalog]
0000000100001752 movq %rax, %rdi
0000000100001755 callq 0x100001b62 ## symbol stub for: _objc_retainAutoreleasedReturnValue
000000010000175a movq %rax, %r13
#endif
CFPropertyListRef nssfm = CFPreferencesCopyValue(CFSTR("NSShowFeedbackMenu"),
kCFPreferencesAnyApplication,
kCFPreferencesAnyUser,
kCFPreferencesCurrentHost);
#if 0
000000010000175d movq 0x8b4(%rip), %rax ## literal pool symbol address: _kCFPreferencesAnyApplication
0000000100001764 movq (%rax), %rsi
0000000100001767 leaq 0x95a(%rip), %rdi ## Objc cfstring ref: @"NSShowFeedbackMenu"
000000010000176e movq %r15, %rdx
0000000100001771 movq %rbx, %rcx
0000000100001774 callq 0x100001b3e ## symbol stub for: _CFPreferencesCopyValue
0000000100001779 movq %rax, %r12
#endif
CFPropertyListRef dsoo = CFPreferencesCopyValue(CFSTR("DisableSeedOptOut"),
CFSTR("com.apple.SoftwareUpdate"),
kCFPreferencesAnyUser,
kCFPreferencesCurrentHost);
#if 0
000000010000177c leaq 0x965(%rip), %rdi ## Objc cfstring ref: @"DisableSeedOptOut"
0000000100001783 leaq 0x97e(%rip), %rsi ## Objc cfstring ref: @"com.apple.SoftwareUpdate"
000000010000178a movq %r15, %rdx
000000010000178d movq %rbx, %rcx
0000000100001790 callq 0x100001b3e ## symbol stub for: _CFPreferencesCopyValue
#endif
#if 0
0000000100001795 movq %rax, %r15
0000000100001798 movq 0xa39(%rip), %rsi ## Objc selector ref: longValue
000000010000179f movq %r14, -0x30(%rbp)
00000001000017a3 movq %r14, %rdi
00000001000017a6 movq 0x883(%rip), %rbx ## Objc message: -[%rdi longValue]
00000001000017ad callq *%rbx
00000001000017af movq %rax, %rcx
#endif
printf("Program: %ld\n", [sp longValue]);
#if 0
00000001000017b2 leaq 0x483(%rip), %rdi ## literal pool for: "Program: %ld\n"
00000001000017b9 xorl %eax, %eax
00000001000017bb movq %rcx, %rsi
00000001000017be callq 0x100001b68 ## symbol stub for: _printf
#endif
int bis = [SDBuildInfo currentBuildIsSeed];
#if 0
00000001000017c3 movq 0xa76(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDBuildInfo
00000001000017ca movq 0xa0f(%rip), %rsi ## Objc selector ref: currentBuildIsSeed
00000001000017d1 callq *%rbx
00000001000017d3 movq %rbx, %r14
00000001000017d6 testb %al, %al
#endif
printf("Build is seed: %s\n", bis ? "YES" : "NO");
#if 0
00000001000017d8 leaq 0x482(%rip), %rbx ## literal pool for: "NO"
00000001000017df leaq 0x477(%rip), %rsi ## literal pool for: "YES"
00000001000017e6 cmoveq %rbx, %rsi
00000001000017ea leaq 0x459(%rip), %rdi ## literal pool for: "Build is seed: %s\n"
00000001000017f1 xorl %eax, %eax
00000001000017f3 callq 0x100001b68 ## symbol stub for: _printf
#endif
printf("CatalogURL: %s\n", [sdcat UTF8String]);
#if 0
00000001000017f8 movq %r13, %rdi
00000001000017fb callq 0x100001b5c ## symbol stub for: _objc_retainAutorelease
0000000100001800 movq %rax, %r13
0000000100001803 movq 0x9de(%rip), %rsi ## Objc selector ref: UTF8String
000000010000180a movq %rax, %rdi
000000010000180d callq *%r14
0000000100001810 movq %rax, %rcx
0000000100001813 leaq 0x44a(%rip), %rdi ## literal pool for: "CatalogURL: %s\n"
000000010000181a xorl %eax, %eax
000000010000181c movq %rcx, %rsi
000000010000181f callq 0x100001b68 ## symbol stub for: _printf
#endif
printf ("NSShowFeedbackMenu: %s\n", nssfm? "YES": "NO");
#if 0
0000000100001824 movq 0x7e5(%rip), %rax ## literal pool symbol address: _kCFBooleanTrue
000000010000182b movq (%rax), %r14
000000010000182e cmpq %r12, %r14
0000000100001831 movq %rbx, %rsi
0000000100001834 leaq 0x422(%rip), %rax ## literal pool for: "YES"
000000010000183b cmoveq %rax, %rsi
000000010000183f leaq 0x42e(%rip), %rdi ## literal pool for: "NSShowFeedbackMenu: %s\n"
0000000100001846 xorl %eax, %eax
0000000100001848 callq 0x100001b68 ## symbol stub for: _printf
#endif
printf ("DisableSeedOptOut: %s\n", dsoo? "YES": "NO");
#if 0
000000010000184d cmpq %r15, %r14
0000000100001850 leaq 0x406(%rip), %rax ## literal pool for: "YES"
0000000100001857 cmoveq %rax, %rbx
000000010000185b leaq 0x42a(%rip), %rdi ## literal pool for: "DisableSeedOptOut: %s\n"
0000000100001862 xorl %eax, %eax
0000000100001864 movq %rbx, %rsi
0000000100001867 callq 0x100001b68 ## symbol stub for: _printf
000000010000186c testq %r12, %r12
000000010000186f je 0x100001879
0000000100001871 movq %r12, %rdi
0000000100001874 callq 0x100001b44 ## symbol stub for: _CFRelease
0000000100001879 testq %r15, %r15
000000010000187c je 0x100001886
000000010000187e movq %r15, %rdi
0000000100001881 callq 0x100001b44 ## symbol stub for: _CFRelease
0000000100001886 movq 0x7ab(%rip), %rbx ## literal pool symbol address: _objc_release
000000010000188d movq %r13, %rdi
0000000100001890 callq *%rbx
0000000100001892 movq -0x30(%rbp), %rdi
0000000100001896 movq %rbx, %rax
0000000100001899 addq $0x8, %rsp
000000010000189d popq %rbx
000000010000189e popq %r12
00000001000018a0 popq %r13
00000001000018a2 popq %r14
00000001000018a4 popq %r15
00000001000018a6 popq %rbp
00000001000018a7 jmpq *%rax
#endif
return 0;
}
int main (int argc, char **argv) {
#if 0
00000001000018a9 pushq %rbp
00000001000018aa movq %rsp, %rbp
00000001000018ad pushq %r15
00000001000018af pushq %r14
00000001000018b1 pushq %r13
00000001000018b3 pushq %r12
00000001000018b5 pushq %rbx
00000001000018b6 pushq %rax
00000001000018b7 movq %rsi, %r13
00000001000018ba movl %edi, %ebx
00000001000018bc callq 0x100001b56 ## symbol stub for: _objc_autoreleasePoolPush
00000001000018c1 movq %rax, %r15
#endif
if (getuid()) {
#if 0
00000001000018c4 callq 0x100001b4a ## symbol stub for: _getuid
00000001000018c9 testl %eax, %eax
00000001000018cb je 0x1000018e0
#endif
puts ("Must be run as root");
#if 0
00000001000018cd leaq 0x57c(%rip), %rdi ## literal pool for: "Must be run as root"
00000001000018d4 callq 0x100001b6e ## symbol stub for: _puts
#endif
exit(1);
}
if (argc == 1) {
#if 0
00000001000018d9 movl $0x1, %ebx
00000001000018de jmp 0x1000018ec
#endif
usage();
return 0;
}
#if 0
00000001000018e0 cmpl $0x1, %ebx
00000001000018e3 jg 0x100001905
00000001000018e5 callq 0x1000016bf
00000001000018ea xorl %ebx, %ebx
00000001000018ec movq %r15, %rdi
00000001000018ef callq 0x100001b50 ## symbol stub for: _objc_autoreleasePoolPop
00000001000018f4 movl %ebx, %eax
00000001000018f6 addq $0x8, %rsp
00000001000018fa popq %rbx
00000001000018fb popq %r12
00000001000018fd popq %r13
00000001000018ff popq %r14
0000000100001901 popq %r15
0000000100001903 popq %rbp
0000000100001904 retq
#endif
NSString *arg = [NSString stringWithUTF8String:argv[1] ];
#if 0
0000000100001905 movq 0x93c(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_NSString
000000010000190c movq 0x8(%r13), %rdx
0000000100001910 movq 0x8d9(%rip), %rsi ## Objc selector ref: stringWithUTF8String:
0000000100001917 movq 0x712(%rip), %r14 ## Objc message: +[NSString stringWithUTF8String:]
000000010000191e callq *%r14
#endif
#if 0
0000000100001921 movq %rax, %rdi
0000000100001924 callq 0x100001b62 ## symbol stub for: _objc_retainAutoreleasedReturnValue
#endif
if ([arg isEqualToString:@"enroll"])
{
#if 0
0000000100001929 movq %rax, %r12
000000010000192c movq 0x8c5(%rip), %rsi ## Objc selector ref: isEqualToString:
0000000100001933 leaq 0x7ee(%rip), %rdx ## Objc cfstring ref: @"enroll"
000000010000193a movq %rax, %rdi
000000010000193d callq *%r14
0000000100001940 testb %al, %al
0000000100001942 je 0x100001953
#endif
if (argc > 2) {
printf("Enrolling...\n");
#if 0
000000010000199d leaq 0x49d(%rip), %rdi ## literal pool for: "Enrolling...\n"
00000001000019a4 callq 0x100001b6e ## symbol stub for: _puts
#endif
[SDSeedProgramManager
enrollInSeedProgram: [SDSeedProgramManager _seedProgramForString:[NSString stringWithUTF8String:argv[1]]]];
#if 0
00000001000019a9 movq 0x8a0(%rip), %r14 ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramManager
00000001000019b0 movq 0x891(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_NSString
00000001000019b7 movq 0x10(%r13), %rdx
00000001000019bb movq 0x82e(%rip), %rsi ## Objc selector ref: stringWithUTF8String:
00000001000019c2 movq 0x667(%rip), %rax ## Objc message: +[NSString stringWithUTF8String:]
00000001000019c9 movq %rax, %r13
00000001000019cc callq *%rax
00000001000019ce movq %rax, %rdi
00000001000019d1 callq 0x100001b62 ## symbol stub for: _objc_retainAutoreleasedReturnValue
00000001000019d6 movq %rax, %rbx
00000001000019d9 movq 0x820(%rip), %rsi ## Objc selector ref: _seedProgramForString:
00000001000019e0 movq %r14, %rdi
00000001000019e3 movq %rax, %rdx
00000001000019e6 callq *%r13
00000001000019e9 movq %rax, %r14
00000001000019ec movq %rbx, %rdi
00000001000019ef callq *0x643(%rip) ## literal pool symbol address: _objc_release
00000001000019f5 movq 0x854(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramManager
00000001000019fc movq 0x805(%rip), %rsi ## Objc selector ref: enrollInSeedProgram:
0000000100001a03 movq %r14, %rdx
0000000100001a06 callq *%r13
0000000100001a09 callq 0x100001700
0000000100001a0e movq %r12, %rbx
0000000100001a11 jmp 0x100001ad6
#endif
}
else { usage(); exit(1);}
#if 0
0000000100001944 cmpl $0x2, %ebx
0000000100001947 jg 0x10000199d
0000000100001949 callq 0x1000016bf
000000010000194e jmp 0x100001a0e
#endif
}
if ([arg isEqualToString:@"unenroll"]) {
#if 0
0000000100001953 leaq 0x7ee(%rip), %rdx ## Objc cfstring ref: @"unenroll"
000000010000195a movq %r12, %rdi
000000010000195d movq 0x894(%rip), %r14 ## Objc selector ref: isEqualToString:
0000000100001964 movq %r14, %rsi
0000000100001967 callq *0x6c3(%rip) ## Objc message: -[%rdi isEqualToString:]
000000010000196d testb %al, %al
000000010000196f movq %r12, %rbx
0000000100001972 je 0x100001a16
#endif
puts ("Unenrolling...\n");
#if 0
0000000100001978 leaq 0x4b2(%rip), %rdi ## literal pool for: "Unenrolling...\n"
000000010000197f callq 0x100001b6e ## symbol stub for: _puts
#endif
[SDSeedProgramManager unenrollFromSeedProgram];
#if 0
0000000100001984 movq 0x8c5(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramManager
000000010000198b movq 0x87e(%rip), %rsi ## Objc selector ref: unenrollFromSeedProgram
0000000100001992 callq *0x698(%rip) ## Objc message: +[SDSeedProgramManager unenrollFromSeedProgram]
0000000100001998 jmp 0x100001ad1
#endif
}
else if ([arg isEqualToString:@"current"])
#if 0
0000000100001a16 leaq 0x74b(%rip), %rdx ## Objc cfstring ref: @"current"
0000000100001a1d movq %rbx, %rdi
0000000100001a20 movq %r14, %rsi
0000000100001a23 callq *0x607(%rip) ## Objc message: +[SDSeedProgramManager enrollInSeedProgram:]
0000000100001a29 testb %al, %al
0000000100001a2b je 0x100001a9a
#endif
{
void *csp = [SDSeedProgramManager currentSeedProgram];
#if 0
0000000100001a2d movq 0x81c(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramManager
0000000100001a34 movq 0x7dd(%rip), %rsi ## Objc selector ref: currentSeedProgram
0000000100001a3b movq 0x5ee(%rip), %rax ## Objc message: +[SDSeedProgramManager currentSeedProgram]
0000000100001a42 movq %rax, %r12
0000000100001a45 callq *%rax
#endif
NSString *str = [SDSeedProgramManager _stringForSeedProgram:csp];
#if 0
0000000100001a47 movq 0x802(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramManager
0000000100001a4e movq 0x7cb(%rip), %rsi ## Objc selector ref: _stringForSeedProgram:
0000000100001a55 movq %rax, %rdx
0000000100001a58 callq *%r12
0000000100001a5b movq %rax, %rdi
0000000100001a5e callq 0x100001b62 ## symbol stub for: _objc_retainAutoreleasedReturnValue
0000000100001a63 movq %rax, %rdi
0000000100001a66 callq 0x100001b5c ## symbol stub for: _objc_retainAutorelease
#endif
printf("Currently enrolled in: %s\n\n",
[str UTF8String]);
#if 0
0000000100001a6b movq %rax, %r14
0000000100001a6e movq 0x773(%rip), %rsi ## Objc selector ref: UTF8String
0000000100001a75 movq %rax, %rdi
0000000100001a78 callq *%r12
0000000100001a7b movq %rax, %rcx
0000000100001a7e leaq 0x236(%rip), %rdi ## literal pool for: "Currently enrolled in: %s\n\n"
0000000100001a85 xorl %eax, %eax
0000000100001a87 movq %rcx, %rsi
0000000100001a8a callq 0x100001b68 ## symbol stub for: _printf
0000000100001a8f movq %r14, %rdi
0000000100001a92 callq *0x5a0(%rip) ## literal pool symbol address: _objc_release
0000000100001a98 jmp 0x100001ad1
#endif
printProgram ();
}
// @migrate
if ([arg isEqualToString:@"migrate"])
{
#if 0
0000000100001a9a leaq 0x6e7(%rip), %rdx ## Objc cfstring ref: @"migrate"
0000000100001aa1 movq %rbx, %rdi
0000000100001aa4 movq %r14, %rsi
0000000100001aa7 callq *0x583(%rip) ## Objc message: +[SDSeedProgramManager UTF8String]
0000000100001aad testb %al, %al
0000000100001aaf je 0x100001aee
#endif
printf("Migrating seed program settings\n\n");
#if 0
0000000100001ab1 leaq 0x358(%rip), %rdi ## literal pool for: "Migrating seed program settings\n"
0000000100001ab8 callq 0x100001b6e ## symbol stub for: _puts
#endif
[SDSeedProgramMigrator migrateSeedProgramSettings];
#if 0
0000000100001abd movq 0x794(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramMigrator
0000000100001ac4 movq 0x75d(%rip), %rsi ## Objc selector ref: migrateSeedProgramSettings
0000000100001acb callq *0x55f(%rip) ## Objc message: +[SDSeedProgramMigrator migrateSeedProgramSettings]
#endif
printProgram ();
}
#if 0
0000000100001ad1 callq 0x100001700
0000000100001ad6 movq %rbx, %rdi
0000000100001ad9 callq *0x559(%rip) ## literal pool symbol address: _objc_release
0000000100001adf movq %r15, %rdi
0000000100001ae2 callq 0x100001b50 ## symbol stub for: _objc_autoreleasePoolPop
0000000100001ae7 xorl %ebx, %ebx
0000000100001ae9 jmp 0x1000018f4
#endif
else if ([arg isEqualToString:@"fixup"])
#if 0
0000000100001aee leaq 0x6b3(%rip), %rdx ## Objc cfstring ref: @"fixup"
0000000100001af5 movq %rbx, %rdi
0000000100001af8 movq %r14, %rsi
0000000100001afb callq *0x52f(%rip) ## Objc message: -[%rdi migrateSeedProgramSettings]
0000000100001b01 testb %al, %al
0000000100001b03 je 0x100001b37
#endif
{
printf("Fixing up seed program settings\n");
#if 0
0000000100001b05 leaq 0x2a4(%rip), %rdi ## literal pool for: "Fixing up seed program settings\n"
0000000100001b0c callq 0x100001b6e ## symbol stub for: _puts
#endif
if (![SDSeedProgramMigrator fixupSeedProgramSettings])
#if 0
0000000100001b11 movq 0x740(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_SDSeedProgramMigrator
0000000100001b18 movq 0x711(%rip), %rsi ## Objc selector ref: fixupSeedProgramSettings
0000000100001b1f callq *0x50b(%rip) ## Objc message: +[SDSeedProgramMigrator fixupSeedProgramSettings]
0000000100001b25 testb %al, %al
0000000100001b27 jne 0x100001ad1
#endif
printf("No seed program was set; did nothing\n");
#if 0
0000000100001b29 leaq 0x2b0(%rip), %rdi ## literal pool for: "No seed program was set; did nothing"
0000000100001b30 callq 0x100001b6e ## symbol stub for: _puts
0000000100001b35 jmp 0x100001ad6
0000000100001b37 callq 0x1000016bf
0000000100001b3c jmp 0x100001ad6
#endif
}
return 0;
}
#include <mach/mach_port.h>
#include "QiLin.h"
#include <mach/kern_return.h>
#include <stdio.h>
void doNothing(void ) {};
int main (int argc, char **argv)
{
void setDebugReporter (status_func *Func);
setDebugReporter(doNothing);
mach_port_t kernel_task;
kern_return_t host_get_special_port(task_t, int node, int which, mach_port_t *);
kern_return_t kr=host_get_special_port(mach_host_self(), 0, 4, &kernel_task);
if (argc < 2) {
fprintf(stderr,"Usage: shaihulud _cmd_ [_args_]\n");
fprintf(stderr,"Bestow the might of ShaiHulud (Sandbox escape + kernel credentials) on command\n");
exit(1);
}
int slide = 0 ;
FILE *ss = fopen("/tmp/slide.txt","r");
if (ss) { fscanf (ss, "0x%x", &slide); fclose(ss); }
int rc = initQiLin (kernel_task, 0xfffffff007004000 + slide);
if (rc) { fprintf(stderr,"Qilin Initialization failed!\n"); return rc;}
int spawnAndShaiHulud (char *AmfidebPath, char *Arg1, char *Arg2, char *Arg3 , char *Arg4, char *Arg5);
rc = spawnAndShaiHulud (argv[1], argv[2], NULL, NULL, NULL,NULL);
}
/*
* Copyright (c) 1998-2009 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef _APPLESMBIOS_SMBIOS_H
#define _APPLESMBIOS_SMBIOS_H
//#include <IOKit/IOLib.h>
//
// Based on System Management BIOS Reference Specification v2.5
//
typedef UInt8 SMBString;
typedef UInt8 SMBByte;
typedef UInt16 SMBWord;
typedef UInt32 SMBDWord;
typedef UInt64 SMBQWord;
#pragma pack(push, 1) // enable 8-bit struct packing
struct DMIEntryPoint {
SMBByte anchor[5];
SMBByte checksum;
SMBWord tableLength;
SMBDWord tableAddress;
SMBWord structureCount;
SMBByte bcdRevision;
};
struct SMBEntryPoint {
SMBByte anchor[4];
SMBByte checksum;
SMBByte entryPointLength;
SMBByte majorVersion;
SMBByte minorVersion;
SMBWord maxStructureSize;
SMBByte entryPointRevision;
SMBByte formattedArea[5];
struct DMIEntryPoint dmi;
};
//
// Header common to all SMBIOS structures
//
struct SMBStructHeader {
SMBByte type;
SMBByte length;
SMBWord handle;
};
#define SMB_STRUCT_HEADER struct SMBStructHeader header;
struct SMBAnchor
{
const struct SMBStructHeader * header;
const UInt8 * next;
const UInt8 * end;
};
#define SMB_ANCHOR_IS_VALID(x) \
((x) && ((x)->header) && ((x)->next) && ((x)->end))
#define SMB_ANCHOR_RESET(x) \
bzero(x, sizeof(struct SMBAnchor));
//
// SMBIOS structure types.
//
enum {
kSMBTypeBIOSInformation = 0,
kSMBTypeSystemInformation = 1,
kSMBTypeBaseBoard = 2,
kSMBTypeSystemEnclosure = 3,
kSMBTypeProcessorInformation = 4,
kSMBTypeMemoryModule = 6,
kSMBTypeCacheInformation = 7,
kSMBTypeSystemSlot = 9,
kSMBTypePhysicalMemoryArray = 16,
kSMBTypeMemoryDevice = 17,
kSMBType32BitMemoryErrorInfo = 18,
kSMBType64BitMemoryErrorInfo = 33,
#ifndef NOJ
// J's additions
jSMBTypeGroupAssociations = 14,
jSMBTypeMemoryArrayMappedAddress = 19,
jSMBTypePortableBatteryInfo = 22,
jSMBTypeOnboardDevicesExtendedInfo = 41,
jSMBTypeSystemBootInfo = 32,
jSMBTypeIMIDeviceInfo = 38,
jSMBTypeManagementDeviceComponentInfo = 35,
jSMBTypeManagementDeviceThresholdData = 36,
//
#endif
/* Apple Specific Structures */
kSMBTypeFirmwareVolume = 128,
kSMBTypeMemorySPD = 130,
kSMBTypeOemProcessorType = 131,
kSMBTypeOemProcessorBusSpeed = 132
};
//
// BIOS Information (Type 0)
//
struct SMBBIOSInformation {
SMB_STRUCT_HEADER // Type 0
SMBString vendor; // BIOS vendor name
SMBString version; // BIOS version
SMBWord startSegment; // BIOS segment start
SMBString releaseDate; // BIOS release date
SMBByte romSize; // (n); 64K * (n+1) bytes
SMBQWord characteristics; // supported BIOS functions
};
//
// System Information (Type 1)
//
struct SMBSystemInformation {
// 2.0+ spec (8 bytes)
SMB_STRUCT_HEADER // Type 1
SMBString manufacturer;
SMBString productName;
SMBString version;
SMBString serialNumber;
// 2.1+ spec (25 bytes)
SMBByte uuid[16]; // can be all 0 or all 1's
SMBByte wakeupReason; // reason for system wakeup
};
//
// Base Board (Type 2)
//
struct SMBBaseBoard {
SMB_STRUCT_HEADER // Type 2
SMBString manufacturer;
SMBString product;
SMBString version;
SMBString serialNumber;
SMBString assetTagNumber;
SMBByte featureFlags;
SMBString locationInChassis;
SMBWord chassisHandle;
SMBByte boardType;
SMBByte numberOfContainedHandles;
// 0 - 255 contained handles go here but we do not include
// them in our structure. Be careful to use numberOfContainedHandles
// times sizeof(SMBWord) when computing the actual record size,
// if you need it.
};
// Values for boardType in Type 2 records
enum {
kSMBBaseBoardUnknown = 0x01,
kSMBBaseBoardOther = 0x02,
kSMBBaseBoardServerBlade = 0x03,
kSMBBaseBoardConnectivitySwitch = 0x04,
kSMBBaseBoardSystemMgmtModule = 0x05,
kSMBBaseBoardProcessorModule = 0x06,
kSMBBaseBoardIOModule = 0x07,
kSMBBaseBoardMemoryModule = 0x08,
kSMBBaseBoardDaughter = 0x09,
kSMBBaseBoardMotherboard = 0x0A,
kSMBBaseBoardProcessorMemoryModule = 0x0B,
kSMBBaseBoardProcessorIOModule = 0x0C,
kSMBBaseBoardInterconnect = 0x0D,
};
//
// System Enclosure (Type 3)
//
struct SMBSystemEnclosure {
SMB_STRUCT_HEADER // Type 3
SMBString manufacturer;
SMBByte type;
SMBString version;
SMBString serialNumber;
SMBString assetTagNumber;
SMBByte bootupState;
SMBByte powerSupplyState;
SMBByte thermalState;
SMBByte securityStatus;
SMBDWord oemDefined;
};
//
// Processor Information (Type 4)
//
struct SMBProcessorInformation {
// 2.0+ spec (26 bytes)
SMB_STRUCT_HEADER // Type 4
SMBString socketDesignation;
SMBByte processorType; // CPU = 3
SMBByte processorFamily; // processor family enum
SMBString manufacturer;
SMBQWord processorID; // based on CPUID
SMBString processorVersion;
SMBByte voltage; // bit7 cleared indicate legacy mode
SMBWord externalClock; // external clock in MHz
SMBWord maximumClock; // max internal clock in MHz
SMBWord currentClock; // current internal clock in MHz
SMBByte status;
SMBByte processorUpgrade; // processor upgrade enum
// 2.1+ spec (32 bytes)
SMBWord L1CacheHandle;
SMBWord L2CacheHandle;
SMBWord L3CacheHandle;
// 2.3+ spec (35 bytes)
SMBString serialNumber;
SMBString assetTag;
SMBString partNumber;
};
#define kSMBProcessorInformationMinSize 26
//
// Memory Module Information (Type 6)
// Obsoleted since SMBIOS version 2.1
//
struct SMBMemoryModule {
SMB_STRUCT_HEADER // Type 6
SMBString socketDesignation;
SMBByte bankConnections;
SMBByte currentSpeed;
SMBWord currentMemoryType;
SMBByte installedSize;
SMBByte enabledSize;
SMBByte errorStatus;
};
#define kSMBMemoryModuleSizeNotDeterminable 0x7D
#define kSMBMemoryModuleSizeNotEnabled 0x7E
#define kSMBMemoryModuleSizeNotInstalled 0x7F
//
// Cache Information (Type 7)
//
struct SMBCacheInformation {
SMB_STRUCT_HEADER // Type 7
SMBString socketDesignation;
SMBWord cacheConfiguration;
SMBWord maximumCacheSize;
SMBWord installedSize;
SMBWord supportedSRAMType;
SMBWord currentSRAMType;
SMBByte cacheSpeed;
SMBByte errorCorrectionType;
SMBByte systemCacheType;
SMBByte associativity;
};
struct SMBSystemSlot {
// 2.0+ spec (12 bytes)
SMB_STRUCT_HEADER // Type 9
SMBString slotDesignation;
SMBByte slotType;
SMBByte slotDataBusWidth;
SMBByte currentUsage;
SMBByte slotLength;
SMBWord slotID;
SMBByte slotCharacteristics1;
// 2.1+ spec (13 bytes)
SMBByte slotCharacteristics2;
};
//
// Physical Memory Array (Type 16)
//
struct SMBPhysicalMemoryArray {
// 2.1+ spec (15 bytes)
SMB_STRUCT_HEADER // Type 16
SMBByte physicalLocation; // physical location
SMBByte arrayUse; // the use for the memory array
SMBByte errorCorrection; // error correction/detection method
SMBDWord maximumCapacity; // maximum memory capacity in kilobytes
SMBWord errorHandle; // handle of a previously detected error
SMBWord numMemoryDevices; // number of memory slots or sockets
};
// Memory Array - Use
enum {
kSMBMemoryArrayUseOther = 0x01,
kSMBMemoryArrayUseUnknown = 0x02,
kSMBMemoryArrayUseSystemMemory = 0x03,
kSMBMemoryArrayUseVideoMemory = 0x04,
kSMBMemoryArrayUseFlashMemory = 0x05,
kSMBMemoryArrayUseNonVolatileMemory = 0x06,
kSMBMemoryArrayUseCacheMemory = 0x07
};
// Memory Array - Error Correction Types
enum {
kSMBMemoryArrayErrorCorrectionTypeOther = 0x01,
kSMBMemoryArrayErrorCorrectionTypeUnknown = 0x02,
kSMBMemoryArrayErrorCorrectionTypeNone = 0x03,
kSMBMemoryArrayErrorCorrectionTypeParity = 0x04,
kSMBMemoryArrayErrorCorrectionTypeSingleBitECC = 0x05,
kSMBMemoryArrayErrorCorrectionTypeMultiBitECC = 0x06,
kSMBMemoryArrayErrorCorrectionTypeCRC = 0x07
};
//
// Memory Device (Type 17)
//
struct SMBMemoryDevice {
// 2.1+ spec (21 bytes)
SMB_STRUCT_HEADER // Type 17
SMBWord arrayHandle; // handle of the parent memory array
SMBWord errorHandle; // handle of a previously detected error
SMBWord totalWidth; // total width in bits; including ECC bits
SMBWord dataWidth; // data width in bits
SMBWord memorySize; // bit15 is scale, 0 = MB, 1 = KB
SMBByte formFactor; // memory device form factor
SMBByte deviceSet; // parent set of identical memory devices
SMBString deviceLocator; // labeled socket; e.g. "SIMM 3"
SMBString bankLocator; // labeled bank; e.g. "Bank 0" or "A"
SMBByte memoryType; // type of memory
SMBWord memoryTypeDetail; // additional detail on memory type
// 2.3+ spec (27 bytes)
SMBWord memorySpeed; // speed of device in MHz (0 for unknown)
SMBString manufacturer;
SMBString serialNumber;
SMBString assetTag;
SMBString partNumber;
};
//
// Firmware Volume Description (Apple Specific - Type 128)
//
enum {
FW_REGION_RESERVED = 0,
FW_REGION_RECOVERY = 1,
FW_REGION_MAIN = 2,
FW_REGION_NVRAM = 3,
FW_REGION_CONFIG = 4,
FW_REGION_DIAGVAULT = 5,
NUM_FLASHMAP_ENTRIES = 8
};
struct FW_REGION_INFO
{
SMBDWord StartAddress;
SMBDWord EndAddress;
};
struct SMBFirmwareVolume {
SMB_STRUCT_HEADER // Type 128
SMBByte RegionCount;
SMBByte Reserved[3];
SMBDWord FirmwareFeatures;
SMBDWord FirmwareFeaturesMask;
SMBByte RegionType[ NUM_FLASHMAP_ENTRIES ];
struct FW_REGION_INFO FlashMap[ NUM_FLASHMAP_ENTRIES ];
};
//
// Memory SPD Data (Apple Specific - Type 130)
//
struct SMBMemorySPD {
SMB_STRUCT_HEADER // Type 130
SMBWord Type17Handle;
SMBWord Offset;
SMBWord Size;
SMBWord Data[];
};
static const char *
SMBMemoryDeviceTypes[] =
{
"RAM", /* 00h Undefined */
"RAM", /* 01h Other */
"RAM", /* 02h Unknown */
"DRAM", /* 03h DRAM */
"EDRAM", /* 04h EDRAM */
"VRAM", /* 05h VRAM */
"SRAM", /* 06h SRAM */
"RAM", /* 07h RAM */
"ROM", /* 08h ROM */
"FLASH", /* 09h FLASH */
"EEPROM", /* 0Ah EEPROM */
"FEPROM", /* 0Bh FEPROM */
"EPROM", /* 0Ch EPROM */
"CDRAM", /* 0Dh CDRAM */
"3DRAM", /* 0Eh 3DRAM */
"SDRAM", /* 0Fh SDRAM */
"SGRAM", /* 10h SGRAM */
"RDRAM", /* 11h RDRAM */
"DDR SDRAM", /* 12h DDR */
"DDR2 SDRAM", /* 13h DDR2 */
"DDR2 FB-DIMM", /* 14h DDR2 FB-DIMM */
"RAM", /* 15h unused */
"RAM", /* 16h unused */
"RAM", /* 17h unused */
"DDR3", /* 18h DDR3, chosen in [5776134] */
};
static const int
kSMBMemoryDeviceTypeCount = sizeof(SMBMemoryDeviceTypes) /
sizeof(SMBMemoryDeviceTypes[0]);
//
// OEM Processor Type (Apple Specific - Type 131)
//
struct SMBOemProcessorType {
SMB_STRUCT_HEADER
SMBWord ProcessorType;
};
//
// OEM Processor Bus Speed (Apple Specific - Type 132)
//
struct SMBOemProcessorBusSpeed {
SMB_STRUCT_HEADER
SMBWord ProcessorBusSpeed; // MT/s unit
};
// Begin J's additions, as per DMTF standard
#ifndef NOJ
struct SMBPortableBatteryInformation {
SMB_STRUCT_HEADER // Type 22
SMBByte location;
SMBByte manufacturer;
SMBByte manufactureDate;
SMBByte serialNumber;
SMBByte deviceName;
SMBByte deviceChemistry;
SMBWord designCapacity;
SMBWord designVoltage;
};
struct SMBOnboardDevicesExtendedInformation {
SMB_STRUCT_HEADER
SMBByte referenceDesignation;
SMBByte deviceType;
SMBByte deviceTypeInstance;
SMBWord SegmentGroupNumber;
SMBByte busNumber;
SMBByte bitField;
};
struct SMBPortConnectorInformation {
SMB_STRUCT_HEADER // Type 8
SMBString InternalReferenceDesignator;
SMBByte InternalConnectorType;
SMBString ExternalReferenceDesignator;
SMBByte ExternalConnectorType;
SMBByte PortType;
SMBWord Data[];
};
struct SMBSystemBootInformation {
SMB_STRUCT_HEADER // Type 22
SMBByte Reserved[6];
SMBByte BootStatus[]; // length -10
};
struct SMBMemoryArrayMappedAddress { // Type 19
SMB_STRUCT_HEADER
SMBDWord startingAddress;
SMBDWord endingAddress;
SMBWord memoryArrayHandle;
SMBByte partitionWidth;
SMBQWord extendedStartingAddress;
SMBQWord extendedEndingAddress;
};
struct SMBSMCVersion { // type 134
SMB_STRUCT_HEADER
SMBByte version[];
};
#endif
#pragma pack(pop) // reset to default struct packing
#endif /* !_APPLESMBIOS_SMBIOS_H */
/*
* SM(BIOS) UTility
*
* One of the source code examples from MOXiI 2, Volume II
*
* Dumps the AppleSMBIOS property from the IORegistry in a nice and
* more human readable format.
*
*
* To compile:
* gcc smut.c -o bios -framework IOKit -framework CoreFoundation
*
* (will compile <del>cleanly</del> just fine, maybe a warning or two.. I promise)
*
* License: Free to use. But don't forget to give credit where due if you
* found this useful.
*
* (This could actually work on any Intel device with SMBIOS - feel free
* to port this to Linux, if there isn't have something for that already)
*
*/
#include <stdio.h>
#include <mach/mach.h>
typedef unsigned char UInt8 ;
typedef unsigned short UInt16 ;
typedef unsigned int UInt32 ;
typedef unsigned long long UInt64 ;
const char ver[] =
{"@(#) PROGRAM: smut 1.0 \tPROJECT: Jtools"}; // for what(1)
#include "SMBIOS.h" // updated from Apple's SMBIOS project (q.v. http://newosxbook.com/code/listings/SMBIOS.h)
#define IOKIT // to unlock device/device_types..
#include <device/device_types.h> // for io_name, io_string
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
// from IOKit/IOKitLib.h
extern const mach_port_t kIOMasterPortDefault;
// from IOKit/IOTypes.h
//typedef mach_port_t io_object_t;
typedef io_object_t io_connect_t;
typedef io_object_t io_enumerator_t;
typedef io_object_t io_iterator_t;
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
kern_return_t
IOServiceGetMatchingServices(
mach_port_t masterPort,
CFDictionaryRef matching,
io_iterator_t * existing );
CFMutableDictionaryRef
IOServiceMatching(
const char * name );
// typedef unsigned long IOOptionBits;
kern_return_t
IORegistryEntryCreateCFProperties(
io_registry_entry_t entry,
CFMutableDictionaryRef * properties,
CFAllocatorRef allocator,
IOOptionBits options );
CFTypeRef
IORegistryEntryCreateCFProperty(
io_registry_entry_t entry,
CFStringRef key,
CFAllocatorRef allocator,
IOOptionBits options );
// Globals go here:
int g_verbose = 0;
#define vprintf if (g_verbose) printf
int hexDump (unsigned char *Buf, int Len)
{
int i = 0 ;
fprintf (stdout, "0x%04X: ", 0);
for (i = 0 ; i < Len; i ++) {
if (i && (i % 8 == 0)) {
fprintf(stdout,"\t%c%c%c%c%c%c%c%c\n",
isprint(Buf[i-8]) ? Buf[i-8] : '.',
isprint(Buf[i-7]) ? Buf[i-7] : '.',
isprint(Buf[i-6]) ? Buf[i-6] : '.',
isprint(Buf[i-5]) ? Buf[i-5] : '.',
isprint(Buf[i-4]) ? Buf[i-4] : '.',
isprint(Buf[i-3]) ? Buf[i-3] : '.',
isprint(Buf[i-2]) ? Buf[i-2] : '.',
isprint(Buf[i-1]) ? Buf[i-1] : '.');
fprintf (stdout, "0x%4X: ", i);
}
fprintf(stdout, "0x%02X ", (unsigned char) Buf[i]);
}
fprintf(stdout,"\n");
return 0;
} // hexDump
CFDataRef getProp(io_service_t Service, char *IOPropertyName)
{
CFMutableDictionaryRef propertiesDict;
if (IOPropertyName)
{
CFStringRef cfStrPropertyName = CFStringCreateWithCString (kCFAllocatorDefault, // CFAllocatorRef alloc,
IOPropertyName, // const char *cStr,
kCFStringEncodingASCII); // CFStringEncoding encoding );
CFTypeRef propertyRef = IORegistryEntryCreateCFProperty(Service, // io_registry_entry_t entry,
cfStrPropertyName, // CFStringRef key,
kCFAllocatorDefault, // CFAllocatorRef allocator,
0); // IOOptionBits options
if (!propertyRef)
{
printf("Property %s not found\n", IOPropertyName);
return NULL;
}
if (CFGetTypeID(propertyRef) == CFStringGetTypeID()) {
int len = CFStringGetLength(propertyRef);
printf("LEN IS %d\n", len);
const char* str = CFStringGetCStringPtr(propertyRef, kCFStringEncodingASCII);
int i = 0;
for (i = 0 ; i < len; i ++) putchar (str[i]);
printf("\n");
}
if (CFGetTypeID(propertyRef) == CFDataGetTypeID()) {
return (propertyRef);
}
}
else {
kern_return_t kr = IORegistryEntryCreateCFProperties( Service,
&propertiesDict,
kCFAllocatorDefault,
kNilOptions );
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)propertiesDict);
if (xml) {
write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml));
CFRelease(xml);
}
} // end else
return 0;
}
char *getSMBString (unsigned char *SMBElement,
unsigned int stringNum)
{
struct SMBStructHeader *sh = (struct SMBStructHeader *) SMBElement;
int s = 1;
char *theString = (char *)(SMBElement + sh->length);
int off = 0 ;
while (s < stringNum)
{
while (theString[0]) { theString++;}
// Now points to NULL, so advance.
theString++;
s++;;
}
if (theString[0]) {return (theString);} // If this is the n-th string
else return( "");
}
const char *bootStatusToText (int Use) {
switch (Use){
case 0: return "no errors detected";
case 1: return "no bootable media";
case 2: return "normal OS failed to load";
case 3: return "firmware detected failure";
case 4: return "OS detected failure";
case 5: return "User requested boot";
case 6: return "system security violation";
case 7: return "previously requested image";
case 8: return "watchdog timer expiration";
default: return "unknown/reserved";
}
} // bootStatusToText
const char *memLocToText (int Use) {
switch (Use){
case 1: return "other";
case 2: return "unnknown";
case 3: return "system board/motherboard";
case 4: return "ISA add-on";
case 5: return "EISA add-on";
case 6: return "PCI add-on";
case 7: return "MCA add-on";
case 8: return "PCMCIA add-on";
case 9: return "proprietary add-on";
default: return "?!";
}
} // memLoc
const char *memUseToText (int Use) {
switch (Use){
case 1: return "other";
case 2: return "unnknown";
case 3: return "system memory";
case 4: return "video memory";
case 5: return "flash memory";
case 6: return "NVRAM";
case 7: return "cache memory";
default: return "?!";
}
} // memUs
const char *enclosureTypeToText (int Type){
switch (Type)
{
case 1: return "other";
case 2: return "unnknown";
case 3: return "dekstop";
case 4: return "low profile desktop";
case 5: return "pizza box";
case 6: return "mini tower";
case 7: return "tower";
case 8: return "portable";
case 9: return "laptop";
case 10: return "notebook";
case 11: return "handheld";
case 12: return "docking station";
case 13: return "all in one";
case 14: return "sub notebook";
default: return "Too many codes - J hasn't filled all";
} //
} // deviceTypeToText
const char *deviceTypeToText (int Type){
switch (Type)
{
case 1: return "other";
case 2: return "unnknown";
case 3: return "video";
case 4: return "SCSI";
case 5: return "Ethernet";
case 6: return "Token Ring...";
case 7: return "Sound";
case 8: return "PATA";
case 9: return "SATA";
case 10: return "SAS";
default: return "?!";
} //
} // deviceTypeToText
ssize_t parseSMBIOSStruct(unsigned char *Bytes, int Offset, int Len)
{
// An SMBIOSStruct starts with a header, defining its type and length:
// struct SMBStructHeader {
// SMBByte type;
// SMBByte length;
// SMBWord handle;
// };
struct SMBStructHeader *ssh = (struct SMBStructHeader *)(Bytes +Offset);
vprintf("%d: Header for type %d, Length %u bytes, handle %hd\n",
Offset,
Bytes[Offset],
Bytes[Offset+1],
(*(SMBWord *) (Bytes + Offset + 2)));
// Advancing however many bytes are specified in the length field will
// bring us to the beginning of the string set - or the first of the
// double-NULL byte terminator
unsigned char *current = Bytes + Offset;
SMBByte len = ssh->length; //current[1]; // Can also get this via struct.
printf("0x%x:", Offset);
switch (Bytes[Offset]) // which is the type
{
case kSMBTypeBIOSInformation: // = 0,
{
#if 0
struct SMBBIOSInformation {
SMB_STRUCT_HEADER // Type 0
SMBString vendor; // BIOS vendor name
SMBString version; // BIOS version
SMBWord startSegment; // BIOS segment start
SMBString releaseDate; // BIOS release date
SMBByte romSize; // (n); 64K * (n+1) bytes
SMBQWord characteristics; // supported BIOS functions
};
#endif
printf("BIOS Information:\n");
struct SMBBIOSInformation *bi = (struct SMBBIOSInformation *) current;
printf("\tVendor: %s\n", getSMBString((void *) current, bi->vendor));
printf("\tVersion: %s\n", getSMBString((void *) current, bi->version));
printf("\tRelease Date: %s\n", getSMBString((void *) current, bi->releaseDate));
}
break;
case kSMBTypeSystemInformation: // = 1,
printf("System Information:\n");
struct SMBSystemInformation *si = (struct SMBSystemInformation *) current;
printf("\tManufacturer: %s\n", getSMBString (current, si->manufacturer));
printf("\tProduct Name: %s\n", getSMBString (current, si->productName));
printf("\tVersion: %s\n", getSMBString (current, si->version));
printf("\tSerial #: %s\n", getSMBString (current, si->serialNumber));
break;
case kSMBTypeBaseBoard: // = 2,
printf("Base Board:\n");
{
struct SMBBaseBoard *bb= (struct SMBBaseBoard *) current;
printf("\tManufacturer: %s\n", getSMBString((void *) current, bb->manufacturer));
printf("\tProduct: %s\n", getSMBString((void *) current, bb->product));
printf("\tVersion: %s\n", getSMBString((void *) current, bb->version));
printf("\tSerial #: %s\n", getSMBString((void *) current, bb->serialNumber));
printf("\tLocation: %s\n", getSMBString((void *) current, bb->locationInChassis));
}
break;
case kSMBTypeSystemEnclosure: // = 3,
{
printf("System Enclosure:\n");
struct SMBSystemEnclosure *se = (struct SMBSystemEnclosure *) current;
printf("\tManufacturer: %s\n", getSMBString((void *)current, se->manufacturer));
printf("\tVersion: %s\n", getSMBString((void*)current, se->version));
printf("\tSerial #: %s\n", getSMBString((void*)current, se->serialNumber));
printf("\tType: %s\n", enclosureTypeToText(se->type & 0x7f));
#if 0
struct SMBSystemEnclosure {
SMB_STRUCT_HEADER // Type 3
SMBString manufacturer;
SMBByte type;
SMBString version;
SMBString serialNumber;
SMBString assetTagNumber;
SMBByte bootupState;
SMBByte powerSupplyState;
SMBByte thermalState;
SMBByte securityStatus;
SMBDWord oemDefined;
#endif
}
break;
case kSMBTypeProcessorInformation: // = 4,
printf("Processor Information:\n");
struct SMBProcessorInformation *pi = (struct SMBProcessorInformation *) (Bytes + Offset);
printf("\tSocket designation: %s\n", getSMBString(Bytes + Offset,
pi->socketDesignation));
printf("\tManufacturer: %s\n", getSMBString(Bytes + Offset, pi->manufacturer));
printf("\tVersion: %s\n", getSMBString(Bytes + Offset, pi->processorVersion));
printf("\tSerial #: %s\n", getSMBString(Bytes + Offset, pi->serialNumber));
printf("\tClocks: External %f Ghz\tMaximum: %f Ghz\tCurrent: %f Ghz\n",
((float)pi->externalClock / 1000),
((float)pi->maximumClock / 1000),
((float)pi->currentClock / 1000));
printf("\tCaches: L1: %d\t L2: %d\tL3: %d\n",
pi->L1CacheHandle,
pi->L2CacheHandle,
pi->L3CacheHandle);
#if 0
struct SMBProcessorInformation {
// 2.0+ spec (26 bytes)
SMB_STRUCT_HEADER // Type 4
SMBString socketDesignation;
SMBByte processorType; // CPU = 3
SMBByte processorFamily; // processor family enum
SMBString manufacturer;
SMBQWord processorID; // based on CPUID
SMBString processorVersion;
SMBByte voltage; // bit7 cleared indicate legacy mode
SMBWord externalClock; // external clock in MHz
SMBWord maximumClock; // max internal clock in MHz
SMBWord currentClock; // current internal clock in MHz
SMBByte status;
SMBByte processorUpgrade; // processor upgrade enum
// 2.1+ spec (32 bytes)
SMBWord L1CacheHandle;
SMBWord L2CacheHandle;
SMBWord L3CacheHandle;
// 2.3+ spec (35 bytes)
SMBString serialNumber;
SMBString assetTag;
SMBString partNumber;
};
#endif
break;
case kSMBTypeMemoryModule: // = 6,
printf("Memory Module:\n");
break;
case kSMBTypeCacheInformation: // = 7,
{
struct SMBCacheInformation *ci = (struct SMBCacheInformation *) current;
printf("Cache Information (#%d):\n", ci->header.handle);
printf("\tSocket designation: %s\n", getSMBString((unsigned char *)ci, ci->socketDesignation));
printf("\tSize: %d/%dKB\n", ci->installedSize, ci->maximumCacheSize);
if (ci->cacheSpeed) { printf("\tSpeed: %d\n", ci->cacheSpeed);}
#if 0
struct SMBCacheInformation {
SMB_STRUCT_HEADER // Type 7
SMBString socketDesignation;
SMBWord cacheConfiguration;
SMBWord maximumCacheSize;
SMBWord installedSize;
SMBWord supportedSRAMType;
SMBWord currentSRAMType;
SMBByte cacheSpeed;
SMBByte errorCorrectionType;
SMBByte systemCacheType;
SMBByte associativity;
};
#endif
}
break;
case kSMBTypeSystemSlot: // = 9,
printf("System Slot:\n");
struct SMBSystemSlot *ss = (struct SMBSystemSlot *) current;
printf("\tDesignation: %s\n", getSMBString(current, ss->slotDesignation));
#if 0
struct SMBSystemSlot {
// 2.0+ spec (12 bytes)
SMB_STRUCT_HEADER // Type 9
SMBString slotDesignation;
SMBByte slotType;
SMBByte slotDataBusWidth;
SMBByte currentUsage;
SMBByte slotLength;
SMBWord slotID;
SMBByte slotCharacteristics1;
// 2.1+ spec (13 bytes)
SMBByte slotCharacteristics2;
};
#endif
break;
case kSMBTypePhysicalMemoryArray: // = 16,
{
printf("Physical Memory Array:\n");
#if 0
struct SMBPhysicalMemoryArray {
// 2.1+ spec (15 bytes)
SMB_STRUCT_HEADER // Type 16
SMBByte physicalLocation; // physical location
SMBByte arrayUse; // the use for the memory array
SMBByte errorCorrection; // error correction/detection method
SMBDWord maximumCapacity; // maximum memory capacity in kilobytes
SMBWord errorHandle; // handle of a previously detected error
SMBWord numMemoryDevices; // number of memory slots or sockets
};
#endif
struct SMBPhysicalMemoryArray * pma = (struct SMBPhysicalMemoryArray *) current;
printf("\tPhysical Location: %d (%s)\n", pma->physicalLocation, memLocToText( pma->physicalLocation ));
printf("\tArray Use: %d (%s)\n", pma->arrayUse, memUseToText(pma->arrayUse));
printf("\tMaximum Capacity: %dGB\n", pma->maximumCapacity/ (1024*1024));
}
break;
case kSMBTypeMemoryDevice: // = 17,
printf("Memory Device:\n");
struct SMBMemoryDevice *md = (struct SMBMemoryDevice *) (Bytes + Offset);
printf("\tSize: %d %s\tSpeed: %d Mhz",
md->memorySize & 0x7FFF,
(md->memorySize & 0x8000 ? "KB" : "MB"),
md->memorySpeed);
printf("\tDevice Locator: %s\tBank Locator: %s\n",
getSMBString (Bytes + Offset, md->deviceLocator),
getSMBString (Bytes + Offset, md->bankLocator));
printf("\tManufacturer: %s\tAsset Tag: %s\n",
getSMBString (Bytes + Offset, md->manufacturer),
getSMBString (Bytes + Offset, md->assetTag));
printf("\tSerial #: %s\tPart #: %s\n",
getSMBString (Bytes + Offset, md->serialNumber),
getSMBString (Bytes + Offset, md->partNumber));
#if 0
struct SMBMemoryDevice {
// 2.1+ spec (21 bytes)
SMB_STRUCT_HEADER // Type 17
SMBWord arrayHandle; // handle of the parent memory array
SMBWord errorHandle; // handle of a previously detected error
SMBWord totalWidth; // total width in bits; including ECC bits
SMBWord dataWidth; // data width in bits
SMBWord memorySize; // bit15 is scale, 0 = MB, 1 = KB
SMBByte formFactor; // memory device form factor
SMBByte deviceSet; // parent set of identical memory devices
SMBString deviceLocator; // labeled socket; e.g. "SIMM 3"
SMBString bankLocator; // labeled bank; e.g. "Bank 0" or "A"
SMBByte memoryType; // type of memory
SMBWord memoryTypeDetail; // additional detail on memory type
// 2.3+ spec (27 bytes)
SMBWord memorySpeed; // speed of device in MHz (0 for unknown)
SMBString manufacturer;
SMBString serialNumber;
SMBString assetTag;
SMBString partNumber;
};
#endif
break;
case kSMBType32BitMemoryErrorInfo: // = 18,
printf("32-Bit Memory Error Info:\n");
break;
case jSMBTypePortableBatteryInfo: // == 22
{
printf("Portable Battery Info:\n");
struct SMBPortableBatteryInformation *bi = (struct SMBPortableBatteryInformation *) current;
printf("\tManufacturer: %s %d\n", getSMBString (current, bi->manufacturer), si->manufacturer);
printf("\tSerial #: %s\n", getSMBString (current, bi->serialNumber));
printf("\tName: %s\n", getSMBString (current, bi->deviceName));
printf("\tDesign Capacity: %d\n",bi->designCapacity);
printf("\tDesign Voltage: %d\n",bi->designVoltage);
}
break;
case jSMBTypeManagementDeviceComponentInfo: // == 35
printf("Management Device Component Info:\n");
break;
case jSMBTypeManagementDeviceThresholdData: // == 36
printf("Management Device Threshold Data:\n");
break;
case jSMBTypeOnboardDevicesExtendedInfo: // = 41
printf("Onboard Devices Extended Info:\n");
{
struct SMBOnboardDevicesExtendedInformation *odei = (struct SMBOnboardDevicesExtendedInformation *) current;
printf("\tReference Designation : %s\tType: %s (%s)\n",
getSMBString(current, odei->referenceDesignation),
deviceTypeToText(odei->deviceType & 0x7f),
(odei->deviceType &0x80 ? "enabled" : "disabled"));
#if 0
struct SMBOnboardDevicesExtendedInformation {
SMB_STRUCT_HEADER
SMBByte referenceDesignation;
SMBByte deviceType;
SMBByte deviceTypeInstance;
SMBWord SegmentGroupNumber;
SMBByte busNumber;
SMBByte bitField;
};
#endif
}
break;
case jSMBTypeGroupAssociations: // 14
printf("Group Associations\n");
break;
case jSMBTypeIMIDeviceInfo: // 38
printf("IPMI Device Information\n");
break;
case kSMBType64BitMemoryErrorInfo: // = 33,
printf("64-Bit Memory Error Info:\n");
break;
// J's Additions:
case 8: // Port Connector Information: (7.9)
printf("Port Connector Information\n");
struct SMBPortConnectorInformation *pci =
(struct SMBPortConnectorInformation *) (Bytes + Offset);
printf("\tInternal Reference Designator: %s\tType: %d\n",
getSMBString(Bytes + Offset , pci->InternalReferenceDesignator),
pci->InternalConnectorType);
printf("\tExternal Reference Designator: %s\tType: %d\n",
getSMBString(Bytes + Offset , pci->ExternalReferenceDesignator),
pci->ExternalConnectorType);
break;
case 10: // On Board Device Information (obsolete in 2.7)
// This is obsolete, but also different in that the
// length is longer than the average
{
int numDevices = (current[1] -4) /2;
printf("On Board Device Information (%d Devices):\n", numDevices);
int d = 0;
for (d = 1 ; d <= numDevices; d++)
{
printf("\t%d: %s\n",
d, getSMBString (current,
*(current + 5 + 2 * (d-1))));
}
}
break;
case 11: // OEM Strings
printf("OEM Strings: (%d Strings)\n", current[0x4]);
Offset += 5;
int s = 0;
while (Bytes[Offset])
{
printf("\tString %d: ", s);
while (Bytes[Offset]) {
if ((Bytes[Offset] == ' ') || ispunct (Bytes[Offset]) || (isalnum(Bytes[Offset]))) {
putchar (Bytes[Offset]);
}
else
{
switch (Bytes[Offset])
{
case 0xa: printf("\\r"); break;
case 0xd: printf("\\n"); break;
default:
printf("\\x%02x", Bytes[Offset]);
} // end switch (Bytes[Offset]) for case 11
}
Offset++;
} ; // end while
printf("\n");
s++;
Offset++;
}
return (Offset+1);
break;
case 12: // System Configuration Options
printf("System Configuration Options:\n");
break;
case 13: // BIOS Language Information
printf("BIOS Language Information:\n");
printf("Installable Languages: %d\n",
*(current + 0x4));
printf("String: %d %s\n", *(current + 0x15),
getSMBString (current ,
*(current+ 0x15)));
break;
case jSMBTypeMemoryArrayMappedAddress: // Memory Array Mapped Address
printf("Memory Array Mapped Address:\n");
{
struct SMBMemoryArrayMappedAddress *mama = (struct SMBMemoryArrayMappedAddress *) current;
printf("\tAddress range: 0x%x-0x%x\n",
mama->startingAddress,
mama->endingAddress);
}
break;
case jSMBTypeSystemBootInfo: // == 32
printf("System Boot Information:\n");
{
struct SMBSystemBootInformation *sbi = (struct SMBSystemBootInfomration *) current;
int i =0;
for (i = 0 ; i < sbi->header.length - 10; i++){
printf("\tStatus[%d]: %d (%s)\n", i,sbi->BootStatus[i],
bootStatusToText(sbi->BootStatus[i]));
}
}
break;
case 127: // End of table
printf("End of Table\n");
break;;
/* Apple Specific Structures */
case kSMBTypeFirmwareVolume: // = 128,
printf("Firmware Volume:\n");
break;
case kSMBTypeMemorySPD: // = 130,
printf("Memory SPD:\n");
hexDump((void *)current, ssh->length);
break;
case kSMBTypeOemProcessorType: // = 131,
printf("OEM Processor Type:\n");
break;
case kSMBTypeOemProcessorBusSpeed: // = 132
printf("Firmware Volume:\n");
break;
case 134: // not sure what this is yet
printf("Apple SMC Version (probably - 134)\n");
{struct SMBSMCVersion *sv = (struct SMBSMCVersion *) current;
printf("\tVersion String: %s\n", sv->version);}
hexDump((void *)current, ssh->length);
break;
default:
printf("Unknown type: %d?! (%d bytes)\n", *current , ssh->length);
hexDump((void *)current, ssh->length);
break;
};
Offset += len +1 ; // To get to strings
if (Bytes[Offset]) Offset--; // If it's the NULL byte terminator, correct.
while (Bytes[Offset])
{
while (Bytes[Offset++]) {};
}
// If we're here, we hit two NULLs.
return (Offset +1 );
}
void parseBIOSData (CFDataRef BIOSDump)
{
int len = CFDataGetLength(BIOSDump);
const char *bytes = alloca (len);
CFDataGetBytes(BIOSDump, CFRangeMake(0,CFDataGetLength(BIOSDump)), (void*)bytes);
int i = 0;
if (g_verbose) {
printf("Data:\n");
for (i = 0 ; i < len; i ++)
{ if (isalnum(bytes[i])) putchar (bytes[i]);
else putchar ('.');
}
} // end g_verbose
// Now parse SMBios Structures
for (i = 0; i < len; )
{
i = parseSMBIOSStruct((void *)bytes, i, len);
}
}
int main(int argc, char **argv)
{
io_iterator_t deviceList;
io_service_t device;
io_name_t deviceName;
io_string_t devicePath;
char *ioPlaneName = "IOService";
char *ioClassName = "AppleSMBIOS";
char *ioPropertyName = "SMBIOS";
int dev = 0;
kern_return_t kr;
if (argc >1)
{
if (strcmp(argv[1], "-v") == 0) { g_verbose++;}
}
// Iterate over all services matching AppleSMBIOS (only one)
// Note the call to IOServiceMatching, to create the dictionary
kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(ioClassName),
&deviceList);
if (kr)
{
fprintf(stderr,"IOServiceGetMatchingServices: error\n");
exit(1);
}
if (!deviceList) { fprintf(stderr,"%s - No devices matched\n", ioClassName); exit(2); }
while ( IOIteratorIsValid(deviceList) &&
(device = IOIteratorNext(deviceList))) {
kr = IORegistryEntryGetName(device, deviceName);
if (kr)
{
fprintf (stderr,"Error getting name for device\n");
IOObjectRelease(device);
continue;
}
kr = IORegistryEntryGetPath(device, ioPlaneName, devicePath);
if (kr) {
// Device does not exist on this plane
IOObjectRelease(device);
continue;
}
dev++;
vprintf("%s\t%s\n",deviceName, devicePath);
CFDataRef BIOSDump = getProp(device, ioPropertyName);
parseBIOSData (BIOSDump);
}
if (device) {
fprintf (stderr,
"Iterator invalidated while getting devices. Did hardware configuration change?\n");
}
return kr;
}
//#include "stackshot.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <kern/kcdata.h> // for the KCData format
#include <time.h>
#include <unistd.h>
#include <uuid/uuid.h>
/**
* Stackshot for 10.11/9 (XNU 32xx) and later:
*
* Uses the new (and still undocumented) syscall #491 in place of #365 which
* has been removed.
*
* Obviously requires root access to run. Provides full stack traces - both
* kernel and user - of all threads in the system.
*
* Compiles cleanly for iOS and MacOS, so I'm leaving it as source rather
* than provide a binary.
*
*
* (AAPL will likely slap an entitlement on this in MacOS 13/iOS 11 anyway..
* Use/copy freely while you can - but a little acknowledgment wouldn't hurt :-)
*
* The full explanation of how to use this is in MOXiI Vol. I (debugging)
* The full explanation of how it works (kernel-side, pretty darn ingenious)
* is in MOXiI Vol. II
*
* So stay tuned. More coming.
*
* Jonathan Levin, 11/01/2016
*
*/
// As with the previous version, we have to rip the typedefs and structs from the kernel
// headers, as they are "private" and not exported...
typedef struct stackshot_config {
/* Input options */
int sc_pid; /* PID to trace, or -1 for the entire system */
uint32_t sc_flags; /* Stackshot flags */
uint64_t sc_delta_timestamp; /* Retrieve a delta stackshot of system state that has changed since this time */
/* Stackshot results */
uint64_t sc_buffer; /* Pointer to stackshot buffer */
uint32_t sc_size; /* Length of the stackshot buffer */
/* Internals */
uint64_t sc_out_buffer_addr; /* Location where the kernel should copy the address of the newly mapped buffer in user space */
uint64_t sc_out_size_addr; /* Location where the kernel should copy the size of the stackshot buffer */
} stackshot_config_t;
// As well as the definition of the system call itself, which is a wrapper over the generic syscall(2)
int stack_snapshot_with_config(int stackshot_config_version, stackshot_config_t *stackshot_config, uint32_t stackshot_config_size) {
// AAPL "deprecates" syscall(2) in 10.12. And I say, if you deprecate it, at least expose the
// syscall header to user mode!
return (syscall (491, stackshot_config_version, (uint64_t) stackshot_config, stackshot_config_size));
}
// from kern/debug.h
enum {
STACKSHOT_GET_DQ = 0x01,
STACKSHOT_SAVE_LOADINFO = 0x02,
STACKSHOT_GET_GLOBAL_MEM_STATS = 0x04,
STACKSHOT_SAVE_KEXT_LOADINFO = 0x08,
STACKSHOT_GET_MICROSTACKSHOT = 0x10,
STACKSHOT_GLOBAL_MICROSTACKSHOT_ENABLE = 0x20,
STACKSHOT_GLOBAL_MICROSTACKSHOT_DISABLE = 0x40,
STACKSHOT_SET_MICROSTACKSHOT_MARK = 0x80,
STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY = 0x100,
STACKSHOT_GET_BOOT_PROFILE = 0x200,
STACKSHOT_SAVE_IMP_DONATION_PIDS = 0x2000,
STACKSHOT_SAVE_IN_KERNEL_BUFFER = 0x4000,
STACKSHOT_RETRIEVE_EXISTING_BUFFER = 0x8000,
STACKSHOT_KCDATA_FORMAT = 0x10000,
STACKSHOT_ENABLE_BT_FAULTING = 0x20000,
STACKSHOT_COLLECT_DELTA_SNAPSHOT = 0x40000,
// and there's more, but I don't use them..
};
#define STACKSHOT_CONFIG_TYPE 1
// Convenience function hiding the implementation - in case AAPL changes it yet again
int get_stackshot (pid_t Pid, char **Addr, uint64_t *Size, uint64_t Flags)
{
stackshot_config_t stconf = { 0 };
stconf.sc_pid = Pid;
stconf.sc_buffer = 0; // buf;
stconf.sc_size = 0; // 4096;
stconf.sc_flags = STACKSHOT_KCDATA_FORMAT | Flags;
stconf.sc_out_buffer_addr = (uint64_t) Addr;
stconf.sc_out_size_addr = (uint64_t) Size;
int rc = stack_snapshot_with_config(STACKSHOT_CONFIG_TYPE, &stconf, sizeof(stconf));
return (rc);
} // get_stackshot
// Dumpers: These will format the item into the Output string, which may be just
// printed out, or used further (as I do in Process Explorer)
void dump_mem_and_io_stats (struct mem_and_io_snapshot *MaI, char *Output)
{
sprintf(Output, "Pages: %d free, %d active, %d Inactive, %d Purgeable\n %d Wired, %d Speculative, %d Throttled, %d Filebacked\nCompressor: %d comp, %d decomp, %d size\nBusy: %d\n",
MaI->free_pages,
MaI->active_pages,
MaI->inactive_pages,
MaI->purgeable_pages,
MaI->wired_pages,
MaI->speculative_pages,
MaI->throttled_pages,
MaI->filebacked_pages,
MaI->compressions,
MaI->decompressions,
MaI->compressor_size,
MaI->busy_buffer_count);
}
void dump_iostats(struct io_stats_snapshot *Stats, char *Output)
{
sprintf(Output, "Disk Reads: %lld reads (%lld bytes), %lld writes (%lld bytes)\n",
Stats->ss_disk_reads_count, Stats->ss_disk_reads_size,
Stats->ss_disk_writes_count, Stats->ss_disk_writes_size);
} // dump_iostats
void dump_cpu_times (struct stackshot_cpu_times *Times, char *Output)
{
sprintf (Output,"User: %lld.%06lld secs, System: %lld.%06lld secs",
Times->user_usec / 1000000,
Times->user_usec % 1000000,
Times->system_usec / 1000000,
Times->system_usec % 1000000);
}
void dump_array (kcdata_item_t Item, char *Output)
{
int elemType = (Item->flags >> 32) & UINT32_MAX;
int elemCount = (Item->flags) & UINT32_MAX;
//printf("%d elements, Type %x\n", elemCount, elemType);
char *elemData = Item->data;
int elem = 0;
int adv = 0;
for (elem = 0 ; elem < elemCount; elem++)
{
switch (elemType)
{
case STACKSHOT_KCTYPE_USER_STACKFRAME64:
case STACKSHOT_KCTYPE_KERN_STACKFRAME64:
{
if (elem == 0) sprintf(Output,"\t\t%s Stack Trace:\n",
elemType == STACKSHOT_KCTYPE_USER_STACKFRAME64 ? "User" : "Kernel");
adv = sizeof (struct stack_snapshot_frame64);
struct stack_snapshot_frame64 *ssf64 =
(struct stack_snapshot_frame64 *) elemData;
sprintf(Output+strlen(Output),"\t\tLR: 0x%016llx, SP: 0x%016llx\n", ssf64->lr, ssf64->sp);
break;
}
case KCDATA_TYPE_LIBRARY_LOADINFO64:
{
if (elem == 0) sprintf(Output,"\t\tLoaded Libraries:\n");
char uuidUnparsed[128];
adv = sizeof(struct dyld_uuid_info_64);
struct dyld_uuid_info_64 *dui64 =
(struct dyld_uuid_info_64 *) elemData;
uuid_unparse_upper (dui64->imageUUID, uuidUnparsed);
sprintf(Output + strlen(Output),"\t\t0x%llx - %s\n",
dui64->imageLoadAddress, uuidUnparsed);
}
break;
case STACKSHOT_KCTYPE_DONATING_PIDS: //0x907u /* int[] */
{
if (elem == 0) sprintf(Output,"\t\tImportance Donors: ");
sprintf(Output + strlen(Output),"%d, ", (* (int *) elemData));
adv = sizeof(int);
if (elem ==elemCount -1) Output[strlen(Output)-2] = '\0'; // truncate last ,
}
break;
default: printf("ARRAY OF TYPE %x - Unhandled (yet)\n", elemType);
break;
}
elemData += adv;
} // end for
}
char *ths_flags_to_text (uint64_t Flags)
{
static char returned[4096];
returned[0] ='\0';
if (Flags & kHasDispatchSerial) strcat (returned, "Has dispatch serial, "); // = 0x4,
// probably don't need this in output..
if (Flags & kStacksPCOnly) strcat (returned, "PC only, "); // = 0x8, /* Stacif (Flags & k traces have no frame pointers. */
if (Flags & kThreadDarwinBG) strcat (returned, "DarwinBG, "); // = 0x10, /* Thread is darwinbg */
if (Flags & kThreadIOPassive) strcat (returned, "Passive I/O, "); // = 0x20, /* Thread uses passive IO */
if (Flags & kThreadSuspended) strcat (returned, "Suspended, "); // = 0x40, /* Thread is suspended */
if (Flags & kThreadTruncatedBT) strcat (returned, "Truncated BT, "); // = 0x80, /* Unmapped pages caused truncated bacif (Flags & ktrace */
if (Flags & kGlobalForcedIdle) strcat (returned, "Global Forced Idle, "); // = 0x100, /* Thread performs global forced idle */
if (Flags & kThreadFaultedBT) strcat (returned, "Faulted BT, "); // = 0x200, /* Some thread stacif (Flags & k pages were faulted in as part of BT */
if (Flags & kThreadTriedFaultBT) strcat (returned, "Tried Faulted BT, "); // = 0x400, /* We tried to fault in thread stacif (Flags & k pages as part of BT */
if (Flags & kThreadOnCore) strcat (returned, "on core, "); // = 0x800, /* Thread was on-core when we entered debugger context */
if (Flags & kThreadIdleWorker) strcat (returned, "Idle Worker, "); // = 0x1000, /* Thread is an idle libpthread worif (Flags & ker thread */
if (returned[strlen(returned) -1] == ' ')
returned[strlen(returned) - 2 ] = '\0';
if (!returned[0] )
sprintf(returned, "0x%llx", Flags);
return (returned);
} ; // ths_flags_to_text
const char *ths_state_to_text(uint32_t State)
{
static char returned[64];
returned[0] = '\0';
if (State & SS_TH_WAIT) strcat (returned,"Waiting ");
if (State & SS_TH_SUSP) strcat (returned,"Suspended ");
if (State & SS_TH_RUN) strcat (returned,"Running ");
if (State & SS_TH_UNINT) strcat (returned,"Uninterruptible ");
if (State & SS_TH_IDLE) strcat (returned,"Idle ");
if (State & SS_TH_TERMINATE) strcat (returned,"Terminated ");
if (State & SS_TH_TERMINATE2) strcat (returned,"will terminate ");
if (!returned[0]) sprintf(returned, "0x%x", State);
return (returned);
}
void dump_thread_snapshot (struct thread_snapshot_v3 *Ths, char *Output)
{
sprintf(Output, "TID: %lld State: %s PRI: %d/%d Flags: %s ",
Ths->ths_thread_id,
ths_state_to_text(Ths->ths_state),
Ths->ths_sched_priority,
Ths->ths_base_priority,
Ths->ths_ss_flags? ths_flags_to_text(Ths->ths_ss_flags) : "(none)");
if (Ths->ths_continuation)
{
sprintf(Output + strlen(Output), "\n\t\tContinuation: 0x%llx (no kernel stack)", Ths->ths_continuation);
}
} // dump_thread_snap
char *tsv2_flags_to_text (uint64_t Flags)
{
static char returned[4096];
returned[0] ='\0';
if (Flags & kTaskRsrcFlagged) strcat (returned, "Resources Flagged, ");
if (Flags & kTerminatedSnapshot) strcat (returned, "Terminated, ");
if (Flags & kPidSuspended) strcat (returned, "PID Suspended, ");
if (Flags & kFrozen) strcat (returned, "Frozen, ");
if (Flags & kTaskDarwinBG) strcat (returned, "DarwinBG, ");
if (Flags & kTaskVisVisible) strcat (returned, "Visible, ");
if (Flags & kTaskVisNonvisible) strcat (returned, "NonVisible, ");
if (Flags & kTaskIsForeground) strcat (returned, "Foreground, ");
if (Flags & kTaskIsBoosted) strcat (returned, "Boosted, ");
if (Flags & kTaskIsSuppressed) strcat (returned, "Suppressed, ");
if (Flags & kTaskIsTimerThrottled) strcat (returned, "Timer Throttled, ");
if (Flags & kTaskIsLiveImpDonor) strcat (returned, "Live Donor, ");
if (Flags & kTaskIsImpDonor) strcat (returned, "Donor, ");
if (Flags & kTaskWqExceededConstrainedThreadLimit) strcat (returned, "Wq Exc Const, ");
if (Flags & kTaskWqExceededTotalThreadLimit) strcat (returned, "Wq Exc Total, ");
if (Flags & kTaskIsDirty) strcat (returned, "Dirty, ");
if (Flags & kTaskSharedRegionInfoUnavailable) strcat (returned, "Shared Reg Unavail, ");
if (Flags & kTaskWqFlagsAvailable) strcat (returned, "WQ Flags avail, ");
if (returned[strlen(returned) -1] == ' ')
returned[strlen(returned) - 2 ] = '\0';
return (returned);
} // tsv2_flags_to_text
// Main Item handler
void handle_kcdata_item (kcdata_item_t Item, char *Output)
{
// Handling most KCData items - not all are needed for stack shots
// some are for corpses (another fascinating topic in and of itself)
// and you can always fill in support for the rest
static int inContainer =0;
{
// print tabs only if not end
int x = inContainer;
if (Item->type == KCDATA_TYPE_CONTAINER_END) x--;
while (x > 0) { sprintf (Output,"\t"); Output +=1; x--;}
}
switch (Item->type)
{
case KCDATA_TYPE_STRING_DESC: // 0x1u
sprintf(Output,"String desc"); break;
case KCDATA_TYPE_UINT32_DESC:// 0x2u
sprintf(Output,"Uint32_t desc"); break;
case KCDATA_TYPE_UINT64_DESC:// 0x3u
sprintf(Output,"Uint64_t desc"); break;
case KCDATA_TYPE_INT32_DESC: // 0x4u
sprintf(Output,"Int32_t desc"); break;
case KCDATA_TYPE_INT64_DESC: // 0x5u
sprintf(Output,"Int64_t desc"); break;
case KCDATA_TYPE_BINDATA_DESC:// 0x6u
sprintf(Output,"bindata desc"); break;
case KCDATA_TYPE_CONTAINER_BEGIN: // 0x13u
// sprintf(Output,"Container begin");
inContainer++;
break;
case KCDATA_TYPE_CONTAINER_END: // 0x14u
//sprintf(Output,"Container end");
inContainer--; break;
case KCDATA_TYPE_ARRAY: // 0x11u /* Array of data OBSOLETE DONT USE THIS*/
case KCDATA_TYPE_ARRAY_PAD0: // 0x20u /* Array of data with: // 0 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD1: // 0x21u /* Array of data with 1 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD2: // 0x22u /* Array of data with 2 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD3: // 0x23u /* Array of data with 3 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD4: // 0x24u /* Array of data with 4 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD5: // 0x25u /* Array of data with 5 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD6: // 0x26u /* Array of data with 6 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD7: // 0x27u /* Array of data with 7 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD8: // 0x28u /* Array of data with 8 byte of padding*/
case KCDATA_TYPE_ARRAY_PAD9: // 0x29u /* Array of data with 9 byte of padding*/
case KCDATA_TYPE_ARRAY_PADa: // 0x2au /* Array of data with a byte of padding*/
case KCDATA_TYPE_ARRAY_PADb: // 0x2bu /* Array of data with b byte of padding*/
case KCDATA_TYPE_ARRAY_PADc: // 0x2cu /* Array of data with c byte of padding*/
case KCDATA_TYPE_ARRAY_PADd: // 0x2du /* Array of data with d byte of padding*/
case KCDATA_TYPE_ARRAY_PADe: // 0x2eu /* Array of data with e byte of padding*/
case KCDATA_TYPE_ARRAY_PADf: // 0x2fu /* Array of data with f byte of padding*/
dump_array(Item, Output);
break;
case KCDATA_TYPE_TYPEDEFINTION: // 0x12u /* Meta type that describes a type on the fly. */
sprintf(Output,"Type def\n");
break;
case KCDATA_TYPE_LIBRARY_LOADINFO: // 0x30u /* struct dyld_uuid_info_32 */
sprintf (Output,"Load Info\n"); break;
case KCDATA_TYPE_LIBRARY_LOADINFO64: // 0x31u /* struct dyld_uuid_info_64 */
sprintf (Output,"Load Info 64"); break;
case KCDATA_TYPE_TIMEBASE: // 0x32u /* struct mach_timebase_info */
// sprintf(Output,"Timebase: "); break;
break;
case KCDATA_TYPE_MACH_ABSOLUTE_TIME: // 0x33u /* uint64_t */
sprintf (Output,"Abs Time: %lld", (*((uint64_t *) &Item->data[0])) ); break;
case KCDATA_TYPE_TIMEVAL: // 0x34u /* struct timeval64 */
sprintf (Output,"Timeval: "); break;
case KCDATA_TYPE_USECS_SINCE_EPOCH: // 0x35u /* time in usecs uint64_t */
{
// Note that this type checks USECS. time_t (for ctime and friends)
// requires SECS. So adjust!
time_t secs_since_epoch = (*((uint64_t *) &Item->data[0])) / 1000000;
sprintf (Output,"Time: %s..", ctime(&secs_since_epoch));
break;
}
case KCDATA_TYPE_PID: // 0x36u /* int32_t */
sprintf (Output,"PID: \n"); break;
case KCDATA_TYPE_PROCNAME: // 0x37u /* char * */
sprintf (Output,"procname: \n"); break;
case KCDATA_TYPE_NESTED_KCDATA: // 0x38u /* nested kcdata buffer */
sprintf (Output,"kcdata: \n"); break;
case STACKSHOT_KCTYPE_IOSTATS: // 0x901u
strcat (Output, "IOStats: ");
dump_iostats ((void *)Item->data, Output + strlen(Output));
break;
case STACKSHOT_KCTYPE_GLOBAL_MEM_STATS: // 0x902u /* struct mem_and_io_snapshot */
dump_mem_and_io_stats ((void *)Item->data, Output);
break;
case STACKSHOT_KCCONTAINER_TASK: // 0x903u:
sprintf(Output,"Container task");
break;
case STACKSHOT_KCCONTAINER_THREAD: // 0x904u:
sprintf(Output,"Container thread");
break;
case STACKSHOT_KCTYPE_TASK_SNAPSHOT: //0x905u /* task_snapshot_v2 */
{
//printf("Task snapshot: %d\n", Item->size);
struct task_snapshot_v2 *tsv2 = (struct task_snapshot_v2 *) Item->data;
sprintf(Output,"PID : %d (%lld), comm: %s Flags: %s\n", tsv2->ts_pid, tsv2->ts_unique_pid, tsv2->ts_p_comm,
tsv2_flags_to_text(tsv2->ts_ss_flags));
if (tsv2->ts_task_size < 1024 *1024)
{
sprintf(Output + strlen(Output), "\tSize: %lldK Max Res: %lldK", tsv2->ts_task_size / (1024), tsv2->ts_max_resident_size / (1024));
}
else
sprintf(Output + strlen(Output), "\tSize: %lldM Max Res: %lldM", tsv2->ts_task_size / (1024*1024), tsv2->ts_max_resident_size / (1024*1024));
}
break;
case STACKSHOT_KCTYPE_THREAD_SNAPSHOT: //0x906u /* thread_snapshot_v2, thread_snapshot_v3 */
{
dump_thread_snapshot ((void *)Item->data, Output);
}
break;
case STACKSHOT_KCTYPE_DONATING_PIDS: //0x907u /* int[] */ // found in array
sprintf(Output,"Donating PIDs");
break;
case STACKSHOT_KCTYPE_SHAREDCACHE_LOADINFO: //0x908u /* same as KCDATA_TYPE_LIBRARY_LOADINFO64 */
{
sprintf(Output,"Shared Cache Load Info");
char uuidUnparsed[128];
struct dyld_uuid_info_64 *dui64 =
(struct dyld_uuid_info_64 *) Item->data;
uuid_unparse_upper (dui64->imageUUID, uuidUnparsed);
sprintf(Output + strlen(Output),"\t\t0x%llx - %s\n",
dui64->imageLoadAddress, uuidUnparsed);
}
break;
case STACKSHOT_KCTYPE_THREAD_NAME: //0x909u /* char[] */
sprintf(Output,"Thread Name: %s", Item->data);
break;
// These are only found in arrays, so I handle them in dump_array...
case STACKSHOT_KCTYPE_KERN_STACKFRAME: //0x90Au /* struct stack_snapshot_frame32 */
printf("Kernel Stack Frame\n");
break;
case STACKSHOT_KCTYPE_KERN_STACKFRAME64: //0x90Bu /* struct stack_snapshot_frame64 */
printf("Kernel Stack Frame 64\n");
break;
case STACKSHOT_KCTYPE_USER_STACKFRAME: //0x90Cu /* struct stack_snapshot_frame32 */
printf("User Stack Frame\n");
break;
case STACKSHOT_KCTYPE_USER_STACKFRAME64: //0x90Du /* struct stack_snapshot_frame64 */
printf("User Stack Frame 64\n");
break;
// Back to common types:
case STACKSHOT_KCTYPE_BOOTARGS: //0x90Eu /* boot args string */
sprintf(Output,"Boot-Args: %s", Item->data);
break;
case STACKSHOT_KCTYPE_OSVERSION: //0x90Fu /* os version string */
// printf("OSVer %d, 0x%llx\n", Item->size, Item->flags);
sprintf(Output,"OS Version: %s", Item->data);
break;
case STACKSHOT_KCTYPE_KERN_PAGE_SIZE: //0x910u /* kernel page size in uint32_t */
sprintf(Output,"Page size: %d bytes", * ((uint32_t *)(Item->data)));
break;
case STACKSHOT_KCTYPE_JETSAM_LEVEL: //0x911u /* jetsam level in uint32_t */
sprintf(Output,"Jetsam Level: %d", * ((uint32_t *)(Item->data)));
break;
case STACKSHOT_KCTYPE_DELTA_SINCE_TIMESTAMP: //0x912u /* timestamp used for the delta stackshot */
sprintf(Output,"Delta since");
break;
case STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT: //0x940u /* task_delta_snapshot_v2 */
sprintf(Output,"Task Delta snapshot");
break;
case STACKSHOT_KCTYPE_THREAD_DELTA_SNAPSHOT: //0x941u /* thread_delta_snapshot_v2 */
sprintf(Output,"Thread Delta snapshot");
break;
case STACKSHOT_KCTYPE_KERN_STACKLR: // 0x913u /* uint32_t */
sprintf(Output,"Kern stack LR\n");
break;
case STACKSHOT_KCTYPE_KERN_STACKLR64: // 0x914u /* uint64_t */
printf("Kern stack LR 64\n");
break;
case STACKSHOT_KCTYPE_USER_STACKLR: // 0x915u /* uint32_t */
printf("Kern stack LR\n");
break;
case STACKSHOT_KCTYPE_USER_STACKLR64: // 0x916u /* uint64_t */
printf("Kern stack LR 64\n");
break;
case STACKSHOT_KCTYPE_NONRUNNABLE_TIDS: // 0x917u /* uint64_t */
printf("Nonrunnable TIDs\n");
break;
case STACKSHOT_KCTYPE_NONRUNNABLE_TASKS: // 0x918u /* uint64_t */
printf("Nonrunnable Tasks\n");
break;
case STACKSHOT_KCTYPE_CPU_TIMES: // 0x919u /* struct stackshot_cpu_times */
strcpy (Output, "CPU Times: ");
dump_cpu_times((void *)Item->data,Output + strlen(Output));
break;
case STACKSHOT_KCTYPE_STACKSHOT_DURATION: // 0x91au /* struct stackshot_duration */
printf("Stackshot duration\n");
break;
case STACKSHOT_KCTYPE_STACKSHOT_FAULT_STATS: // 0x91bu /* struct stackshot_fault_stats */
printf("Stackshot fault stats\n");
break;
case STACKSHOT_KCTYPE_KERNELCACHE_LOADINFO: // 0x91cu /* kernelcache UUID -- same as KCDATA_TYPE_LIBRARY_LOADINFO64 */
printf("kernelcache UUID:\n");
break;
case KCDATA_TYPE_BUFFER_END:
printf("Buffer end!\n");
break;
default :
printf("UNKNOWN MAGIC 0x%x! AAPL must've changed the format (or J hasn't gotten to it yet?)!\n", Item->type);
}
} // handle_kcdata_item
void handle_kcdata_iter(kcdata_iter_t Iter,char *Output)
{
handle_kcdata_item (Iter.item,Output);
};
int main (int argc, char **argv)
{
char *addr;
uint64_t size;
int pid = -1;
// Sorry , people. Stackshot requires root privileges :-|
if (geteuid()) {
fprintf(stderr,"You're wasting my time, little man. Come back as root.\n");
exit(1);
}
if (argc == 2)
{
pid = atoi(argv[1]);
}
int Flags = STACKSHOT_GET_GLOBAL_MEM_STATS | STACKSHOT_GET_DQ | STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO | STACKSHOT_SAVE_IMP_DONATION_PIDS;
int rc = get_stackshot (pid, &addr, &size, Flags);
if (rc < 0) {
perror ("stack_snapshot_with_config");
exit(1);
}
// If you want the buffer for hexdumping, etc:
// fprintf(stderr,"RC: %d - Got %d bytes in %p\n", rc, size, addr);
// write (1, addr, size);
kcdata_item_t item = (kcdata_item_t) addr;
if (item->type != KCDATA_BUFFER_BEGIN_STACKSHOT) {
fprintf(stderr,"Stackshot doesn't begin with magic - maybe AAPL changed format again?\n");
return 1;
}
int off = 16;
int inContainer = 0;
char Output[40960];
#ifdef APPLE_ITER
// This loop will iterate using AAPL's own APIs:
kcdata_iter_t iter = kcdata_iter (addr, size);
iter = kcdata_iter_next(iter);
while (kcdata_iter_valid(iter) )
{
// fprintf(stdout,"Type: 0x%x, Size: %d bytes is ", kcdata_iter_type(iter), kcdata_iter_size(iter));
handle_kcdata_iter (iter,Output);
printf("%s\n",Output);
Output[0] ='\0';
#if 0
if (kcdata_iter_type(iter) == KCDATA_TYPE_ARRAY)
{
if (kcdata_iter_array_valid(iter))
printf(" (%d elements of type %x)\n",
kcdata_iter_array_elem_count(iter), kcdata_iter_array_elem_type(iter));
else { printf("Invalid array!\n");}
}
#endif
iter = kcdata_iter_next(iter);
}
#else // my way
while (off < size)
{
int skip = 0;
kcdata_item_t item = (kcdata_item_t) (addr + off);
handle_kcdata_item(item, Output);
printf("%s\n", Output);
Output[0] = '\0';
int padding = item->size % 16;
if (padding ) padding = 16-padding;
{
//printf("Advancing by %d bytes, padding %d\n", *(magic+1), padding);
off+= item->size + 16 + padding;
}
}
#endif
} // main
#!/bin/sh
if [[ $# != 2 ]]; then
echo Usage: tbdize shared_cache private_framework
echo
echo This will create a file which \*should\* be compatible with Apple\'s \"tbd\" file format,
echo which is now what they use in place of dylib stubs. Apple removed pretty much everything
echo in Xcode 7, and it\'s time to bring it back.
echo
echo This MAY be buggy in some cases, AND it doesn\'t export Obj-c classes just yet. I know.
echo Obj-c will come soon. But I use this all too often, so you might find it useful as well.
echo
echo For comments or bug-reports, please use http://NewOSXBook.com/forum/
echo
exit 1
fi
cache=$1
fw=$2
##
## Ensure we have jtool
##
jtoolTest=`jtool -l /bin/ls 2>/dev/null`
if [[ -z "$jtoolTest" ]]; then
echo Where\'s jtool\? Please make sure it\'s in your path \(Recommended: /usr/local/bin\)
exit 3
fi
##
## Ensure file is a shared cache:
##
cacheTest=`jtool $cache 2>/dev/null| grep "File is a shared cache" 2>/dev/null`
if [[ -z "$cacheTest" ]]; then
echo $cache is not recognized by jtool to be a shared cache. Sorry.
exit 2
else
# cacheTest also has the architecture
arch=`echo $cacheTest | cut -d'(' -f2 | cut -d')' -f1`
fi
##
## Now see if found in cache
##
uuidOutput=`jtool -l $cache:$fw 2>/dev/null| grep LC_UUID`
# Output of LC_UUID should be "LC 09: LC_UUID UUID: 419BCF22-D977-32BD-99F6-F7BB6B50E133"
uuid=`echo $uuidOutput | cut -d':' -f3`
if [[ -z "$uuid" ]]; then
echo Framework $fw not found in cache.
exit 3
fi
syms=`jtool -S $cache:$fw 2>/dev/null|grep " [TS] " | cut -d' ' -f3`
objcc=`jtool -d objc $cache:$fw 2>/dev/null`
dir=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/PrivateFrameworks/$fw.framework
if [ ! -d $dir ]; then
if mkdir -p $dir; then
echo Created framework directory in the iPhone SDK
else
echo Unable to create framework directory for some reason, will put file in /tmp
DIR=/tmp
fi
fi
if touch $dir/$fw.tbd 2>/dev/null; then
true;
else
echo Unable to create $dir/$fw.tbd - defaulting to /tmp instead.
dir=/tmp
fi
## At this point all is in readiness:
#echo "--- !tapi-tbd-v2"
echo "---" > $dir/$fw.tbd
echo "archs: [ $arch ]" >> $dir/$fw.tbd
#echo "uuids: [ '$arch': $uuid ]" >> $dir/$fw.tbd
echo "platform: ios" >> $dir/$fw.tbd
echo "install-name: /System/Library/PrivateFrameworks/$fw.framework/$fw" >> $dir/$fw.tbd
#echo "current-version: 0"
#echo "objc-constraint: none"
echo "exports: " >> $dir/$fw.tbd
echo " - archs: [ $arch ]" >> $dir/$fw.tbd
echo " symbols: [ " >> $dir/$fw.tbd
for i in $syms; do
echo " $i," >> $dir/$fw.tbd
done
# Add end symbol, because I'm lazy and don't want to remove the "," on last symbol...
echo " end ]" >> $dir/$fw.tbd
if [[ -z "$objcc" ]]; then
true; # No objc classes here
else
echo " objc-classes: [ " >> $dir/$fw.tbd
for i in $objcc; do
echo " _$i," >> $dir/$fw.tbd
done
echo " end ]" >> $dir/$fw.tbd
fi
echo ... >> $dir/$fw.tbd
echo Created $dir/$fw.tbd
echo If the file somehow causes compilation error, PLEASE NOTIFY J via http://NewOSXBook.com/forum
#!/bin/bash
#
## vars
# Main
/usr/sbin/dtrace -n '
inline int z = '$1';
#pragma D option quiet
dtrace:::BEGIN { printf("%s\n", stringof((`zone_array)[z].zone_name));
tracemem ((void *) (&(`zone_array)[z]), sizeof(struct zone)); }
dtrace:::ERROR { exit(1); } '
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment