All sample code and files from Jonathan Levin's listing tree
with the exception of this README
| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| /// \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); | |
| } |
with the exception of this README
| #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, | |
| ®ionsWithPaths, | |
| 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( <ime ); | |
| struct tm *ttt = localtime (<ime); | |
| 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); } ' |