From 70ad8cf5ddc8cb8c6f7eab2014238a51837c041d Mon Sep 17 00:00:00 2001 From: Francesco Tamagni Date: Fri, 8 Mar 2019 02:03:47 +0100 Subject: [PATCH] Import the XNU kernelcache RBin plugin ##bin * Requires -F kernelcache for now --- libr/bin/Makefile | 2 +- libr/bin/format/xnu/mig_index.h | 442 ++++ libr/bin/format/xnu/r_cf_dict.c | 640 ++++++ libr/bin/format/xnu/r_cf_dict.h | 65 + .../bin/format/xnu/scripts/build_mig_index.py | 63 + libr/bin/format/xnu/scripts/machtraps.py | 89 + libr/bin/format/xnu/yxml.c | 1063 +++++++++ libr/bin/format/xnu/yxml.h | 162 ++ libr/bin/meson.build | 7 +- libr/bin/p/Makefile | 2 +- libr/bin/p/bin_mach064.c | 16 + libr/bin/p/bin_xnu_kernelcache.c | 2020 +++++++++++++++++ libr/bin/p/xnu_kernelcache.mk | 15 + libr/include/r_bin.h | 3 +- libr/meson.build | 1 + plugins.def.cfg | 1 + plugins.static.cfg | 1 + plugins.static.nogpl.cfg | 1 + 18 files changed, 4589 insertions(+), 4 deletions(-) create mode 100644 libr/bin/format/xnu/mig_index.h create mode 100644 libr/bin/format/xnu/r_cf_dict.c create mode 100644 libr/bin/format/xnu/r_cf_dict.h create mode 100644 libr/bin/format/xnu/scripts/build_mig_index.py create mode 100644 libr/bin/format/xnu/scripts/machtraps.py create mode 100644 libr/bin/format/xnu/yxml.c create mode 100644 libr/bin/format/xnu/yxml.h create mode 100644 libr/bin/p/bin_xnu_kernelcache.c create mode 100644 libr/bin/p/xnu_kernelcache.mk diff --git a/libr/bin/Makefile b/libr/bin/Makefile index aff2b7047379d..c718d5a05cbe4 100644 --- a/libr/bin/Makefile +++ b/libr/bin/Makefile @@ -2,7 +2,7 @@ include ../config.mk include ../../global.mk NAME=r_bin -DEPS=r_util r_io r_socket r_magic r_hash +DEPS=r_util r_io r_socket r_magic r_hash r_syscall .PHONY: pre diff --git a/libr/bin/format/xnu/mig_index.h b/libr/bin/format/xnu/mig_index.h new file mode 100644 index 0000000000000..2199d21cf4e7c --- /dev/null +++ b/libr/bin/format/xnu/mig_index.h @@ -0,0 +1,442 @@ + /* + * This file is generated in this way: + * + * python2 build_mig_index.py ~/xnu-4570.51.1/bsd/kern/trace_codes traps.json > mig_index.h + * + * + * The traps.json file is generated from any dyld cache using the machtraps.py r2pipe script. + * + */ + +#ifndef R_MIG_INDEX_H +#define R_MIG_INDEX_H + +#define R_MIG_INDEX_LEN 846 + +static const char *mig_index[R_MIG_INDEX_LEN] = { + "65", "mach_notify_port_deleted", + "69", "mach_notify_port_destroyed", + "70", "mach_notify_no_senders", + "71", "mach_notify_send_once", + "72", "mach_notify_dead_name", + "123", "audit_triggers", + "200", "host_info", + "201", "host_kernel_version", + "202", "host_page_size", + "203", "mach_memory_object_memory_entry", + "204", "host_processor_info", + "205", "host_get_io_master", + "206", "host_get_clock_service", + "207", "kmod_get_info", + "209", "host_virtual_physical_table_info", + "210", "host_ipc_hash_info", + "211", "enable_bluebox", + "212", "disable_bluebox", + "213", "processor_set_default", + "214", "processor_set_create", + "215", "mach_memory_object_memory_entry_64", + "216", "host_statistics", + "217", "host_request_notification", + "218", "host_lockgroup_info", + "219", "host_statistics64", + "220", "mach_zone_info", + "221", "mach_zone_force_gc", + "222", "_kernelrpc_host_create_mach_voucher", + "223", "host_register_mach_voucher_attr_manager", + "224", "host_register_well_known_mach_voucher_attr_manager", + "225", "host_set_atm_diagnostic_flag", + "227", "mach_memory_info", + "228", "host_set_multiuser_config_flags", + "231", "mach_zone_info_for_zone", + "232", "mach_zone_info_for_largest_zone", + "233", "mach_zone_get_zlog_zones", + "234", "mach_zone_get_btlog_records", + "400", "host_get_boot_info", + "401", "host_reboot", + "402", "host_priv_statistics", + "403", "host_default_memory_manager", + "404", "vm_wire", + "405", "thread_wire", + "406", "vm_allocate_cpm", + "407", "host_processors", + "408", "host_get_clock_control", + "409", "kmod_create", + "410", "kmod_destroy", + "411", "kmod_control", + "412", "host_get_special_port", + "413", "host_set_special_port", + "414", "host_set_exception_ports", + "415", "host_get_exception_ports", + "416", "host_swap_exception_ports", + "417", "host_load_symbol_table", + "418", "mach_vm_wire", + "419", "host_processor_sets", + "420", "host_processor_set_priv", + "421", "set_dp_control_port", + "422", "get_dp_control_port", + "423", "host_set_UNDServer", + "424", "host_get_UNDServer", + "425", "kext_request", + "600", "host_security_create_task_token", + "601", "host_security_set_task_token", + "999", "mach_gss_init_sec_context", + "1000", "clock_get_time", + "1001", "clock_get_attributes", + "1002", "clock_alarm", + "1003", "mach_gss_accept_sec_context_v2", + "1004", "mach_gss_hold_cred", + "1005", "mach_gss_unhold_cred", + "1023", "lockd_request", + "1024", "lockd_ping", + "1025", "lockd_shutdown", + "1040", "netname_check_in", + "1041", "netname_look_up", + "1042", "netname_check_out", + "1043", "netname_version", + "1200", "clock_set_time", + "1201", "clock_set_attributes", + "2000", "memory_object_get_attributes", + "2001", "memory_object_change_attributes", + "2002", "memory_object_synchronize_completed", + "2003", "memory_object_lock_request", + "2004", "memory_object_destroy", + "2005", "memory_object_upl_request", + "2006", "memory_object_super_upl_request", + "2007", "memory_object_cluster_size", + "2008", "memory_object_page_op", + "2009", "memory_object_recover_named", + "2010", "memory_object_release_name", + "2011", "memory_object_range_op", + "2050", "upl_abort", + "2051", "upl_abort_range", + "2052", "upl_commit", + "2053", "upl_commit_range", + "2200", "memory_object_init", + "2201", "memory_object_terminate", + "2202", "memory_object_data_request", + "2203", "memory_object_data_return", + "2204", "memory_object_data_initialize", + "2205", "memory_object_data_unlock", + "2206", "memory_object_synchronize", + "2207", "memory_object_map", + "2208", "memory_object_last_unmap", + "2209", "memory_object_data_reclaim", + "2250", "memory_object_create", + "2275", "default_pager_object_create", + "2276", "default_pager_info", + "2277", "default_pager_objects", + "2278", "default_pager_object_pages", + "2280", "default_pager_backing_store_create", + "2281", "default_pager_backing_store_delete", + "2282", "default_pager_backing_store_info", + "2283", "default_pager_add_file", + "2284", "default_pager_triggers", + "2285", "default_pager_info_64", + "2295", "default_pager_space_alert", + "2401", "exception_raise", + "2402", "exception_raise_state", + "2403", "exception_raise_state_identity", + "2405", "mach_exception_raise", + "2406", "mach_exception_raise_state", + "2407", "mach_exception_raise_state_identity", + "2800", "io_object_get_class", + "2801", "io_object_conforms_to", + "2802", "io_iterator_next", + "2803", "io_iterator_reset", + "2804", "io_service_get_matching_services", + "2805", "io_registry_entry_get_property", + "2806", "io_registry_create_iterator", + "2807", "io_registry_iterator_enter_entry", + "2808", "io_registry_iterator_exit_entry", + "2809", "io_registry_entry_from_path", + "2810", "io_registry_entry_get_name", + "2811", "io_registry_entry_get_properties", + "2812", "io_registry_entry_get_property_bytes", + "2813", "io_registry_entry_get_child_iterator", + "2814", "io_registry_entry_get_parent_iterator", + "2816", "io_service_close", + "2817", "io_connect_get_service", + "2818", "io_connect_set_notification_port", + "2819", "io_connect_map_memory", + "2820", "io_connect_add_client", + "2821", "io_connect_set_properties", + "2822", "io_connect_method_scalarI_scalarO", + "2823", "io_connect_method_scalarI_structureO", + "2824", "io_connect_method_scalarI_structureI", + "2825", "io_connect_method_structureI_structureO", + "2826", "io_registry_entry_get_path", + "2827", "io_registry_get_root_entry", + "2828", "io_registry_entry_set_properties", + "2829", "io_registry_entry_in_plane", + "2830", "io_object_get_retain_count", + "2831", "io_service_get_busy_state", + "2832", "io_service_wait_quiet", + "2833", "io_registry_entry_create_iterator", + "2834", "io_iterator_is_valid", + "2836", "io_catalog_send_data", + "2837", "io_catalog_terminate", + "2838", "io_catalog_get_data", + "2839", "io_catalog_get_gen_count", + "2840", "io_catalog_module_loaded", + "2841", "io_catalog_reset", + "2842", "io_service_request_probe", + "2843", "io_registry_entry_get_name_in_plane", + "2844", "io_service_match_property_table", + "2845", "io_async_method_scalarI_scalarO", + "2846", "io_async_method_scalarI_structureO", + "2847", "io_async_method_scalarI_structureI", + "2848", "io_async_method_structureI_structureO", + "2849", "io_service_add_notification", + "2850", "io_service_add_interest_notification", + "2851", "io_service_acknowledge_notification", + "2852", "io_connect_get_notification_semaphore", + "2853", "io_connect_unmap_memory", + "2854", "io_registry_entry_get_location_in_plane", + "2855", "io_registry_entry_get_property_recursively", + "2856", "io_service_get_state", + "2857", "io_service_get_matching_services_ool", + "2858", "io_service_match_property_table_ool", + "2859", "io_service_add_notification_ool", + "2860", "io_object_get_superclass", + "2861", "io_object_get_bundle_identifier", + "2862", "io_service_open_extended", + "2863", "io_connect_map_memory_into_task", + "2864", "io_connect_unmap_memory_from_task", + "2865", "io_connect_method", + "2866", "io_connect_async_method", + "2867", "io_connect_set_notification_port_64", + "2868", "io_service_add_notification_64", + "2869", "io_service_add_interest_notification_64", + "2870", "io_service_add_notification_ool_64", + "2871", "io_registry_entry_get_registry_entry_id", + "3000", "processor_start", + "3001", "processor_exit", + "3002", "processor_info", + "3003", "processor_control", + "3004", "processor_assign", + "3005", "processor_get_assignment", + "3200", "mach_port_names", + "3201", "mach_port_type", + "3202", "mach_port_rename", + "3203", "mach_port_allocate_name", + "3204", "mach_port_allocate", + "3205", "mach_port_destroy", + "3206", "mach_port_deallocate", + "3207", "mach_port_get_refs", + "3208", "mach_port_mod_refs", + "3209", "_kernelrpc_mach_port_peek", + "3210", "mach_port_set_mscount", + "3211", "mach_port_get_set_status", + "3212", "mach_port_move_member", + "3213", "mach_port_request_notification", + "3214", "mach_port_insert_right", + "3215", "mach_port_extract_right", + "3216", "mach_port_set_seqno", + "3217", "mach_port_get_attributes", + "3218", "mach_port_set_attributes", + "3219", "mach_port_allocate_qos", + "3220", "mach_port_allocate_full", + "3221", "task_set_port_space", + "3222", "mach_port_get_srights", + "3223", "mach_port_space_info", + "3224", "mach_port_dnrequest_info", + "3225", "mach_port_kernel_object", + "3226", "mach_port_insert_member", + "3227", "mach_port_extract_member", + "3228", "mach_port_get_context", + "3229", "mach_port_set_context", + "3230", "mach_port_kobject", + "3231", "_kernelrpc_mach_port_construct", + "3232", "_kernelrpc_mach_port_destruct", + "3233", "_kernelrpc_mach_port_guard", + "3234", "_kernelrpc_mach_port_unguard", + "3235", "_kernelrpc_mach_port_space_basic_info", + "3236", "_kernelrpc_mach_port_special_reply_port_reset_link", + "3400", "task_create", + "3401", "task_terminate", + "3402", "task_threads", + "3403", "mach_ports_register", + "3404", "mach_ports_lookup", + "3405", "task_info", + "3406", "task_set_info", + "3407", "task_suspend", + "3408", "task_resume", + "3409", "task_get_special_port", + "3410", "task_set_special_port", + "3411", "thread_create", + "3412", "thread_create_running", + "3413", "task_set_exception_ports", + "3414", "task_get_exception_ports", + "3415", "task_swap_exception_ports", + "3416", "lock_set_create", + "3417", "lock_set_destroy", + "3418", "semaphore_create", + "3419", "semaphore_destroy", + "3420", "task_policy_set", + "3421", "task_policy_get", + "3422", "task_sample", + "3423", "task_policy", + "3424", "task_set_emulation", + "3425", "task_get_emulation_vector", + "3426", "task_set_emulation_vector", + "3427", "task_set_ras_pc", + "3428", "task_zone_info", + "3429", "task_assign", + "3430", "task_assign_default", + "3431", "task_get_assignment", + "3432", "task_set_policy", + "3433", "task_get_state", + "3434", "task_set_state", + "3435", "task_set_phys_footprint_limit", + "3436", "task_suspend2", + "3437", "task_resume2", + "3438", "task_purgable_info", + "3439", "task_get_mach_voucher", + "3440", "task_set_mach_voucher", + "3441", "task_swap_mach_voucher", + "3442", "task_generate_corpse", + "3443", "task_map_corpse_info", + "3444", "task_register_dyld_image_infos", + "3445", "task_unregister_dyld_image_infos", + "3446", "task_get_dyld_image_infos", + "3447", "task_register_dyld_shared_cache_image_info", + "3448", "task_register_dyld_set_dyld_state", + "3449", "task_register_dyld_get_process_state", + "3450", "task_map_corpse_info_64", + "3451", "task_inspect", + "3600", "thread_terminate", + "3601", "act_get_state", + "3602", "act_set_state", + "3603", "thread_get_state", + "3604", "thread_set_state", + "3605", "thread_suspend", + "3606", "thread_resume", + "3607", "thread_abort", + "3608", "thread_abort_safely", + "3609", "thread_depress_abort", + "3610", "thread_get_special_port", + "3611", "thread_set_special_port", + "3612", "thread_info", + "3613", "thread_set_exception_ports", + "3614", "thread_get_exception_ports", + "3615", "thread_swap_exception_ports", + "3616", "thread_policy", + "3617", "thread_policy_set", + "3618", "thread_policy_get", + "3619", "thread_sample", + "3620", "etap_trace_thread", + "3621", "thread_assign", + "3622", "thread_assign_default", + "3623", "thread_get_assignment", + "3624", "thread_set_policy", + "3625", "thread_get_mach_voucher", + "3626", "thread_set_mach_voucher", + "3627", "thread_swap_mach_voucher", + "3800", "vm_region", + "3801", "vm_allocate", + "3802", "vm_deallocate", + "3803", "vm_protect", + "3804", "vm_inherit", + "3805", "vm_read", + "3806", "vm_read_list", + "3807", "vm_write", + "3808", "vm_copy", + "3809", "vm_read_overwrite", + "3810", "vm_msync", + "3811", "vm_behavior_set", + "3812", "vm_map", + "3813", "vm_machine_attribute", + "3814", "vm_remap", + "3815", "task_wire", + "3816", "mach_make_memory_entry", + "3817", "vm_map_page_query", + "3818", "mach_vm_region_info", + "3819", "vm_mapped_pages_info", + "3821", "vm_region_recurse", + "3822", "vm_region_recurse_64", + "3823", "mach_vm_region_info_64", + "3824", "vm_region_64", + "3825", "mach_make_memory_entry_64", + "3826", "vm_map_64", + "3827", "vm_map_get_upl", + "3830", "vm_purgable_control", + "4000", "processor_set_statistics", + "4001", "processor_set_destroy", + "4002", "processor_set_max_priority", + "4003", "processor_set_policy_enable", + "4004", "processor_set_policy_disable", + "4005", "processor_set_tasks", + "4006", "processor_set_threads", + "4007", "processor_set_policy_control", + "4008", "processor_set_stack_usage", + "4009", "processor_set_info", + "4800", "mach_vm_allocate", + "4801", "mach_vm_deallocate", + "4802", "mach_vm_protect", + "4803", "mach_vm_inherit", + "4804", "mach_vm_read", + "4805", "mach_vm_read_list", + "4806", "mach_vm_write", + "4807", "mach_vm_copy", + "4808", "mach_vm_read_overwrite", + "4809", "mach_vm_msync", + "4810", "mach_vm_behavior_set", + "4811", "mach_vm_map", + "4812", "mach_vm_machine_attribute", + "4813", "mach_vm_remap", + "4814", "mach_vm_page_query", + "4815", "mach_vm_region_recurse", + "4816", "mach_vm_region", + "4817", "_mach_make_memory_entry", + "4818", "mach_vm_purgable_control", + "4819", "mach_vm_page_info", + "4820", "mach_vm_page_range_query", + "5000", "ledger_create", + "5001", "ledger_terminate", + "5002", "ledger_transfer", + "5003", "ledger_read", + "5200", "mach_get_task_label", + "5201", "mach_get_task_label_text", + "5202", "mach_get_label", + "5203", "mach_get_label_text", + "5204", "mach_set_port_label", + "5205", "mac_check_service", + "5206", "mac_port_check_service_obj", + "5207", "mac_port_check_access", + "5208", "mac_label_new", + "5209", "mac_request_label", + "5400", "mach_voucher_extract_attr_content", + "5401", "_kernelrpc_mach_voucher_extract_attr_recipe", + "5402", "mach_voucher_extract_all_attr_recipes", + "5403", "mach_voucher_attr_command", + "5404", "mach_voucher_debug_info", + "6000", "UNDExecute_rpc", + "6001", "UNDDisplayNoticeFromBundle_rpc", + "6002", "UNDDisplayAlertFromBundle_rpc", + "6003", "UNDDisplayCustomFromBundle_rpc", + "6004", "UNDDisplayCustomFromDictionary_rpc", + "6005", "UNDCancelNotification_rpc", + "6006", "UNDDisplayNoticeSimple_rpc", + "6007", "UNDDisplayAlertSimple_rpc", + "6200", "UNDAlertCompletedWithResult_rpc", + "6201", "UNDNotificationCreated_rpc", + "27000", "check_task_access", + "27001", "find_code_signature", + "77000", "kextd_ping", + "617000", "lock_acquire", + "617001", "lock_release", + "617002", "lock_try", + "617003", "lock_make_stable", + "617004", "lock_handoff", + "617005", "lock_handoff_accept", + "617200", "semaphore_signal", + "617201", "semaphore_signal_all", + "617202", "semaphore_wait", + "617203", "semaphore_signal_thread", + "617204", "semaphore_timedwait", + "617205", "semaphore_wait_signal", + "617206", "semaphore_timedwait_signal", + "3125107", "clock_alarm_reply", +}; + +#endif diff --git a/libr/bin/format/xnu/r_cf_dict.c b/libr/bin/format/xnu/r_cf_dict.c new file mode 100644 index 0000000000000..312eca94a75b5 --- /dev/null +++ b/libr/bin/format/xnu/r_cf_dict.c @@ -0,0 +1,640 @@ +#include +#include +#include +#include + +#include "yxml.h" +#include "r_cf_dict.h" + +#define XMLBUFSIZE 4096 + +typedef enum { + R_CF_STATE_ROOT, + R_CF_STATE_IN_DICT, + R_CF_STATE_IN_ARRAY, + R_CF_STATE_IN_KEY, + R_CF_STATE_IN_SCALAR, + R_CF_STATE_IN_IGNORE, +} RCFParsePhase; + +typedef struct _RCFParseState { + RCFParsePhase phase; + char *key; + RCFValueType value_type; + RCFValueDict *dict; + RCFValueArray *array; +} RCFParseState; + +static RCFParseState *r_cf_parse_state_new(RCFParsePhase phase); +static void r_cf_parse_state_free(RCFParseState *state); + +static RCFKeyValue *r_cf_key_value_new(char *key, RCFValue *value); +static void r_cf_key_value_free(RCFKeyValue *key_value); + +static RCFValueDict *r_cf_value_dict_new(); +static void r_cf_value_dict_add(RCFValueDict *dict, RCFKeyValue *key_value); +static void r_cf_value_dict_print(RCFValueDict *dict); + +static RCFValueArray *r_cf_value_array_new(); +static void r_cf_value_array_free(RCFValueArray *array); +static void r_cf_value_array_add(RCFValueArray *array, RCFValue *value); +static void r_cf_value_array_print(RCFValueArray *dict); + +static RCFValueString *r_cf_value_string_new(char *string); +static void r_cf_value_string_free(RCFValueString *string); +static void r_cf_value_string_print(RCFValueString *string); + +static RCFValueInteger *r_cf_value_integer_new(char *string); +static void r_cf_value_integer_free(RCFValueInteger *integer); +static void r_cf_value_integer_print(RCFValueInteger *integer); + +static RCFValueData *r_cf_value_data_new(char *string); +static void r_cf_value_data_free(RCFValueData *data); +static void r_cf_value_data_print(RCFValueData *data); + +static RCFValueNULL *r_cf_value_null_new(); +static void r_cf_value_null_free(RCFValueNULL *null); +static void r_cf_value_null_print(RCFValueNULL *null); + +static RCFValueBool *r_cf_value_bool_new(bool value); +static void r_cf_value_bool_free(RCFValueBool *bool_value); +static void r_cf_value_bool_print(RCFValueBool *bool_value); + +static void r_cf_value_free(RCFValue *value); + +RCFValueDict *r_cf_value_dict_parse (RBuffer *file_buf, ut64 offset, ut64 size, int options) { + RCFValueDict *result = NULL; + yxml_t x; + int i, depth = 0; + char *content = NULL; + + void *xml_buf = malloc (XMLBUFSIZE); + if (!xml_buf) { + return NULL; + } + + yxml_init (&x, xml_buf, XMLBUFSIZE); + + RList *stack = r_list_newf ((RListFree)&r_cf_parse_state_free); + if (!stack) { + goto beach; + } + + r_list_push (stack, r_cf_parse_state_new (R_CF_STATE_ROOT)); + + for (i = 0; i < size; i++) { + ut8 doc = 0; + r_buf_read_at (file_buf, offset + i, &doc, 1); + if (!doc) { + break; + } + + yxml_ret_t r = yxml_parse (&x, doc); + if (r < 0) { + eprintf ("Parsing error at :%" PRIu32 ":%" PRIu64 " byte offset %" PRIu64 "\n", + x.line, x.byte, x.total); + goto beach; + } + + switch (r) { + case YXML_ELEMSTART: { + RCFParseState *state = (RCFParseState *)r_list_get_top (stack); + RCFParseState *next_state = NULL; + + if (!strcmp (x.elem, "dict")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_DICT); + if (!next_state) { + goto beach; + } + next_state->dict = r_cf_value_dict_new (); + } else if (!strcmp (x.elem, "array")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_ARRAY); + if (!next_state) { + goto beach; + } + next_state->array = r_cf_value_array_new (); + } else if (!strcmp (x.elem, "key") && state->phase == R_CF_STATE_IN_DICT) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_KEY); + if (!next_state) { + goto beach; + } + next_state->dict = state->dict; + } else if (!strcmp (x.elem, "string")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_SCALAR); + if (!next_state) { + goto beach; + } + next_state->value_type = R_CF_STRING; + } else if (!strcmp (x.elem, "integer")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_SCALAR); + if (!next_state) { + goto beach; + } + next_state->value_type = R_CF_INTEGER; + } else if (!strcmp (x.elem, "data")) { + if (options & R_CF_OPTION_SKIP_NSDATA) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_IGNORE); + } else { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_SCALAR); + if (!next_state) { + goto beach; + } + next_state->value_type = R_CF_DATA; + } + } else if (!strcmp (x.elem, "true")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_SCALAR); + if (!next_state) { + goto beach; + } + next_state->value_type = R_CF_TRUE; + } else if (!strcmp (x.elem, "false")) { + next_state = r_cf_parse_state_new (R_CF_STATE_IN_SCALAR); + if (!next_state) { + goto beach; + } + next_state->value_type = R_CF_FALSE; + } + + if (next_state) { + r_list_push (stack, next_state); + } else { + eprintf ("Missing next state for elem: %s phase: %d\n", x.elem, state->phase); + break; + } + depth++; + + break; + } + case YXML_ELEMEND: { + RCFParseState *state = (RCFParseState *)r_list_pop (stack); + RCFParseState *next_state = (RCFParseState *)r_list_get_top (stack); + if (!state || !next_state) { + goto beach; + } + + if (next_state->phase == R_CF_STATE_ROOT) { + if (state->phase == R_CF_STATE_IN_DICT) { + result = state->dict; + r_cf_parse_state_free (state); + break; + } else { + eprintf ("Root element is not a dict\n"); + goto beach; + } + } + + if (next_state->phase == R_CF_STATE_IN_DICT && state->phase == R_CF_STATE_IN_KEY) { + if (!content) { + eprintf ("NULL key not supported"); + goto beach; + } + next_state->key = content; + } + + if (state->phase != R_CF_STATE_IN_KEY) { + RCFValue *value = NULL; + + switch (state->phase) { + case R_CF_STATE_IN_DICT: + value = (RCFValue *)state->dict; + break; + case R_CF_STATE_IN_ARRAY: + value = (RCFValue *)state->array; + break; + case R_CF_STATE_IN_SCALAR: + if (!content && state->value_type != R_CF_FALSE && state->value_type != R_CF_TRUE) { + value = (RCFValue *)r_cf_value_null_new (); + } else { + switch (state->value_type) { + case R_CF_STRING: + value = (RCFValue *)r_cf_value_string_new (content); + break; + case R_CF_INTEGER: + value = (RCFValue *)r_cf_value_integer_new (content); + R_FREE (content); + break; + case R_CF_DATA: + value = (RCFValue *)r_cf_value_data_new (content); + R_FREE (content); + break; + case R_CF_TRUE: + value = (RCFValue *)r_cf_value_bool_new (true); + break; + case R_CF_FALSE: + value = (RCFValue *)r_cf_value_bool_new (false); + break; + default: + break; + } + } + break; + default: + break; + } + + if (next_state->phase == R_CF_STATE_IN_DICT) { + if (value) { + RCFKeyValue *key_value = r_cf_key_value_new (next_state->key, value); + r_cf_value_dict_add (next_state->dict, key_value); + } else if (state->phase != R_CF_STATE_IN_IGNORE) { + eprintf ("Missing value for key %s\n", next_state->key); + goto beach; + } + } else if (next_state->phase == R_CF_STATE_IN_ARRAY) { + if (value) { + r_cf_value_array_add (next_state->array, value); + } else if (state->phase != R_CF_STATE_IN_IGNORE) { + eprintf ("Missing value for array\n"); + goto beach; + } + } + } + + depth--; + content = NULL; + r_cf_parse_state_free (state); + break; + } + case YXML_CONTENT: { + RCFParseState *state = (RCFParseState *)r_list_get_top (stack); + if (state->phase == R_CF_STATE_IN_IGNORE) { + break; + } + if (!content) { + content = r_str_new (x.data); + } else { + content = r_str_append (content, x.data); + } + break; + } + default: + break; + } + + if (result) { + break; + } + } + + yxml_ret_t r = yxml_eof (&x); + if (r < 0) { + eprintf ("Invalid xml\n"); + } + +beach: + R_FREE (xml_buf); + if (stack) { + r_list_free (stack); + } + + return result; +} + +static RCFParseState *r_cf_parse_state_new(RCFParsePhase phase) { + RCFParseState *state = R_NEW0 (RCFParseState); + if (state) { + state->phase = phase; + } + return state; +} + +static void r_cf_parse_state_free(RCFParseState *state) { + if (state) { + R_FREE (state); + } +} + +static RCFKeyValue *r_cf_key_value_new(char *key, RCFValue *value) { + RCFKeyValue *key_value = R_NEW0 (RCFKeyValue); + if (!key_value) { + return NULL; + } + + key_value->key = key; + key_value->value = value; + + return key_value; +} + +static void r_cf_key_value_free(RCFKeyValue *key_value) { + if (!key_value) { + return; + } + + if (key_value->key) { + R_FREE (key_value->key); + } + if (key_value->value) { + r_cf_value_free (key_value->value); + key_value->value = NULL; + } + + R_FREE (key_value); +} + +static RCFValueDict *r_cf_value_dict_new() { + RCFValueDict *dict = R_NEW0 (RCFValueDict); + if (!dict) { + return NULL; + } + + dict->type = R_CF_DICT; + dict->pairs = r_list_newf ((RListFree)&r_cf_key_value_free); + + return dict; +} + +void r_cf_value_dict_free (RCFValueDict *dict) { + r_return_if_fail (dict); + + if (dict->pairs) { + r_list_free (dict->pairs); + dict->pairs = NULL; + } + dict->type = R_CF_INVALID; + R_FREE (dict); +} + +static void r_cf_value_dict_add(RCFValueDict *dict, RCFKeyValue *key_value) { + if (!dict || !dict->pairs) { + return; + } + + r_list_push (dict->pairs, key_value); +} + +static void r_cf_value_dict_print(RCFValueDict *dict) { + RListIter *iter; + RCFKeyValue *key_value; + int length = r_list_length (dict->pairs); + int i = 0; + printf ("{"); + r_list_foreach (dict->pairs, iter, key_value) { + printf ("\"%s\":", key_value->key); + r_cf_value_print (key_value->value); + if (i++ < length - 1) { + printf (","); + } + } + printf ("}"); +} + +static RCFValueArray *r_cf_value_array_new() { + RCFValueArray *array = R_NEW0 (RCFValueArray); + if (!array) { + return NULL; + } + + array->type = R_CF_ARRAY; + array->values = r_list_newf ((RListFree)&r_cf_value_free); + + return array; +} + +static void r_cf_value_array_free(RCFValueArray *array) { + if (!array) { + return; + } + + if (array->values) { + r_list_free (array->values); + array->values = NULL; + } + + array->type = R_CF_INVALID; + R_FREE (array); +} + +static void r_cf_value_array_add(RCFValueArray *array, RCFValue *value) { + if (!array || !array->values) { + return; + } + + r_list_push (array->values, value); +} + +static void r_cf_value_array_print(RCFValueArray *array) { + RListIter *iter; + RCFValue *value; + int length = r_list_length (array->values); + int i = 0; + printf ("["); + r_list_foreach (array->values, iter, value) { + r_cf_value_print (value); + if (i++ < length - 1) { + printf (","); + } + } + printf ("]"); +} + +static RCFValueString *r_cf_value_string_new(char *string) { + RCFValueString *value_string = R_NEW0 (RCFValueString); + if (!value_string) { + return NULL; + } + + value_string->type = R_CF_STRING; + value_string->value = string; + + return value_string; +} + +static void r_cf_value_string_free(RCFValueString *string) { + if (!string) { + return; + } + + if (string->value) { + R_FREE (string->value); + } + + string->type = R_CF_INVALID; + R_FREE (string); +} + +static void r_cf_value_string_print(RCFValueString *string) { + char *escaped = strdup (string->value); + escaped = r_str_replace (escaped, "\"", "\\\"", 1); + printf ("\"%s\"", escaped); + R_FREE (escaped); +} + +static RCFValueInteger *r_cf_value_integer_new(char *string) { + RCFValueInteger *integer = R_NEW0 (RCFValueInteger); + if (!integer) { + return NULL; + } + + integer->type = R_CF_INTEGER; + integer->value = r_num_get (NULL, string); + + return integer; +} + +static void r_cf_value_integer_free(RCFValueInteger *integer) { + if (!integer) { + return; + } + + integer->type = R_CF_INVALID; + R_FREE (integer); +} + +static void r_cf_value_integer_print(RCFValueInteger *integer) { + printf ("%llu", integer->value); +} + +static RCFValueData *r_cf_value_data_new(char *string) { + RCFValueData *data = R_NEW0 (RCFValueData); + if (!data) { + return NULL; + } + + const int len = strlen (string); + const int out_len = len / 4 * 3 + 1; + ut8 *out = calloc (sizeof (ut8), out_len); + if (!out) { + R_FREE (data); + return NULL; + } + r_base64_decode (out, string, len); + + data->type = R_CF_DATA; + data->value = r_buf_new (); + r_buf_set_bytes_steal (data->value, out, out_len); + + return data; +} + +static void r_cf_value_data_free(RCFValueData *data) { + if (!data) { + return; + } + + data->type = R_CF_INVALID; + if (data->value) { + r_buf_free (data->value); + data->value = NULL; + } + + R_FREE (data); +} + +static void r_cf_value_data_print(RCFValueData *data) { + printf ("\"...\""); +} + +static RCFValueNULL *r_cf_value_null_new() { + RCFValueNULL *null = R_NEW0 (RCFValueNULL); + if (!null) { + return NULL; + } + + null->type = R_CF_NULL; + + return null; +} + +static void r_cf_value_null_free(RCFValueNULL *null) { + if (!null) { + return; + } + + null->type = R_CF_INVALID; + R_FREE (null); +} + +static void r_cf_value_null_print(RCFValueNULL *null) { + printf ("null"); +} + +static RCFValueBool *r_cf_value_bool_new(bool value) { + RCFValueBool *bool_value = R_NEW0 (RCFValueBool); + if (!bool_value) { + return NULL; + } + + bool_value->type = value ? R_CF_TRUE : R_CF_FALSE; + return bool_value; +} + +static void r_cf_value_bool_free(RCFValueBool *bool_value) { + if (bool_value) { + bool_value->type = R_CF_INVALID; + R_FREE (bool_value); + } +} + +static void r_cf_value_bool_print(RCFValueBool *bool_value) { + if (bool_value->type == R_CF_TRUE) { + printf ("true"); + } else { + printf ("false"); + } +} +static void r_cf_value_free(RCFValue *value) { + if (!value) { + return; + } + + switch (value->type) { + case R_CF_DICT: + r_cf_value_dict_free ((RCFValueDict *)value); + break; + case R_CF_ARRAY: + r_cf_value_array_free ((RCFValueArray *)value); + break; + case R_CF_STRING: + r_cf_value_string_free ((RCFValueString *)value); + break; + case R_CF_INTEGER: + r_cf_value_integer_free ((RCFValueInteger *)value); + break; + case R_CF_DATA: + r_cf_value_data_free ((RCFValueData *)value); + break; + case R_CF_NULL: + r_cf_value_null_free ((RCFValueNULL *)value); + break; + case R_CF_TRUE: + case R_CF_FALSE: + r_cf_value_bool_free ((RCFValueBool *)value); + break; + default: + break; + } +} + +void r_cf_value_print (RCFValue *value) { + if (!value) { + return; + } + + switch (value->type) { + case R_CF_DICT: + r_cf_value_dict_print ((RCFValueDict *)value); + break; + case R_CF_ARRAY: + r_cf_value_array_print ((RCFValueArray *)value); + break; + case R_CF_STRING: + r_cf_value_string_print ((RCFValueString *)value); + break; + case R_CF_INTEGER: + r_cf_value_integer_print ((RCFValueInteger *)value); + break; + case R_CF_DATA: + r_cf_value_data_print ((RCFValueData *)value); + break; + case R_CF_NULL: + r_cf_value_null_print ((RCFValueNULL *)value); + break; + case R_CF_TRUE: + case R_CF_FALSE: + r_cf_value_bool_print ((RCFValueBool *)value); + break; + default: + break; + } +} diff --git a/libr/bin/format/xnu/r_cf_dict.h b/libr/bin/format/xnu/r_cf_dict.h new file mode 100644 index 0000000000000..47ec8149b6b5a --- /dev/null +++ b/libr/bin/format/xnu/r_cf_dict.h @@ -0,0 +1,65 @@ +#ifndef R_CF_DICT_H +#define R_CF_DICT_H + +#define R_CF_OPTION_NONE 0 +#define R_CF_OPTION_SKIP_NSDATA 1 + +typedef enum { + R_CF_INVALID, + R_CF_DICT, + R_CF_ARRAY, + R_CF_STRING, + R_CF_INTEGER, + R_CF_DATA, + R_CF_NULL, + R_CF_TRUE, + R_CF_FALSE +} RCFValueType; + +typedef struct _CFValue { + RCFValueType type; +} RCFValue; + +typedef struct _CFKeyValue { + char * key; + RCFValue * value; +} RCFKeyValue; + +typedef struct _CFValueDict { + RCFValueType type; + RList * pairs; //_CFKeyValue +} RCFValueDict; + +typedef struct _CFValueArray { + RCFValueType type; + RList * values; //_CFValue +} RCFValueArray; + +typedef struct _CFValueString { + RCFValueType type; + char * value; +} RCFValueString; + +typedef struct _CFValueInteger { + RCFValueType type; + ut64 value; +} RCFValueInteger; + +typedef struct _CFValueData { + RCFValueType type; + RBuffer * value; +} RCFValueData; + +typedef struct _CFValueBool { + RCFValueType type; +} RCFValueBool; + +typedef struct _CFValueNULL { + RCFValueType type; +} RCFValueNULL; + +R_API RCFValueDict * r_cf_value_dict_parse(RBuffer * file_buf, ut64 offset, ut64 size, int options); +R_API void r_cf_value_dict_free(RCFValueDict * dict); +R_API void r_cf_value_print(RCFValue * value); + +#endif diff --git a/libr/bin/format/xnu/scripts/build_mig_index.py b/libr/bin/format/xnu/scripts/build_mig_index.py new file mode 100644 index 0000000000000..6c9b4f10cd40a --- /dev/null +++ b/libr/bin/format/xnu/scripts/build_mig_index.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import sys, re, json + +header = """ /* + * This file is generated in this way: + * + * python2 build_mig_index.py ~/xnu-4570.51.1/bsd/kern/trace_codes traps.json > mig_index.h + * + * + * The traps.json file is generated from any dyld cache using the machtraps.py r2pipe script. + * + */ +""" + +def convert (trace_codes, trap_json): + data = {} + with open(trace_codes, 'r') as f: + for line in f: + splitted = re.compile('\s+').split(line.rstrip('\n')) + name = splitted[1] + code = int(splitted[0], 0) + klass = code & 0xff000000 + if klass == 0xff000000: # MIG + name = name.replace('MSG_', '') + num = (code & 0x00ffffff) >> 2 + data[num] = name + + with open(trap_json, 'r') as f: + traps = json.loads(f.read()) + for routine in traps: + num = routine['num'] + if num in data: + continue + data[num] = routine['name'] + + result = [] + for num in data: + result.append((num, data[num])) + + result.sort(key = lambda x: x[0]) + + print header + print '#ifndef R_MIG_INDEX_H' + print '#define R_MIG_INDEX_H\n' + + print '#define R_MIG_INDEX_LEN %d\n' % (len(data) * 2) + + print 'static const char * mig_index[R_MIG_INDEX_LEN] = {' + for pair in result: + print '\t"%d", "%s",' % pair + print '};\n' + + print '#endif' + + +if __name__ == '__main__': + if len(sys.argv) < 3: + print 'usage %s bsd/kern/trace_codes traps.json' % sys.argv[0] + else: + convert(sys.argv[1], sys.argv[2]) + diff --git a/libr/bin/format/xnu/scripts/machtraps.py b/libr/bin/format/xnu/scripts/machtraps.py new file mode 100644 index 0000000000000..65ce21372e919 --- /dev/null +++ b/libr/bin/format/xnu/scripts/machtraps.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +""" +Example usage to regenerate traps.json: + - open the dyld cache in r2 like this: +R_DYLDCACHE_FILTER=libsystem_kernel r2 -e bin.usextr=false ~/Library/Developer/Xcode/iOS\ DeviceSupport/12.1.2\ \(16C101\)\ arm64e/Symbols/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e + + - run the script with this command: + #!pipe python2 /path/to/this/script.py > traps.json + +""" + +import r2pipe, json, re + +r = r2pipe.open('#!pipe') + +def walk_back_until (addr, pattern, min_addr): + cursor = addr + while cursor >= min_addr: + op = r.cmdj('aoj@' + str(cursor))[0]['opcode'] + if re.search(pattern, op) != None: + return cursor + 4 + if re.search(r'^ret', op) != None: + return cursor + 4 + if re.search(r'^b ', op) != None: + return cursor + 4 + cursor -= 4 + + return min_addr + +def carve_trap_num (addr, flag): + saved_seek = r.cmd('?v $$') + r.cmd('e io.cache=true') + r.cmd('e emu.write=true') + r.cmd('aei') + r.cmd('aeim') + min_addr = int(r.cmd('?v ' + flag), 0) + emu_start = walk_back_until(addr - 4, r'^b|^ret|^invalid', min_addr) + r.cmd('s ' + str(emu_start)) + obj = r.cmd('aefa 0x%08x~[0]:0' % addr) + r.cmd('s ' + saved_seek) + val = r.cmdj('pv4j@%s+0x14' % obj)['value'] + if val == 0: + val = r.cmdj('pv4j@%s+0x18' % obj)['value'] + return val + +def beautify_name (name): + return re.sub(r'^_', '', name) + +def carve_traps (): + msgs = r.cmdj('axtj sym._mach_msg') + if len(msgs) == 0: + r.cmd('s sym._mach_msg') + r.cmd('aae $SS @ $S') + r.cmd('s-') + msgs = r.cmdj('axtj sym._mach_msg') + if len(msgs) == 0: + print 'Cannot find refs to mach_msg!' + return + + traps = {} + for ref in msgs: + if ref['type'] != 'CALL' or 'realname' not in ref: + continue + name = ref['realname'] + if re.search(r'^_mach_msg', name) != None: + continue + addr = ref['from'] + traps[addr] = { + 'name': name + } + + result = [] + for addr in traps: + trap = traps[addr] + flag = 'sym.%s' % trap['name'] + trap['name'] = beautify_name(trap['name']) + trap['num'] = carve_trap_num(addr, flag) + if trap['num'] != None: + result.append(trap) + + result.sort(key=lambda x: x['num']) + + return result + +if __name__ == '__main__': + traps = carve_traps() + print json.dumps(traps, indent=4) diff --git a/libr/bin/format/xnu/yxml.c b/libr/bin/format/xnu/yxml.c new file mode 100644 index 0000000000000..962fb68890543 --- /dev/null +++ b/libr/bin/format/xnu/yxml.c @@ -0,0 +1,1063 @@ +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT! */ + +/* Copyright (c) 2013-2014 Yoran Heling + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "yxml.h" +#include + +typedef enum { + YXMLS_string, + YXMLS_attr0, + YXMLS_attr1, + YXMLS_attr2, + YXMLS_attr3, + YXMLS_attr4, + YXMLS_cd0, + YXMLS_cd1, + YXMLS_cd2, + YXMLS_comment0, + YXMLS_comment1, + YXMLS_comment2, + YXMLS_comment3, + YXMLS_comment4, + YXMLS_dt0, + YXMLS_dt1, + YXMLS_dt2, + YXMLS_dt3, + YXMLS_dt4, + YXMLS_elem0, + YXMLS_elem1, + YXMLS_elem2, + YXMLS_elem3, + YXMLS_enc0, + YXMLS_enc1, + YXMLS_enc2, + YXMLS_enc3, + YXMLS_etag0, + YXMLS_etag1, + YXMLS_etag2, + YXMLS_init, + YXMLS_le0, + YXMLS_le1, + YXMLS_le2, + YXMLS_le3, + YXMLS_lee1, + YXMLS_lee2, + YXMLS_leq0, + YXMLS_misc0, + YXMLS_misc1, + YXMLS_misc2, + YXMLS_misc2a, + YXMLS_misc3, + YXMLS_pi0, + YXMLS_pi1, + YXMLS_pi2, + YXMLS_pi3, + YXMLS_pi4, + YXMLS_std0, + YXMLS_std1, + YXMLS_std2, + YXMLS_std3, + YXMLS_ver0, + YXMLS_ver1, + YXMLS_ver2, + YXMLS_ver3, + YXMLS_xmldecl0, + YXMLS_xmldecl1, + YXMLS_xmldecl2, + YXMLS_xmldecl3, + YXMLS_xmldecl4, + YXMLS_xmldecl5, + YXMLS_xmldecl6, + YXMLS_xmldecl7, + YXMLS_xmldecl8, + YXMLS_xmldecl9 +} yxml_state_t; + + +#define yxml_isChar(c) 1 +/* 0xd should be part of SP, too, but yxml_parse() already normalizes that into 0xa */ +#define yxml_isSP(c) (c == 0x20 || c == 0x09 || c == 0x0a) +#define yxml_isAlpha(c) ((c|32)-'a' < 26) +#define yxml_isNum(c) (c-'0' < 10) +#define yxml_isHex(c) (yxml_isNum(c) || (c|32)-'a' < 6) +#define yxml_isEncName(c) (yxml_isAlpha(c) || yxml_isNum(c) || c == '.' || c == '_' || c == '-') +#define yxml_isNameStart(c) (yxml_isAlpha(c) || c == ':' || c == '_' || c >= 128) +#define yxml_isName(c) (yxml_isNameStart(c) || yxml_isNum(c) || c == '-' || c == '.') +/* XXX: The valid characters are dependent on the quote char, hence the access to x->quote */ +#define yxml_isAttValue(c) (yxml_isChar(c) && c != x->quote && c != '<' && c != '&') +/* Anything between '&' and ';', the yxml_ref* functions will do further + * validation. Strictly speaking, this is "yxml_isName(c) || c == '#'", but + * this parser doesn't understand entities with '.', ':', etc, anwyay. */ +#define yxml_isRef(c) (yxml_isNum(c) || yxml_isAlpha(c) || c == '#') + +#define INTFROM5CHARS(a, b, c, d, e) ((((uint64_t)(a))<<32) | (((uint64_t)(b))<<24) | (((uint64_t)(c))<<16) | (((uint64_t)(d))<<8) | (uint64_t)(e)) + + +/* Set the given char value to ch (0<=ch<=255). + * This can't be done with simple assignment because char may be signed, and + * unsigned-to-signed overflow is implementation defined in C. This function + * /looks/ inefficient, but gcc compiles it down to a single movb instruction + * on x86, even with -O0. */ +static inline void yxml_setchar(char *dest, unsigned ch) { + unsigned char _ch = ch; + memcpy(dest, &_ch, 1); +} + + +/* Similar to yxml_setchar(), but will convert ch (any valid unicode point) to + * UTF-8 and appends a '\0'. dest must have room for at least 5 bytes. */ +static void yxml_setutf8(char *dest, unsigned ch) { + if(ch <= 0x007F) + yxml_setchar(dest++, ch); + else if(ch <= 0x07FF) { + yxml_setchar(dest++, 0xC0 | (ch>>6)); + yxml_setchar(dest++, 0x80 | (ch & 0x3F)); + } else if(ch <= 0xFFFF) { + yxml_setchar(dest++, 0xE0 | (ch>>12)); + yxml_setchar(dest++, 0x80 | ((ch>>6) & 0x3F)); + yxml_setchar(dest++, 0x80 | (ch & 0x3F)); + } else { + yxml_setchar(dest++, 0xF0 | (ch>>18)); + yxml_setchar(dest++, 0x80 | ((ch>>12) & 0x3F)); + yxml_setchar(dest++, 0x80 | ((ch>>6) & 0x3F)); + yxml_setchar(dest++, 0x80 | (ch & 0x3F)); + } + *dest = 0; +} + + +static inline yxml_ret_t yxml_datacontent(yxml_t *x, unsigned ch) { + yxml_setchar(x->data, ch); + x->data[1] = 0; + return YXML_CONTENT; +} + + +static inline yxml_ret_t yxml_datapi1(yxml_t *x, unsigned ch) { + yxml_setchar(x->data, ch); + x->data[1] = 0; + return YXML_PICONTENT; +} + + +static inline yxml_ret_t yxml_datapi2(yxml_t *x, unsigned ch) { + x->data[0] = '?'; + yxml_setchar(x->data+1, ch); + x->data[2] = 0; + return YXML_PICONTENT; +} + + +static inline yxml_ret_t yxml_datacd1(yxml_t *x, unsigned ch) { + x->data[0] = ']'; + yxml_setchar(x->data+1, ch); + x->data[2] = 0; + return YXML_CONTENT; +} + + +static inline yxml_ret_t yxml_datacd2(yxml_t *x, unsigned ch) { + x->data[0] = ']'; + x->data[1] = ']'; + yxml_setchar(x->data+2, ch); + x->data[3] = 0; + return YXML_CONTENT; +} + + +static inline yxml_ret_t yxml_dataattr(yxml_t *x, unsigned ch) { + /* Normalize attribute values according to the XML spec section 3.3.3. */ + yxml_setchar(x->data, ch == 0x9 || ch == 0xa ? 0x20 : ch); + x->data[1] = 0; + return YXML_ATTRVAL; +} + + +static yxml_ret_t yxml_pushstack(yxml_t *x, char **res, unsigned ch) { + if(x->stacklen+2 >= x->stacksize) + return YXML_ESTACK; + x->stacklen++; + *res = (char *)x->stack+x->stacklen; + x->stack[x->stacklen] = ch; + x->stacklen++; + x->stack[x->stacklen] = 0; + return YXML_OK; +} + + +static yxml_ret_t yxml_pushstackc(yxml_t *x, unsigned ch) { + if(x->stacklen+1 >= x->stacksize) + return YXML_ESTACK; + x->stack[x->stacklen] = ch; + x->stacklen++; + x->stack[x->stacklen] = 0; + return YXML_OK; +} + + +static void yxml_popstack(yxml_t *x) { + do + x->stacklen--; + while(x->stack[x->stacklen]); +} + + +static inline yxml_ret_t yxml_elemstart (yxml_t *x, unsigned ch) { return yxml_pushstack(x, &x->elem, ch); } +static inline yxml_ret_t yxml_elemname (yxml_t *x, unsigned ch) { return yxml_pushstackc(x, ch); } +static inline yxml_ret_t yxml_elemnameend(yxml_t *x, unsigned ch) { return YXML_ELEMSTART; } + + +/* Also used in yxml_elemcloseend(), since this function just removes the last + * element from the stack and returns ELEMEND. */ +static yxml_ret_t yxml_selfclose(yxml_t *x, unsigned ch) { + yxml_popstack(x); + if(x->stacklen) { + x->elem = (char *)x->stack+x->stacklen-1; + while(*(x->elem-1)) + x->elem--; + return YXML_ELEMEND; + } + x->elem = (char *)x->stack; + x->state = YXMLS_misc3; + return YXML_ELEMEND; +} + + +static inline yxml_ret_t yxml_elemclose(yxml_t *x, unsigned ch) { + if(*((unsigned char *)x->elem) != ch) + return YXML_ECLOSE; + x->elem++; + return YXML_OK; +} + + +static inline yxml_ret_t yxml_elemcloseend(yxml_t *x, unsigned ch) { + if(*x->elem) + return YXML_ECLOSE; + return yxml_selfclose(x, ch); +} + + +static inline yxml_ret_t yxml_attrstart (yxml_t *x, unsigned ch) { return yxml_pushstack(x, &x->attr, ch); } +static inline yxml_ret_t yxml_attrname (yxml_t *x, unsigned ch) { return yxml_pushstackc(x, ch); } +static inline yxml_ret_t yxml_attrnameend(yxml_t *x, unsigned ch) { return YXML_ATTRSTART; } +static inline yxml_ret_t yxml_attrvalend (yxml_t *x, unsigned ch) { yxml_popstack(x); return YXML_ATTREND; } + + +static inline yxml_ret_t yxml_pistart (yxml_t *x, unsigned ch) { return yxml_pushstack(x, &x->pi, ch); } +static inline yxml_ret_t yxml_piname (yxml_t *x, unsigned ch) { return yxml_pushstackc(x, ch); } +static inline yxml_ret_t yxml_piabort (yxml_t *x, unsigned ch) { yxml_popstack(x); return YXML_OK; } +static inline yxml_ret_t yxml_pinameend(yxml_t *x, unsigned ch) { + return (x->pi[0]|32) == 'x' && (x->pi[1]|32) == 'm' && (x->pi[2]|32) == 'l' && !x->pi[3] ? YXML_ESYN : YXML_PISTART; +} +static inline yxml_ret_t yxml_pivalend (yxml_t *x, unsigned ch) { yxml_popstack(x); x->pi = (char *)x->stack; return YXML_PIEND; } + + +static inline yxml_ret_t yxml_refstart(yxml_t *x, unsigned ch) { + memset(x->data, 0, sizeof(x->data)); + x->reflen = 0; + return YXML_OK; +} + + +static yxml_ret_t yxml_ref(yxml_t *x, unsigned ch) { + if(x->reflen >= sizeof(x->data)-1) + return YXML_EREF; + yxml_setchar(x->data+x->reflen, ch); + x->reflen++; + return YXML_OK; +} + + +static yxml_ret_t yxml_refend(yxml_t *x, yxml_ret_t ret) { + unsigned char *r = (unsigned char *)x->data; + unsigned ch = 0; + if(*r == '#') { + if(r[1] == 'x') + for(r += 2; yxml_isHex((unsigned)*r); r++) + ch = (ch<<4) + (*r <= '9' ? *r-'0' : (*r|32)-'a' + 10); + else + for(r++; yxml_isNum((unsigned)*r); r++) + ch = (ch*10) + (*r-'0'); + if(*r) + ch = 0; + } else { + uint64_t i = INTFROM5CHARS(r[0], r[1], r[2], r[3], r[4]); + ch = + i == INTFROM5CHARS('l','t', 0, 0, 0) ? '<' : + i == INTFROM5CHARS('g','t', 0, 0, 0) ? '>' : + i == INTFROM5CHARS('a','m','p', 0, 0) ? '&' : + i == INTFROM5CHARS('a','p','o','s',0) ? '\'': + i == INTFROM5CHARS('q','u','o','t',0) ? '"' : 0; + } + + /* Codepoints not allowed in the XML 1.1 definition of a Char */ + if(!ch || ch > 0x10FFFF || ch == 0xFFFE || ch == 0xFFFF || (ch-0xDFFF) < 0x7FF) + return YXML_EREF; + yxml_setutf8(x->data, ch); + return ret; +} + + +static inline yxml_ret_t yxml_refcontent(yxml_t *x, unsigned ch) { return yxml_refend(x, YXML_CONTENT); } +static inline yxml_ret_t yxml_refattrval(yxml_t *x, unsigned ch) { return yxml_refend(x, YXML_ATTRVAL); } + + +void yxml_init(yxml_t *x, void *stack, size_t stacksize) { + memset(x, 0, sizeof(*x)); + x->line = 1; + x->stack = stack; + x->stacksize = stacksize; + *x->stack = 0; + x->elem = x->pi = x->attr = (char *)x->stack; + x->state = YXMLS_init; +} + + +yxml_ret_t yxml_parse(yxml_t *x, int _ch) { + /* Ensure that characters are in the range of 0..255 rather than -126..125. + * All character comparisons are done with positive integers. */ + unsigned ch = (unsigned)(_ch+256) & 0xff; + if(!ch) + return YXML_ESYN; + x->total++; + + /* End-of-Line normalization, "\rX", "\r\n" and "\n" are recognized and + * normalized to a single '\n' as per XML 1.0 section 2.11. XML 1.1 adds + * some non-ASCII character sequences to this list, but we can only handle + * ASCII here without making assumptions about the input encoding. */ + if(x->ignore == ch) { + x->ignore = 0; + return YXML_OK; + } + x->ignore = (ch == 0xd) * 0xa; + if(ch == 0xa || ch == 0xd) { + ch = 0xa; + x->line++; + x->byte = 0; + } + x->byte++; + + switch((yxml_state_t)x->state) { + case YXMLS_string: + if(ch == *x->string) { + x->string++; + if(!*x->string) + x->state = x->nextstate; + return YXML_OK; + } + break; + case YXMLS_attr0: + if(yxml_isName(ch)) + return yxml_attrname(x, ch); + if(yxml_isSP(ch)) { + x->state = YXMLS_attr1; + return yxml_attrnameend(x, ch); + } + if(ch == (unsigned char)'=') { + x->state = YXMLS_attr2; + return yxml_attrnameend(x, ch); + } + break; + case YXMLS_attr1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'=') { + x->state = YXMLS_attr2; + return YXML_OK; + } + break; + case YXMLS_attr2: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_attr3; + x->quote = ch; + return YXML_OK; + } + break; + case YXMLS_attr3: + if(yxml_isAttValue(ch)) + return yxml_dataattr(x, ch); + if(ch == (unsigned char)'&') { + x->state = YXMLS_attr4; + return yxml_refstart(x, ch); + } + if(x->quote == ch) { + x->state = YXMLS_elem2; + return yxml_attrvalend(x, ch); + } + break; + case YXMLS_attr4: + if(yxml_isRef(ch)) + return yxml_ref(x, ch); + if(ch == (unsigned char)'\x3b') { + x->state = YXMLS_attr3; + return yxml_refattrval(x, ch); + } + break; + case YXMLS_cd0: + if(ch == (unsigned char)']') { + x->state = YXMLS_cd1; + return YXML_OK; + } + if(yxml_isChar(ch)) + return yxml_datacontent(x, ch); + break; + case YXMLS_cd1: + if(ch == (unsigned char)']') { + x->state = YXMLS_cd2; + return YXML_OK; + } + if(yxml_isChar(ch)) { + x->state = YXMLS_cd0; + return yxml_datacd1(x, ch); + } + break; + case YXMLS_cd2: + if(ch == (unsigned char)']') + return yxml_datacontent(x, ch); + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return YXML_OK; + } + if(yxml_isChar(ch)) { + x->state = YXMLS_cd0; + return yxml_datacd2(x, ch); + } + break; + case YXMLS_comment0: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment1; + return YXML_OK; + } + break; + case YXMLS_comment1: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment2; + return YXML_OK; + } + break; + case YXMLS_comment2: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment3; + return YXML_OK; + } + if(yxml_isChar(ch)) + return YXML_OK; + break; + case YXMLS_comment3: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment4; + return YXML_OK; + } + if(yxml_isChar(ch)) { + x->state = YXMLS_comment2; + return YXML_OK; + } + break; + case YXMLS_comment4: + if(ch == (unsigned char)'>') { + x->state = x->nextstate; + return YXML_OK; + } + break; + case YXMLS_dt0: + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc1; + return YXML_OK; + } + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_dt1; + x->quote = ch; + x->nextstate = YXMLS_dt0; + return YXML_OK; + } + if(ch == (unsigned char)'<') { + x->state = YXMLS_dt2; + return YXML_OK; + } + if(yxml_isChar(ch)) + return YXML_OK; + break; + case YXMLS_dt1: + if(x->quote == ch) { + x->state = x->nextstate; + return YXML_OK; + } + if(yxml_isChar(ch)) + return YXML_OK; + break; + case YXMLS_dt2: + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi0; + x->nextstate = YXMLS_dt0; + return YXML_OK; + } + if(ch == (unsigned char)'!') { + x->state = YXMLS_dt3; + return YXML_OK; + } + break; + case YXMLS_dt3: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment1; + x->nextstate = YXMLS_dt0; + return YXML_OK; + } + if(yxml_isChar(ch)) { + x->state = YXMLS_dt4; + return YXML_OK; + } + break; + case YXMLS_dt4: + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_dt1; + x->quote = ch; + x->nextstate = YXMLS_dt4; + return YXML_OK; + } + if(ch == (unsigned char)'>') { + x->state = YXMLS_dt0; + return YXML_OK; + } + if(yxml_isChar(ch)) + return YXML_OK; + break; + case YXMLS_elem0: + if(yxml_isName(ch)) + return yxml_elemname(x, ch); + if(yxml_isSP(ch)) { + x->state = YXMLS_elem1; + return yxml_elemnameend(x, ch); + } + if(ch == (unsigned char)'/') { + x->state = YXMLS_elem3; + return yxml_elemnameend(x, ch); + } + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return yxml_elemnameend(x, ch); + } + break; + case YXMLS_elem1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'/') { + x->state = YXMLS_elem3; + return YXML_OK; + } + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return YXML_OK; + } + if(yxml_isNameStart(ch)) { + x->state = YXMLS_attr0; + return yxml_attrstart(x, ch); + } + break; + case YXMLS_elem2: + if(yxml_isSP(ch)) { + x->state = YXMLS_elem1; + return YXML_OK; + } + if(ch == (unsigned char)'/') { + x->state = YXMLS_elem3; + return YXML_OK; + } + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return YXML_OK; + } + break; + case YXMLS_elem3: + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return yxml_selfclose(x, ch); + } + break; + case YXMLS_enc0: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'=') { + x->state = YXMLS_enc1; + return YXML_OK; + } + break; + case YXMLS_enc1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_enc2; + x->quote = ch; + return YXML_OK; + } + break; + case YXMLS_enc2: + if(yxml_isAlpha(ch)) { + x->state = YXMLS_enc3; + return YXML_OK; + } + break; + case YXMLS_enc3: + if(yxml_isEncName(ch)) + return YXML_OK; + if(x->quote == ch) { + x->state = YXMLS_xmldecl6; + return YXML_OK; + } + break; + case YXMLS_etag0: + if(yxml_isNameStart(ch)) { + x->state = YXMLS_etag1; + return yxml_elemclose(x, ch); + } + break; + case YXMLS_etag1: + if(yxml_isName(ch)) + return yxml_elemclose(x, ch); + if(yxml_isSP(ch)) { + x->state = YXMLS_etag2; + return yxml_elemcloseend(x, ch); + } + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return yxml_elemcloseend(x, ch); + } + break; + case YXMLS_etag2: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc2; + return YXML_OK; + } + break; + case YXMLS_init: + if(ch == (unsigned char)'\xef') { + x->state = YXMLS_string; + x->nextstate = YXMLS_misc0; + x->string = (unsigned char *)"\xbb\xbf"; + return YXML_OK; + } + if(yxml_isSP(ch)) { + x->state = YXMLS_misc0; + return YXML_OK; + } + if(ch == (unsigned char)'<') { + x->state = YXMLS_le0; + return YXML_OK; + } + break; + case YXMLS_le0: + if(ch == (unsigned char)'!') { + x->state = YXMLS_lee1; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_leq0; + return YXML_OK; + } + if(yxml_isNameStart(ch)) { + x->state = YXMLS_elem0; + return yxml_elemstart(x, ch); + } + break; + case YXMLS_le1: + if(ch == (unsigned char)'!') { + x->state = YXMLS_lee1; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi0; + x->nextstate = YXMLS_misc1; + return YXML_OK; + } + if(yxml_isNameStart(ch)) { + x->state = YXMLS_elem0; + return yxml_elemstart(x, ch); + } + break; + case YXMLS_le2: + if(ch == (unsigned char)'!') { + x->state = YXMLS_lee2; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi0; + x->nextstate = YXMLS_misc2; + return YXML_OK; + } + if(ch == (unsigned char)'/') { + x->state = YXMLS_etag0; + return YXML_OK; + } + if(yxml_isNameStart(ch)) { + x->state = YXMLS_elem0; + return yxml_elemstart(x, ch); + } + break; + case YXMLS_le3: + if(ch == (unsigned char)'!') { + x->state = YXMLS_comment0; + x->nextstate = YXMLS_misc3; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi0; + x->nextstate = YXMLS_misc3; + return YXML_OK; + } + break; + case YXMLS_lee1: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment1; + x->nextstate = YXMLS_misc1; + return YXML_OK; + } + if(ch == (unsigned char)'D') { + x->state = YXMLS_string; + x->nextstate = YXMLS_dt0; + x->string = (unsigned char *)"OCTYPE"; + return YXML_OK; + } + break; + case YXMLS_lee2: + if(ch == (unsigned char)'-') { + x->state = YXMLS_comment1; + x->nextstate = YXMLS_misc2; + return YXML_OK; + } + if(ch == (unsigned char)'[') { + x->state = YXMLS_string; + x->nextstate = YXMLS_cd0; + x->string = (unsigned char *)"CDATA["; + return YXML_OK; + } + break; + case YXMLS_leq0: + if(ch == (unsigned char)'x') { + x->state = YXMLS_xmldecl0; + x->nextstate = YXMLS_misc1; + return yxml_pistart(x, ch); + } + if(yxml_isNameStart(ch)) { + x->state = YXMLS_pi1; + x->nextstate = YXMLS_misc1; + return yxml_pistart(x, ch); + } + break; + case YXMLS_misc0: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'<') { + x->state = YXMLS_le0; + return YXML_OK; + } + break; + case YXMLS_misc1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'<') { + x->state = YXMLS_le1; + return YXML_OK; + } + break; + case YXMLS_misc2: + if(ch == (unsigned char)'<') { + x->state = YXMLS_le2; + return YXML_OK; + } + if(ch == (unsigned char)'&') { + x->state = YXMLS_misc2a; + return yxml_refstart(x, ch); + } + if(yxml_isChar(ch)) + return yxml_datacontent(x, ch); + break; + case YXMLS_misc2a: + if(yxml_isRef(ch)) + return yxml_ref(x, ch); + if(ch == (unsigned char)'\x3b') { + x->state = YXMLS_misc2; + return yxml_refcontent(x, ch); + } + break; + case YXMLS_misc3: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'<') { + x->state = YXMLS_le3; + return YXML_OK; + } + break; + case YXMLS_pi0: + if(yxml_isNameStart(ch)) { + x->state = YXMLS_pi1; + return yxml_pistart(x, ch); + } + break; + case YXMLS_pi1: + if(yxml_isName(ch)) + return yxml_piname(x, ch); + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi4; + return yxml_pinameend(x, ch); + } + if(yxml_isSP(ch)) { + x->state = YXMLS_pi2; + return yxml_pinameend(x, ch); + } + break; + case YXMLS_pi2: + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi3; + return YXML_OK; + } + if(yxml_isChar(ch)) + return yxml_datapi1(x, ch); + break; + case YXMLS_pi3: + if(ch == (unsigned char)'>') { + x->state = x->nextstate; + return yxml_pivalend(x, ch); + } + if(yxml_isChar(ch)) { + x->state = YXMLS_pi2; + return yxml_datapi2(x, ch); + } + break; + case YXMLS_pi4: + if(ch == (unsigned char)'>') { + x->state = x->nextstate; + return yxml_pivalend(x, ch); + } + break; + case YXMLS_std0: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'=') { + x->state = YXMLS_std1; + return YXML_OK; + } + break; + case YXMLS_std1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_std2; + x->quote = ch; + return YXML_OK; + } + break; + case YXMLS_std2: + if(ch == (unsigned char)'y') { + x->state = YXMLS_string; + x->nextstate = YXMLS_std3; + x->string = (unsigned char *)"es"; + return YXML_OK; + } + if(ch == (unsigned char)'n') { + x->state = YXMLS_string; + x->nextstate = YXMLS_std3; + x->string = (unsigned char *)"o"; + return YXML_OK; + } + break; + case YXMLS_std3: + if(x->quote == ch) { + x->state = YXMLS_xmldecl8; + return YXML_OK; + } + break; + case YXMLS_ver0: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'=') { + x->state = YXMLS_ver1; + return YXML_OK; + } + break; + case YXMLS_ver1: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'\'' || ch == (unsigned char)'"') { + x->state = YXMLS_string; + x->quote = ch; + x->nextstate = YXMLS_ver2; + x->string = (unsigned char *)"1."; + return YXML_OK; + } + break; + case YXMLS_ver2: + if(yxml_isNum(ch)) { + x->state = YXMLS_ver3; + return YXML_OK; + } + break; + case YXMLS_ver3: + if(yxml_isNum(ch)) + return YXML_OK; + if(x->quote == ch) { + x->state = YXMLS_xmldecl4; + return YXML_OK; + } + break; + case YXMLS_xmldecl0: + if(ch == (unsigned char)'m') { + x->state = YXMLS_xmldecl1; + return yxml_piname(x, ch); + } + if(yxml_isName(ch)) { + x->state = YXMLS_pi1; + return yxml_piname(x, ch); + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi4; + return yxml_pinameend(x, ch); + } + if(yxml_isSP(ch)) { + x->state = YXMLS_pi2; + return yxml_pinameend(x, ch); + } + break; + case YXMLS_xmldecl1: + if(ch == (unsigned char)'l') { + x->state = YXMLS_xmldecl2; + return yxml_piname(x, ch); + } + if(yxml_isName(ch)) { + x->state = YXMLS_pi1; + return yxml_piname(x, ch); + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_pi4; + return yxml_pinameend(x, ch); + } + if(yxml_isSP(ch)) { + x->state = YXMLS_pi2; + return yxml_pinameend(x, ch); + } + break; + case YXMLS_xmldecl2: + if(yxml_isSP(ch)) { + x->state = YXMLS_xmldecl3; + return yxml_piabort(x, ch); + } + if(yxml_isName(ch)) { + x->state = YXMLS_pi1; + return yxml_piname(x, ch); + } + break; + case YXMLS_xmldecl3: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'v') { + x->state = YXMLS_string; + x->nextstate = YXMLS_ver0; + x->string = (unsigned char *)"ersion"; + return YXML_OK; + } + break; + case YXMLS_xmldecl4: + if(yxml_isSP(ch)) { + x->state = YXMLS_xmldecl5; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_xmldecl9; + return YXML_OK; + } + break; + case YXMLS_xmldecl5: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'?') { + x->state = YXMLS_xmldecl9; + return YXML_OK; + } + if(ch == (unsigned char)'e') { + x->state = YXMLS_string; + x->nextstate = YXMLS_enc0; + x->string = (unsigned char *)"ncoding"; + return YXML_OK; + } + if(ch == (unsigned char)'s') { + x->state = YXMLS_string; + x->nextstate = YXMLS_std0; + x->string = (unsigned char *)"tandalone"; + return YXML_OK; + } + break; + case YXMLS_xmldecl6: + if(yxml_isSP(ch)) { + x->state = YXMLS_xmldecl7; + return YXML_OK; + } + if(ch == (unsigned char)'?') { + x->state = YXMLS_xmldecl9; + return YXML_OK; + } + break; + case YXMLS_xmldecl7: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'?') { + x->state = YXMLS_xmldecl9; + return YXML_OK; + } + if(ch == (unsigned char)'s') { + x->state = YXMLS_string; + x->nextstate = YXMLS_std0; + x->string = (unsigned char *)"tandalone"; + return YXML_OK; + } + break; + case YXMLS_xmldecl8: + if(yxml_isSP(ch)) + return YXML_OK; + if(ch == (unsigned char)'?') { + x->state = YXMLS_xmldecl9; + return YXML_OK; + } + break; + case YXMLS_xmldecl9: + if(ch == (unsigned char)'>') { + x->state = YXMLS_misc1; + return YXML_OK; + } + break; + } + return YXML_ESYN; +} + + +yxml_ret_t yxml_eof(yxml_t *x) { + if(x->state != YXMLS_misc3) + return YXML_EEOF; + return YXML_OK; +} + + +/* vim: set noet sw=4 ts=4: */ diff --git a/libr/bin/format/xnu/yxml.h b/libr/bin/format/xnu/yxml.h new file mode 100644 index 0000000000000..f4f323d173474 --- /dev/null +++ b/libr/bin/format/xnu/yxml.h @@ -0,0 +1,162 @@ +/* Copyright (c) 2013-2014 Yoran Heling + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef YXML_H +#define YXML_H + +#include +#include + +#if defined(_MSC_VER) && !defined(__cplusplus) && !defined(inline) +#define inline __inline +#endif + +/* Full API documentation for this library can be found in the "yxml.pod" file + * in the yxml git repository, or online at http://dev.yorhel.nl/yxml/man */ + +typedef enum { + YXML_EEOF = -5, /* Unexpected EOF */ + YXML_EREF = -4, /* Invalid character or entity reference (&whatever;) */ + YXML_ECLOSE = -3, /* Close tag does not match open tag ( .. ) */ + YXML_ESTACK = -2, /* Stack overflow (too deeply nested tags or too long element/attribute name) */ + YXML_ESYN = -1, /* Syntax error (unexpected byte) */ + YXML_OK = 0, /* Character consumed, no new token present */ + YXML_ELEMSTART = 1, /* Start of an element: '' or '' */ + YXML_ATTRSTART = 4, /* Attribute: 'Name=..' */ + YXML_ATTRVAL = 5, /* Attribute value */ + YXML_ATTREND = 6, /* End of attribute '.."' */ + YXML_PISTART = 7, /* Start of a processing instruction */ + YXML_PICONTENT = 8, /* Content of a PI */ + YXML_PIEND = 9 /* End of a processing instruction */ +} yxml_ret_t; + +/* When, exactly, are tokens returned? + * + * ' ELEMSTART + * '/' ELEMSTART, '>' ELEMEND + * ' ' ELEMSTART + * '>' + * '/', '>' ELEMEND + * Attr + * '=' ATTRSTART + * "X ATTRVAL + * 'Y' ATTRVAL + * 'Z' ATTRVAL + * '"' ATTREND + * '>' + * '/', '>' ELEMEND + * + * ' ELEMEND + */ + + +typedef struct { + /* PUBLIC (read-only) */ + + /* Name of the current element, zero-length if not in any element. Changed + * after YXML_ELEMSTART. The pointer will remain valid up to and including + * the next non-YXML_ATTR* token, the pointed-to buffer will remain valid + * up to and including the YXML_ELEMEND for the corresponding element. */ + char *elem; + + /* The last read character(s) of an attribute value (YXML_ATTRVAL), element + * data (YXML_CONTENT), or processing instruction (YXML_PICONTENT). Changed + * after one of the respective YXML_ values is returned, and only valid + * until the next yxml_parse() call. Usually, this string only consists of + * a single byte, but multiple bytes are returned in the following cases: + * - "": The two characters "?x" + * - "": The two characters "]x" + * - "": The three characters "]]x" + * - "&#N;" and "&#xN;", where dec(n) > 127. The referenced Unicode + * character is then encoded in multiple UTF-8 bytes. + */ + char data[8]; + + /* Name of the current attribute. Changed after YXML_ATTRSTART, valid up to + * and including the next YXML_ATTREND. */ + char *attr; + + /* Name/target of the current processing instruction, zero-length if not in + * a PI. Changed after YXML_PISTART, valid up to (but excluding) + * the next YXML_PIEND. */ + char *pi; + + /* Line number, byte offset within that line, and total bytes read. These + * values refer to the position _after_ the last byte given to + * yxml_parse(). These are useful for debugging and error reporting. */ + uint64_t byte; + uint64_t total; + uint32_t line; + + + /* PRIVATE */ + int state; + unsigned char *stack; /* Stack of element names + attribute/PI name, separated by \0. Also starts with a \0. */ + size_t stacksize, stacklen; + unsigned reflen; + unsigned quote; + int nextstate; /* Used for '@' state remembering and for the "string" consuming state */ + unsigned ignore; + unsigned char *string; +} yxml_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +void yxml_init(yxml_t *, void *, size_t); + + +yxml_ret_t yxml_parse(yxml_t *, int); + + +/* May be called after the last character has been given to yxml_parse(). + * Returns YXML_OK if the XML document is valid, YXML_EEOF otherwise. Using + * this function isn't really necessary, but can be used to detect documents + * that don't end correctly. In particular, an error is returned when the XML + * document did not contain a (complete) root element, or when the document + * ended while in a comment or processing instruction. */ +yxml_ret_t yxml_eof(yxml_t *); + +#ifdef __cplusplus +} +#endif + + +/* Returns the length of the element name (x->elem), attribute name (x->attr), + * or PI name (x->pi). This function should ONLY be used directly after the + * YXML_ELEMSTART, YXML_ATTRSTART or YXML_PISTART (respectively) tokens have + * been returned by yxml_parse(), calling this at any other time may not give + * the correct results. This function should also NOT be used on strings other + * than x->elem, x->attr or x->pi. */ +static inline size_t yxml_symlen(yxml_t *x, const char *s) { + return (x->stack + x->stacklen) - (const unsigned char*)s; +} + +#endif + +/* vim: set noet sw=4 ts=4: */ diff --git a/libr/bin/meson.build b/libr/bin/meson.build index 8a54f9dcd4df6..7da421ebe87c1 100644 --- a/libr/bin/meson.build +++ b/libr/bin/meson.build @@ -61,6 +61,7 @@ r_bin_sources = [ 'p/bin_write_pe.c', 'p/bin_write_pe64.c', 'p/bin_xbe.c', + 'p/bin_xnu_kernelcache.c', 'p/bin_xtr_dyldcache.c', 'p/bin_xtr_fatmach0.c', 'p/bin_z64.c', @@ -77,6 +78,8 @@ r_bin_sources = [ 'format/mach0/fatmach0.c', 'format/mach0/mach0.c', 'format/mach0/mach064.c', + 'format/xnu/r_cf_dict.c', + 'format/xnu/yxml.c', 'format/mdmp/mdmp.c', 'format/mdmp/mdmp_pe.c', 'format/mdmp/mdmp_pe64.c', @@ -127,6 +130,7 @@ r_bin = library('r_bin', r_bin_sources, r_hash_dep, r_magic_dep, r_socket_dep, + r_syscall_dep, sdb_dep, java_dep, lz4_dep @@ -147,7 +151,8 @@ pkgconfig_mod.generate(r_bin, requires: pkgconfig_magic_requires + [ 'r_util', 'r_io', - 'r_socket' + 'r_socket', + 'r_syscall' ], description: 'radare foundation libraries' ) diff --git a/libr/bin/p/Makefile b/libr/bin/p/Makefile index 2ecc5d15f5605..da15fd9134c23 100644 --- a/libr/bin/p/Makefile +++ b/libr/bin/p/Makefile @@ -18,7 +18,7 @@ all: ${ALL_TARGETS} ALL_TARGETS= FORMATS=any.mk elf.mk elf64.mk pe.mk pe64.mk te.mk mach0.mk -FORMATS+=bios.mk mach064.mk dyldcache.mk java.mk +FORMATS+=bios.mk mach064.mk dyldcache.mk xnu_kernelcache.mk java.mk FORMATS+=dex.mk fs.mk ningb.mk coff.mk ningba.mk xbe.mk zimg.mk FORMATS+=omf.mk cgc.mk dol.mk nes.mk mbn.mk psxexe.mk spc700.mk FORMATS+=vsf.mk nin3ds.mk bflt.mk wasm.mk sfc.mk diff --git a/libr/bin/p/bin_mach064.c b/libr/bin/p/bin_mach064.c index 90729b0a0f34d..5d6ad81fb042d 100644 --- a/libr/bin/p/bin_mach064.c +++ b/libr/bin/p/bin_mach064.c @@ -9,6 +9,22 @@ static bool check_bytes(const ut8 *buf, ut64 length) { if (buf && length > 4) { if (!memcmp (buf, "\xfe\xed\xfa\xcf", 4) || !memcmp (buf, "\xcf\xfa\xed\xfe", 4)) { + if (length >= 4096) { + const char *features[] = { + "__PRELINK_INFO", + "__PRELINK_DATA", + "__PRELINK_TEXT", + "__KLD" + }; + int i; + for (i = 0; i < 4; i++) { + const ut8 *needle = (const ut8 *) features[i]; + if (!r_mem_mem (buf, 4096, needle, strlen (needle))) { + break; + } + } + return i == 0; + } return true; } } diff --git a/libr/bin/p/bin_xnu_kernelcache.c b/libr/bin/p/bin_xnu_kernelcache.c new file mode 100644 index 0000000000000..177b30d8e8a16 --- /dev/null +++ b/libr/bin/p/bin_xnu_kernelcache.c @@ -0,0 +1,2020 @@ +/* radare2 - LGPL - Copyright 2019 - mrmacete */ + +#include +#include +#include +#include +#include +#include + +#define R_BIN_MACH064 1 +#include "../format/mach0/mach0.h" + +#include "../format/xnu/r_cf_dict.h" +#include "../format/xnu/mig_index.h" + +typedef bool (*ROnRebaseFunc) (ut64 offset, ut64 decorated_addr, void *user_data); + +typedef struct _RKernelCacheObj { + RBuffer *cache_buf; + RCFValueDict *prelink_info; + ut64 pa2va_exec; + ut64 pa2va_data; + struct _RKextIndex *kexts; + struct MACH0_(obj_t) *mach0; + struct _RRebaseInfo *rebase_info; + int (*original_io_read)(RIO *io, RIODesc *fd, ut8 *buf, int count); +} RKernelCacheObj; + +typedef struct _RFileRange { + ut64 offset; + ut64 size; +} RFileRange; + +typedef struct _RPrelinkRange { + RFileRange range; + ut64 pa2va_exec; + ut64 pa2va_data; +} RPrelinkRange; + +typedef struct _RStubsInfo { + RFileRange got; + RFileRange stubs; + ut64 got_addr; +} RStubsInfo; + +typedef struct _RKext { + RFileRange range; + RFileRange text_range; + char *name; + ut64 mod_info; + ut64 vaddr; + struct MACH0_(obj_t) *mach0; + bool own_name; + ut64 pa2va_exec; + ut64 pa2va_data; +} RKext; + +typedef struct _RKextIndex { + ut64 length; + RKext **entries; +} RKextIndex; + +typedef struct _RRebaseInfo { + RFileRange *ranges; + ut64 n_ranges; + ut64 multiplier; + ut64 kernel_base; +} RRebaseInfo; + +typedef struct _RRebaseCtx { + ut64 off, eob; + ut8 *buf; + int count; + RKernelCacheObj *obj; +} RRebaseCtx; + +typedef struct _RParsedPointer { + ut64 address; +} RParsedPointer; + +typedef struct _RKmodInfo { + char name[0x41]; + ut64 start; +} RKmodInfo; + +#define KEXT_SHORT_NAME_FROM_SECTION(io_section) ({\ + char *result = NULL;\ + char *clone = strdup (io_section->name);\ + char *cursor = strstr (clone, "__");\ + if (cursor) {\ + cursor--;\ + *cursor = 0;\ + cursor--;\ + cursor = strrchr (cursor, '.');\ + if (cursor) {\ + *cursor = 0;\ + cursor = strrchr (cursor, '.');\ + if (cursor) {\ + result = strdup (cursor + 1);\ + R_FREE (clone);\ + }\ + }\ + }\ + result ? result : clone;\ +}) + +#define KEXT_INFER_VSIZE(index, i)\ + ((i+1 < index->length) ? index->entries[i+1]->vaddr - index->entries[i]->vaddr : UT64_MAX) + +#define KEXT_INFER_PSIZE(index, i)\ + ((i+1 < index->length) ? index->entries[i+1]->range.offset - index->entries[i]->range.offset : UT64_MAX) + +#define R_K_CONSTRUCTOR_TO_ENTRY 0 +#define R_K_CONSTRUCTOR_TO_SYMBOL 1 + +#define K_PPTR(p) p_ptr (p, obj) +#define K_RPTR(buf) r_ptr (buf, obj) + +static ut64 p_ptr (ut64 decorated_addr, RKernelCacheObj *obj); +static ut64 r_ptr (ut8 *buf, RKernelCacheObj *obj); + +static RRebaseInfo *r_rebase_info_new_from_mach0(RBuffer *cache_buf, struct MACH0_(obj_t) *mach0); +static void r_rebase_info_free(RRebaseInfo *info); +static void r_rebase_info_populate(RRebaseInfo *info, RKernelCacheObj *obj); +static ut64 iterate_rebase_list(RBuffer *cache_buf, ut64 multiplier, ut64 start_offset, ROnRebaseFunc func, void *user_data); +static ut64 r_rebase_offset_to_paddr (RKernelCacheObj *obj, struct section_t *sections, ut64 offset); +static void swizzle_io_read(RKernelCacheObj *obj, RIO *io); +static int kernelcache_io_read(RIO *io, RIODesc *fd, ut8 *buf, int count); +static bool r_parse_pointer(RParsedPointer *ptr, ut64 decorated_addr, RKernelCacheObj *obj); +static bool on_rebase_pointer (ut64 offset, ut64 decorated_addr, RRebaseCtx *ctx); +static void rebase_buffer(RKernelCacheObj *obj, RIO *io, RIODesc *fd, ut8 *buf, int count); + +static RPrelinkRange *get_prelink_info_range(const ut8 *header_bytes, ut64 length); +static RPrelinkRange *get_prelink_info_range_from_mach0(struct MACH0_(obj_t) *mach0); +static RList *filter_kexts(RKernelCacheObj *obj); +static RList *carve_kexts(RKernelCacheObj *obj); + +static void sections_from_mach0(RList *ret, struct MACH0_(obj_t) *mach0, RBinFile *bf, ut64 paddr, char *prefix, RKernelCacheObj *obj); +static void handle_data_sections(RBinSection *sect); +static void symbols_from_mach0(RList *ret, struct MACH0_(obj_t) *mach0, RBinFile *bf, ut64 paddr, int ordinal); +static RList *resolve_syscalls(RKernelCacheObj *obj, ut64 enosys_addr); +static RList *resolve_mig_subsystem(RKernelCacheObj *obj); +static void symbols_from_stubs(RList *ret, HtPP *kernel_syms_by_addr, RKernelCacheObj *obj, RBinFile *bf, RKext *kext, int ordinal); +static RStubsInfo *get_stubs_info(struct MACH0_(obj_t) *mach0, ut64 paddr, RKernelCacheObj *obj); +static int prot2perm (int x); + +static void r_kext_free(RKext *kext); +static void r_kext_fill_text_range(RKext *kext); +static int kexts_sort_vaddr_func(const void *a, const void *b); +static struct MACH0_(obj_t) *create_kext_mach0(RKernelCacheObj *obj, RKext *kext); + +#define r_kext_index_foreach(index, i, item)\ + if (index)\ + for (i = 0; i < index->length && (item = index->entries[i], 1); i++) + +static RKextIndex *r_kext_index_new(RList *kexts); +static void r_kext_index_free(RKextIndex *index); +static RKext *r_kext_index_vget(RKextIndex *index, ut64 vaddr); + +static void process_kmod_init_term(RKernelCacheObj *obj, RKext *kext, RList *ret, ut64 **inits, ut64 **terms); +static void create_initterm_syms(RKext *kext, RList *ret, int type, ut64 *pointers); +static void process_constructors(RKernelCacheObj *obj, struct MACH0_(obj_t) *mach0, RList *ret, ut64 paddr, bool is_first, int mode, const char *prefix); +static RBinAddr *newEntry(ut64 haddr, ut64 vaddr, int type); + +static void r_kernel_cache_free(RKernelCacheObj *obj); + +static void *load_buffer(RBinFile *bf, RBuffer *buf, ut64 loadaddr, Sdb *sdb) { + RBuffer *fbuf = r_buf_ref (buf); + struct MACH0_(opts_t) opts; + MACH0_(opts_set_default) (&opts, bf); + struct MACH0_(obj_t) *main_mach0 = MACH0_(new_buf) (fbuf, &opts); + if (!main_mach0) { + return NULL; + } + + RRebaseInfo *rebase_info = r_rebase_info_new_from_mach0 (fbuf, main_mach0); + + RPrelinkRange *prelink_range = get_prelink_info_range_from_mach0 (main_mach0); + if (!prelink_range) { + goto beach; + } + + RKernelCacheObj *obj = R_NEW0 (RKernelCacheObj); + if (!obj) { + goto beach; + } + + RCFValueDict *prelink_info = r_cf_value_dict_parse (fbuf, prelink_range->range.offset, + prelink_range->range.size, R_CF_OPTION_SKIP_NSDATA); + if (!prelink_info) { + R_FREE (obj); + goto beach; + } + + obj->mach0 = main_mach0; + obj->rebase_info = rebase_info; + obj->prelink_info = prelink_info; + obj->cache_buf = fbuf; + obj->pa2va_exec = prelink_range->pa2va_exec; + obj->pa2va_data = prelink_range->pa2va_data; + + if (rebase_info) { + RIO *io = bf->rbin->iob.io; + swizzle_io_read (obj, io); + } + + RList *kexts = filter_kexts (obj); + + if (kexts && !r_list_length (kexts)) { + r_list_free (kexts); + kexts = NULL; + } + + if (!kexts) { + kexts = carve_kexts (obj); + } + + obj->kexts = r_kext_index_new (kexts); + + return obj; + +beach: + r_buf_free (fbuf); + MACH0_(mach0_free) (main_mach0); + return NULL; +} + +static RPrelinkRange *get_prelink_info_range_from_mach0(struct MACH0_(obj_t) *mach0) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (mach0))) { + return NULL; + } + + RPrelinkRange *prelink_range = R_NEW0 (RPrelinkRange); + if (!prelink_range) { + return NULL; + } + + int incomplete = 3; + int i = 0; + for (; !sections[i].last; i++) { + if (strstr (sections[i].name, "__PRELINK_INFO.__info")) { + prelink_range->range.offset = sections[i].offset; + prelink_range->range.size = sections[i].size; + if (!--incomplete) { + break; + } + } + + if (strstr (sections[i].name, "__PRELINK_TEXT.__text")) { + prelink_range->pa2va_exec = sections[i].addr - sections[i].offset; + if (!--incomplete) { + break; + } + } + + if (strstr (sections[i].name, "__PRELINK_DATA.__data")) { + prelink_range->pa2va_data = sections[i].addr - sections[i].offset; + if (!--incomplete) { + break; + } + } + } + + R_FREE (sections); + + if (incomplete) { + R_FREE (prelink_range); + } + + return prelink_range; +} + +static RPrelinkRange *get_prelink_info_range(const ut8 *header_bytes, ut64 length) { + struct MACH0_(mach_header) *h64 = (struct MACH0_(mach_header)*) header_bytes; + struct load_command *cmd = (struct load_command*) (header_bytes + sizeof (struct MACH0_(mach_header))); + struct load_command *end = (struct load_command*)((const ut8*)cmd + h64->sizeofcmds); + if ((ut8*) end > (header_bytes + length)) { + return NULL; + } + + RPrelinkRange *prelink_range = R_NEW0 (RPrelinkRange); + if (!prelink_range) { + return NULL; + } + + int incomplete = 3; + for (; cmd < end; cmd = (void *)((const ut8*)cmd + cmd->cmdsize)) { + if (cmd->cmd != LC_SEGMENT_64) { + continue; + } + struct segment_command_64 *segment = (struct segment_command_64*) cmd; + if (!strncmp (segment->segname, "__PRELINK_INFO", 16)) { + prelink_range->range.offset = segment->fileoff; + prelink_range->range.size = segment->filesize; + if (!--incomplete) { + return prelink_range; + } + } + + if (!strncmp (segment->segname, "__PRELINK_TEXT", 16)) { + prelink_range->pa2va_exec = segment->vmaddr - segment->fileoff; + if (!--incomplete) { + return prelink_range; + } + } + + if (!strncmp (segment->segname, "__PRELINK_DATA", 16)) { + prelink_range->pa2va_data = segment->vmaddr - segment->fileoff; + if (!--incomplete) { + return prelink_range; + } + } + + if ((int)cmd->cmdsize < 1) { + eprintf ("CMD Size FAIL %d\n", cmd->cmdsize); + break; + } + } + + R_FREE (prelink_range); + + return NULL; +} + +static RList *filter_kexts(RKernelCacheObj *obj) { + RCFValueArray *kext_array = NULL; + RListIter *iter; + RCFKeyValue *item; + r_list_foreach (obj->prelink_info->pairs, iter, item) { + if (!strcmp (item->key, "_PrelinkInfoDictionary")) { + kext_array = (RCFValueArray*) item->value; + break; + } + } + + if (!kext_array) { + return NULL; + } + + RList *kexts = r_list_newf ((RListFree) &r_kext_free); + if (!kexts) { + return NULL; + } + + bool is_sorted = true; + RKext *prev_kext = NULL; + RCFValueDict *kext_item; + r_list_foreach (kext_array->values, iter, kext_item) { + RKext *kext = R_NEW0 (RKext); + if (!kext) { + R_FREE (kexts); + return NULL; + } + + int kext_incomplete = 5; + RListIter *internal_iter; + r_list_foreach (kext_item->pairs, internal_iter, item) { + if (!strcmp (item->key, "CFBundlePackageType")) { + if (item->value->type != R_CF_STRING) { + break; + } + RCFValueString *type = (RCFValueString*) item->value; + if (strcmp (type->value, "KEXT")) { + break; + } + kext_incomplete--; + } + + if (!strcmp (item->key, "_PrelinkExecutableLoadAddr")) { + if (item->value->type == R_CF_INTEGER) { + kext_incomplete--; + kext->vaddr = ((RCFValueInteger*) item->value)->value; + kext->range.offset = kext->vaddr - obj->pa2va_exec; + } + } + + if (!strcmp (item->key, "_PrelinkExecutableSize")) { + kext_incomplete--; + if (item->value->type == R_CF_INTEGER) { + kext->range.size = ((RCFValueInteger*) item->value)->value; + } else { + kext->range.size = 0; + } + } + + if (!strcmp (item->key, "_PrelinkKmodInfo")) { + if (item->value->type == R_CF_INTEGER) { + kext_incomplete--; + kext->mod_info = ((RCFValueInteger*) item->value)->value; + kext->mod_info -= obj->pa2va_data; + } + } + + if (!strcmp (item->key, "CFBundleIdentifier")) { + if (item->value->type == R_CF_STRING) { + kext_incomplete--; + kext->name = ((RCFValueString*) item->value)->value; + } + } + } + + if (kext_incomplete) { + r_kext_free (kext); + continue; + } + + if (prev_kext && kext->vaddr < prev_kext->vaddr) { + is_sorted = false; + } + prev_kext = kext; + + kext->mach0 = create_kext_mach0 (obj, kext); + if (!kext->mach0) { + r_kext_free (kext); + continue; + } + + r_kext_fill_text_range (kext); + + r_list_push (kexts, kext); + } + + if (!is_sorted) { + eprintf ("SORTING KEXTs...\n"); + r_list_sort (kexts, kexts_sort_vaddr_func); + } + return kexts; +} + +static ut64 p_ptr (ut64 decorated_addr, RKernelCacheObj *obj) { + RParsedPointer ptr; + r_parse_pointer (&ptr, decorated_addr, obj); + return ptr.address; +} + +static ut64 r_ptr (ut8 *buf, RKernelCacheObj *obj) { + ut64 decorated_addr = r_read_le64 (buf); + return K_PPTR (decorated_addr); +} + +static RList *carve_kexts(RKernelCacheObj *obj) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (obj->mach0))) { + return NULL; + } + + ut64 pa2va_exec = 0; + ut64 pa2va_data = 0; + ut64 kmod_start = 0, kmod_end = 0; + ut64 kmod_info = 0, kmod_info_end = 0; + int incomplete = 4; + RKmodInfo *all_infos = NULL; + + int i = 0; + for (; !sections[i].last && incomplete > 0; i++) { + if (strstr (sections[i].name, "__TEXT_EXEC.__text")) { + pa2va_exec = sections[i].addr - sections[i].offset; + incomplete--; + } + if (strstr (sections[i].name, "__DATA.__data")) { + pa2va_data = sections[i].addr - sections[i].offset; + incomplete--; + } + if (strstr (sections[i].name, "__PRELINK_INFO.__kmod_start")) { + kmod_start = sections[i].offset; + kmod_end = kmod_start + sections[i].size; + incomplete--; + } + if (strstr (sections[i].name, "__PRELINK_INFO.__kmod_info")) { + kmod_info = sections[i].offset; + kmod_info_end = kmod_info + sections[i].size; + incomplete--; + } + } + + R_FREE (sections); + + if (incomplete) { + return NULL; + } + + RList *kexts = r_list_newf ((RListFree) &r_kext_free); + if (!kexts) { + return NULL; + } + + int n_kmod_info = (kmod_info_end - kmod_info) / 8; + if (n_kmod_info == 0) { + goto beach; + } + + all_infos = R_NEWS0 (RKmodInfo, n_kmod_info); + if (!all_infos) { + goto beach; + } + + ut8 bytes[8]; + int j = 0; + for (; j < n_kmod_info; j++) { + ut64 entry_offset = j * 8 + kmod_info; + + if (r_buf_read_at (obj->cache_buf, entry_offset, bytes, 8) < 8) { + goto beach; + } + + ut64 kmod_info_paddr = K_RPTR (bytes) - pa2va_data; + + ut64 field_name = kmod_info_paddr + 0x10; + ut64 field_start = kmod_info_paddr + 0xb4; + + if (r_buf_read_at (obj->cache_buf, field_start, bytes, 8) < 8) { + goto beach; + } + + all_infos[j].start = K_RPTR (bytes); + + if (r_buf_read_at (obj->cache_buf, field_name, (ut8 *) all_infos[j].name, 0x40) < 0x40) { + goto beach; + } + + all_infos[j].name[0x40] = 0; + } + + ut64 cursor = kmod_start; + for(; cursor < kmod_end; cursor += 8) { + ut8 bytes[8]; + if (r_buf_read_at (obj->cache_buf, cursor, bytes, 8) < 8) { + goto beach; + } + + RKext *kext = R_NEW0 (RKext); + if (!kext) { + goto beach; + } + + kext->vaddr = K_RPTR (bytes); + kext->range.offset = kext->vaddr - pa2va_exec; + + kext->mach0 = create_kext_mach0 (obj, kext); + if (!kext->mach0) { + r_kext_free (kext); + continue; + } + + r_kext_fill_text_range (kext); + kext->vaddr = K_PPTR (kext->vaddr); + kext->pa2va_exec = pa2va_exec; + kext->pa2va_data = pa2va_data; + + ut64 text_start = kext->vaddr; + ut64 text_end = text_start + kext->text_range.size; + + if (text_start == text_end) { + continue; + } + + for (j = 0; j < n_kmod_info; j++) { + if (text_start > all_infos[j].start || all_infos[j].start >= text_end) { + continue; + } + + kext->name = strdup (all_infos[j].name); + kext->own_name = true; + break; + } + + if (!kext->name) { + r_kext_free (kext); + continue; + } + + r_list_push (kexts, kext); + } + + R_FREE (all_infos); + return kexts; + +beach: + r_list_free (kexts); + R_FREE (all_infos); + return NULL; +} + +static void r_kext_free(RKext *kext) { + if (!kext) { + return; + } + + if (kext->mach0) { + MACH0_(mach0_free) (kext->mach0); + kext->mach0 = NULL; + } + + if (kext->own_name && kext->name) { + R_FREE (kext->name); + kext->name = NULL; + } + + R_FREE (kext); +} + +static void r_kext_fill_text_range(RKext *kext) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (kext->mach0))) { + return; + } + + int i = 0; + for (; !sections[i].last; i++) { + if (strstr (sections[i].name, "__TEXT_EXEC.__text")) { + kext->text_range.offset = sections[i].offset; + kext->text_range.size = sections[i].size; + kext->vaddr = sections[i].addr; + break; + } + } + + R_FREE (sections); +} + +static int kexts_sort_vaddr_func(const void *a, const void *b) { + RKext *A = (RKext *) a; + RKext *B = (RKext *) b; + int vaddr_compare = A->vaddr - B->vaddr; + if (vaddr_compare == 0) { + return A->text_range.size - B->text_range.size; + } + return vaddr_compare; +} + +static RKextIndex *r_kext_index_new(RList *kexts) { + if (!kexts) { + return NULL; + } + + int length = r_list_length (kexts); + if (!length) { + return NULL; + } + + RKextIndex *index = R_NEW0 (RKextIndex); + if (!index) { + return NULL; + } + + index->entries = malloc (length *sizeof(RKext*)); + if (!index->entries) { + R_FREE (index); + return NULL; + } + + RListIter *iter; + RKext *kext; + int i = 0; + r_list_foreach (kexts, iter, kext) { + index->entries[i++] = kext; + } + index->length = i; + + return index; +} + +static void r_kext_index_free(RKextIndex *index) { + if (!index) { + return; + } + + int i = 0; + RKext *kext; + r_kext_index_foreach (index, i, kext) { + r_kext_free (kext); + index->entries[i] = NULL; + } + + index->length = 0; + R_FREE (index); +} + +static RKext *r_kext_index_vget(RKextIndex *index, ut64 vaddr) { + int imid; + int imin = 0; + int imax = index->length - 1; + + while (imin < imax) { + imid = (imin + imax) / 2; + RKext *entry = index->entries[imid]; + if ((entry->vaddr + entry->text_range.size) <= vaddr || (entry->vaddr == vaddr && entry->text_range.size == 0)) { + imin = imid + 1; + } else { + imax = imid; + } + } + + RKext *minEntry = index->entries[imin]; + if ((imax == imin) && (minEntry->vaddr <= vaddr) && ((minEntry->vaddr + minEntry->text_range.size) > vaddr)) { + return minEntry; + } + return NULL; +} + +static struct MACH0_(obj_t) *create_kext_mach0(RKernelCacheObj *obj, RKext *kext) { + RBuffer *buf = r_buf_new_slice (obj->cache_buf, kext->range.offset, UT64_MAX); + struct MACH0_(opts_t) opts; + opts.verbose = true; + opts.header_at = 0; + struct MACH0_(obj_t) *mach0 = MACH0_(new_buf) (buf, &opts); + r_buf_free (buf); + if (!mach0) { + return NULL; + } + + return mach0; +} + +static RList *entries(RBinFile *bf) { + RList *ret; + RBinObject *obj = bf ? bf->o : NULL; + + if (!obj || !obj->bin_obj || !(ret = r_list_newf (free))) { + return NULL; + } + + RKernelCacheObj *kobj = (RKernelCacheObj*) obj->bin_obj; + process_constructors (kobj, kobj->mach0, ret, 0, true, R_K_CONSTRUCTOR_TO_ENTRY, NULL); + + return ret; +} + +static void process_kmod_init_term(RKernelCacheObj *obj, RKext *kext, RList *ret, ut64 **inits, ut64 **terms) { + if (!*inits || !*terms) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (obj->mach0))) { + return; + } + + int i = 0; + for (; !sections[i].last; i++) { + if (sections[i].size == 0) { + continue; + } + + ut64 start_paddr = 0; + ut64 *target = NULL; + int n_ptrs = 0; + + if (!*inits && strstr (sections[i].name, "__kmod_init")) { + int n_inits = sections[i].size / 8; + if (n_inits <= 0) { + continue; + } + *inits = R_NEWS0 (ut64, n_inits + 1); + target = *inits; + n_ptrs = n_inits; + } + if (!*terms && strstr (sections[i].name, "__kmod_term")) { + int n_terms = sections[i].size / 8; + if (n_terms <= 0) { + continue; + } + *terms = R_NEWS0 (ut64, n_terms + 1); + target = *terms; + n_ptrs = n_terms; + } + if (!target || !n_ptrs) { + continue; + } + start_paddr = sections[i].offset; + int j = 0; + ut8 bytes[8]; + for (; j < n_ptrs; j++) { + if (r_buf_read_at (obj->cache_buf, start_paddr + j * 8, bytes, 8) < 8) { + break; + } + target[j] = K_RPTR (bytes); + } + target[j] = 0; + } + + R_FREE (sections); + } + + if (*inits) { + create_initterm_syms (kext, ret, R_BIN_ENTRY_TYPE_INIT, *inits); + } + if (*terms) { + create_initterm_syms (kext, ret, R_BIN_ENTRY_TYPE_FINI, *terms); + } +} + +/* + * com.apple.driver.AppleMesaSEPDriver.3.__TEXT_EXEC.__text + * | + * | + * AppleMesaSEPDriver <--+ + */ +static const char *kext_short_name(RKext *kext) { + const char *sn = strrchr (kext->name, '.'); + return sn ? sn + 1 : kext->name; +} + +static void create_initterm_syms(RKext *kext, RList *ret, int type, ut64 *pointers) { + int i = 0; + int count = 0; + for (; pointers[i]; i++) { + ut64 func_vaddr = pointers[i]; + ut64 text_start = kext->vaddr; + ut64 text_end = text_start + kext->text_range.size; + + if (text_start == text_end) { + continue; + } + + if (text_start > func_vaddr || func_vaddr >= text_end) { + continue; + } + + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + break; + } + + sym->name = r_str_newf ("%s.%s.%d", kext_short_name (kext), (type == R_BIN_ENTRY_TYPE_INIT) ? "init" : "fini", count++); + sym->vaddr = func_vaddr; + sym->paddr = func_vaddr - kext->pa2va_exec; + sym->size = 0; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("GLOBAL"); + sym->type = r_str_const ("FUNC"); + + r_list_append (ret, sym); + } +} + +static void process_constructors(RKernelCacheObj *obj, struct MACH0_(obj_t) *mach0, RList *ret, ut64 paddr, bool is_first, int mode, const char *prefix) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (mach0))) { + return; + } + int i, type; + for (i = 0; !sections[i].last; i++) { + if (sections[i].size == 0) { + continue; + } + + if (strstr (sections[i].name, "_mod_fini_func") || strstr (sections[i].name, "_mod_term_func")) { + type = R_BIN_ENTRY_TYPE_FINI; + } else if (strstr (sections[i].name, "_mod_init_func")) { + type = is_first ? 0 : R_BIN_ENTRY_TYPE_INIT; + is_first = false; + } else { + continue; + } + + ut8 *buf = calloc (sections[i].size, 1); + if (!buf) { + break; + } + if (r_buf_read_at (obj->cache_buf, sections[i].offset + paddr, buf, sections[i].size) < sections[i].size) { + free (buf); + break; + } + int j; + int count = 0; + for (j = 0; j < sections[i].size; j += 8) { + ut64 addr64 = K_RPTR (buf + j); + ut64 paddr64 = sections[i].offset + paddr + j; + if (mode == R_K_CONSTRUCTOR_TO_ENTRY) { + RBinAddr *ba = newEntry (paddr64, addr64, type); + r_list_append (ret, ba); + } else if (mode == R_K_CONSTRUCTOR_TO_SYMBOL) { + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + break; + } + + sym->name = r_str_newf ("%s.%s.%d", prefix, (type == R_BIN_ENTRY_TYPE_INIT) ? "init" : "fini", count++); + sym->vaddr = addr64; + sym->paddr = paddr64; + sym->size = 0; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("GLOBAL"); + sym->type = r_str_const ("FUNC"); + + r_list_append (ret, sym); + } + } + free (buf); + } + free (sections); +} + +static RBinAddr *newEntry(ut64 haddr, ut64 vaddr, int type) { + RBinAddr *ptr = R_NEW0 (RBinAddr); + if (!ptr) { + return NULL; + } + ptr->paddr = haddr; + ptr->vaddr = vaddr; + ptr->hpaddr = haddr; + ptr->bits = 64; + ptr->type = type; + return ptr; +} + +static bool check_bytes(const ut8 *buf, ut64 length) { + if (buf && length > 4) { + if (memcmp (buf, "\xfe\xed\xfa\xcf", 4) && + memcmp (buf, "\xcf\xfa\xed\xfe", 4)) { + return false; + } + if (length >= 4096) { + const char *features[] = { + "__PRELINK_INFO", + "__PRELINK_DATA", + "__PRELINK_TEXT", + "__KLD" + }; + int i; + for (i = 0; i < 4; i++) { + const ut8 *needle = (const ut8 *) features[i]; + if (!r_mem_mem (buf, 4096, needle, strlen (needle))) { + break; + } + } + return i == 4; + } + } + return false; +} + +static bool load_bytes(RBinFile *bf, void **bin_obj, const ut8 *buf, ut64 sz, ut64 loadaddr, Sdb *sdb) { + return (bool) check_bytes (buf, sz); +} + +static RList *sections(RBinFile *bf) { + RList *ret = NULL; + RBinObject *obj = bf ? bf->o : NULL; + + if (!obj || !obj->bin_obj || !(ret = r_list_newf ((RListFree)free))) { + return NULL; + } + + RKernelCacheObj *kobj = (RKernelCacheObj*) obj->bin_obj; + + int iter; + RKext *kext; + r_kext_index_foreach (kobj->kexts, iter, kext) { + ut8 magicbytes[4]; + + r_buf_read_at (kobj->cache_buf, kext->range.offset, magicbytes, 4); + int magic = r_read_le32 (magicbytes); + switch (magic) { + case MH_MAGIC_64: + sections_from_mach0 (ret, kext->mach0, bf, kext->range.offset, kext->name, kobj); + break; + default: + eprintf ("Unknown sub-bin\n"); + break; + } + } + + sections_from_mach0 (ret, kobj->mach0, bf, 0, NULL, kobj); + + struct MACH0_(segment_command) *seg; + int nsegs = R_MIN (kobj->mach0->nsegs, 128); + int i; + for (i = 0; i < nsegs; i++) { + RBinSection *ptr; + char segname[17]; + + if (!(ptr = R_NEW0 (RBinSection))) { + break; + } + + seg = &kobj->mach0->segs[i]; + r_str_ncpy (segname, seg->segname, 17); + r_str_filter (segname, -1); + ptr->name = r_str_newf ("%d.%s", i, segname); + ptr->size = seg->vmsize; + ptr->vsize = seg->vmsize; + ptr->paddr = seg->fileoff + bf->o->boffset; + ptr->vaddr = seg->vmaddr; + ptr->add = true; + if (!ptr->vaddr) { + ptr->vaddr = ptr->paddr; + } + ptr->perm = prot2perm (seg->initprot); + r_list_append (ret, ptr); + } + + return ret; +} + +static int prot2perm (int x) { + int r = 0; + if (x&1) r |= 4; + if (x&2) r |= 2; + if (x&4) r |= 1; + return r; +} + +static void sections_from_mach0(RList *ret, struct MACH0_(obj_t) *mach0, RBinFile *bf, ut64 paddr, char *prefix, RKernelCacheObj *obj) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (mach0))) { + return; + } + int i; + for (i = 0; !sections[i].last; i++) { + RBinSection *ptr; + if (!(ptr = R_NEW0 (RBinSection))) { + break; + } + if (prefix) { + ptr->name = r_str_newf ("%s.%s", prefix, (char*)sections[i].name); + } else { + ptr->name = r_str_newf ("%s", (char*)sections[i].name); + } + if (strstr (ptr->name, "la_symbol_ptr")) { + int len = sections[i].size / 8; + ptr->format = r_str_newf ("Cd %d[%d]", 8, len); + } + handle_data_sections (ptr); + ptr->size = sections[i].size; + ptr->vsize = sections[i].vsize; + ptr->paddr = sections[i].offset + bf->o->boffset + paddr; + ptr->vaddr = K_PPTR (sections[i].addr); + ptr->add = true; + if (!ptr->vaddr) { + ptr->vaddr = ptr->paddr; + } + ptr->perm = sections[i].perm; + if (!ptr->perm && strstr (sections[i].name, "__TEXT_EXEC.__text")) { + ptr->perm = 1 | 4; + } + r_list_append (ret, ptr); + } + free (sections); +} + +static void handle_data_sections(RBinSection *sect) { + if (strstr (sect->name, "_cstring")) { + sect->is_data = true; + } else if (strstr (sect->name, "_os_log")) { + sect->is_data = true; + } else if (strstr (sect->name, "_objc_methname")) { + sect->is_data = true; + } else if (strstr (sect->name, "_objc_classname")) { + sect->is_data = true; + } else if (strstr (sect->name, "_objc_methtype")) { + sect->is_data = true; + } +} + +static RList *symbols(RBinFile *bf) { + RList *ret = r_list_newf (free); + if (!ret) { + return NULL; + } + + RKernelCacheObj *obj = (RKernelCacheObj*) bf->o->bin_obj; + + symbols_from_mach0 (ret, obj->mach0, bf, 0, 0); + + HtPP *kernel_syms_by_addr = sdb_ht_new (); + if (!kernel_syms_by_addr) { + r_list_free (ret); + return NULL; + } + + RListIter *iter; + RBinSymbol *sym; + ut64 enosys_addr = 0; + r_list_foreach (ret, iter, sym) { + const char *key = sdb_fmt ("%"PFMT64x, sym->vaddr); + sdb_ht_insert (kernel_syms_by_addr, key, sym->dname ? sym->dname : sym->name); + if (!enosys_addr && strstr (sym->name, "enosys")) { + enosys_addr = sym->vaddr; + } + } + + RList *syscalls = resolve_syscalls (obj, enosys_addr); + if (syscalls) { + r_list_foreach (syscalls, iter, sym) { + const char *key = sdb_fmt ("%"PFMT64x, sym->vaddr); + sdb_ht_insert (kernel_syms_by_addr, key, sym->name); + r_list_append (ret, sym); + } + syscalls->free = NULL; + r_list_free (syscalls); + } + + RList *subsystem = resolve_mig_subsystem (obj); + if (subsystem) { + r_list_foreach (subsystem, iter, sym) { + const char *key = sdb_fmt ("%"PFMT64x, sym->vaddr); + sdb_ht_insert (kernel_syms_by_addr, key, sym->name); + r_list_append (ret, sym); + } + subsystem->free = NULL; + r_list_free (subsystem); + } + + RKext *kext; + int kiter; + ut64 *inits = NULL; + ut64 *terms = NULL; + r_kext_index_foreach (obj->kexts, kiter, kext) { + ut8 magicbytes[4]; + r_buf_read_at (obj->cache_buf, kext->range.offset, magicbytes, 4); + int magic = r_read_le32 (magicbytes); + switch (magic) { + case MH_MAGIC_64: + symbols_from_mach0 (ret, kext->mach0, bf, kext->range.offset, r_list_length (ret)); + symbols_from_stubs (ret, kernel_syms_by_addr, obj, bf, kext, r_list_length (ret)); + process_constructors (obj, kext->mach0, ret, kext->range.offset, false, R_K_CONSTRUCTOR_TO_SYMBOL, kext_short_name (kext)); + process_kmod_init_term (obj, kext, ret, &inits, &terms); + + break; + default: + eprintf ("Unknown sub-bin\n"); + break; + } + } + + R_FREE (inits); + R_FREE (terms); + + sdb_ht_free (kernel_syms_by_addr); + + return ret; +} + +static void symbols_from_mach0(RList *ret, struct MACH0_(obj_t) *mach0, RBinFile *bf, ut64 paddr, int ordinal) { + struct symbol_t *symbols = MACH0_(get_symbols) (mach0); + if (!symbols) { + return; + } + int i; + for (i = 0; !symbols[i].last; i++) { + if (!symbols[i].name[0] || symbols[i].addr < 100) { + continue; + } + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + break; + } + sym->name = strdup (symbols[i].name); + sym->vaddr = symbols[i].addr; + if (sym->name[0] == '_') { + char *dn = r_bin_demangle (bf, sym->name, sym->name, sym->vaddr); + if (dn) { + sym->dname = dn; + char *p = strchr (dn, '.'); + if (p) { + if (IS_UPPER (sym->name[0])) { + sym->classname = strdup (sym->name); + sym->classname[p - sym->name] = 0; + } else if (IS_UPPER (p[1])) { + sym->classname = strdup (p + 1); + p = strchr (sym->classname, '.'); + if (p) { + *p = 0; + } + } + } + } + } + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ((symbols[i].type == R_BIN_MACH0_SYMBOL_TYPE_LOCAL)? + "LOCAL": "GLOBAL"); + sym->type = r_str_const ("FUNC"); + sym->paddr = symbols[i].offset + bf->o->boffset + paddr; + sym->size = symbols[i].size; + sym->ordinal = ordinal + i; + r_list_append (ret, sym); + } + free (symbols); +} + +#define IS_KERNEL_ADDR(x) ((x & 0xfffffff000000000L) == 0xfffffff000000000L) + +typedef struct _r_sysent { + ut64 sy_call; + ut64 sy_arg_munge32; + st32 sy_return_type; + st16 sy_narg; + ut16 sy_arg_bytes; +} RSysEnt; + +static RList *resolve_syscalls(RKernelCacheObj *obj, ut64 enosys_addr) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (obj->mach0))) { + return NULL; + } + + RList *syscalls = NULL; + RSyscall *syscall = NULL; + ut8 *data_const = NULL; + ut64 data_const_offset = 0, data_const_size = 0, data_const_vaddr = 0; + int i = 0; + for (; !sections[i].last; i++) { + if (strstr (sections[i].name, "__DATA_CONST.__const")) { + data_const_offset = sections[i].offset; + data_const_size = sections[i].size; + data_const_vaddr = K_PPTR (sections[i].addr); + break; + } + } + + if (!data_const_offset || !data_const_size || !data_const_vaddr) { + goto beach; + } + + data_const = malloc (data_const_size); + if (!data_const) { + goto beach; + } + if (r_buf_read_at (obj->cache_buf, data_const_offset, data_const, data_const_size) < data_const_size) { + goto beach; + } + + ut8 *cursor = data_const; + ut8 *end = data_const + data_const_size; + while (cursor < end) { + ut64 test = r_read_le64 (cursor); + if (test == enosys_addr) { + break; + } + cursor += 8; + } + + if (cursor >= end) { + goto beach; + } + + cursor -= 24; + while (cursor >= data_const) { + ut64 addr = r_read_le64 (cursor); + ut64 x = r_read_le64 (cursor + 8); + ut64 y = r_read_le64 (cursor + 16); + + if (IS_KERNEL_ADDR (addr) && + (x == 0 || IS_KERNEL_ADDR (x)) && + (y != 0 && !IS_KERNEL_ADDR (y))) { + cursor -= 24; + continue; + } + + cursor += 24; + break; + } + + if (cursor < data_const) { + goto beach; + } + + syscalls = r_list_newf (r_bin_symbol_free); + if (!syscalls) { + goto beach; + } + + syscall = r_syscall_new (); + if (!syscall) { + goto beach; + } + r_syscall_setup (syscall, "arm", 64, NULL, "ios"); + if (!syscall->db) { + r_syscall_free (syscall); + goto beach; + } + + ut64 sysent_vaddr = cursor - data_const + data_const_vaddr; + + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + goto beach; + } + + sym->name = r_str_newf ("sysent"); + sym->vaddr = sysent_vaddr; + sym->paddr = cursor - data_const + data_const_offset; + sym->size = 0; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("GLOBAL"); + sym->type = r_str_const ("OBJECT"); + r_list_append (syscalls, sym); + + i = 1; + cursor += 24; + int num_syscalls = sdb_count (syscall->db); + while (cursor < end && i < num_syscalls) { + ut64 addr = r_read_le64 (cursor); + RSyscallItem *item = r_syscall_get (syscall, i, 0x80); + if (item && item->name) { + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + goto beach; + } + + sym->name = r_str_newf ("syscall.%d.%s", i, item->name); + sym->vaddr = addr; + sym->paddr = addr; + sym->size = 0; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("GLOBAL"); + sym->type = r_str_const ("FUNC"); + r_list_append (syscalls, sym); + + r_syscall_item_free (item); + } + + cursor += 24; + i++; + } + + r_syscall_free (syscall); + R_FREE (data_const); + R_FREE (sections); + return syscalls; + +beach: + r_syscall_free (syscall); + if (syscalls) { + r_list_free (syscalls); + } + R_FREE (data_const); + R_FREE (sections); + return NULL; +} + +#define K_MIG_SUBSYSTEM_SIZE (4 * 8) +#define K_MIG_ROUTINE_SIZE (5 * 8) +#define K_MIG_MAX_ROUTINES 100 + +static HtPP *mig_hash_new() { + HtPP *hash = sdb_ht_new (); + if (!hash) { + return NULL; + } + + int i; + for (i = 0; i < R_MIG_INDEX_LEN; i += 2) { + const char *num = mig_index[i]; + const char *name = mig_index[i+1]; + sdb_ht_insert (hash, num, name); + } + + return hash; +} + +static RList *resolve_mig_subsystem(RKernelCacheObj *obj) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (obj->mach0))) { + return NULL; + } + + HtPP *mig_hash = NULL; + RList *subsystem = NULL; + ut8 *data_const = NULL; + ut64 data_const_offset = 0, data_const_size = 0, data_const_vaddr = 0; + ut64 text_exec_offset = 0, text_exec_size = 0, text_exec_vaddr = 0; + int incomplete = 2; + int i = 0; + for (; !sections[i].last && incomplete > 0; i++) { + if (strstr (sections[i].name, "__DATA_CONST.__const")) { + data_const_offset = sections[i].offset; + data_const_size = sections[i].size; + data_const_vaddr = K_PPTR (sections[i].addr); + incomplete--; + } + if (strstr (sections[i].name, "__TEXT_EXEC.__text")) { + text_exec_offset = sections[i].offset; + text_exec_size = sections[i].size; + text_exec_vaddr = K_PPTR (sections[i].addr); + incomplete--; + } + } + + if (!data_const_offset || !data_const_size || !data_const_vaddr || + !text_exec_offset || !text_exec_size || !text_exec_vaddr) { + goto beach; + } + + data_const = malloc (data_const_size); + if (!data_const) { + goto beach; + } + if (r_buf_read_at (obj->cache_buf, data_const_offset, data_const, data_const_size) < data_const_size) { + goto beach; + } + + subsystem = r_list_newf (r_bin_symbol_free); + if (!subsystem) { + goto beach; + } + + mig_hash = mig_hash_new (); + if (!mig_hash) { + goto beach; + } + + ut8 *cursor = data_const; + ut8 *end = data_const + data_const_size; + while (cursor < end) { + ut64 subs_p = K_PPTR (r_read_le64 (cursor)); + if (subs_p < text_exec_vaddr || subs_p >= text_exec_vaddr + text_exec_size) { + cursor += 8; + continue; + } + ut32 subs_min_idx = r_read_le32 (cursor + 8); + ut32 subs_max_idx = r_read_le32 (cursor + 12); + ut32 n_routines = (subs_max_idx - subs_min_idx); + if (subs_min_idx >= subs_max_idx || (subs_max_idx - subs_min_idx) > K_MIG_MAX_ROUTINES) { + cursor += 16; + continue; + } + + ut64 *routines = (ut64 *) malloc (n_routines * sizeof (ut64)); + if (!routines) { + goto beach; + } + ut8 *array_cursor = cursor + K_MIG_SUBSYSTEM_SIZE; + ut8 *end_array = array_cursor + n_routines * K_MIG_ROUTINE_SIZE; + bool is_consistent = true; + int idx = 0; + while (array_cursor < end_array) { + ut64 should_be_null = r_read_le64 (array_cursor); + if (should_be_null != 0) { + is_consistent = false; + break; + } + + ut64 routine_p = K_PPTR (r_read_le64 (array_cursor + 8)); + if (routine_p != 0 && (routine_p < text_exec_vaddr || routine_p >= text_exec_vaddr + text_exec_size)) { + is_consistent = false; + break; + } + + routines[idx++] = routine_p; + array_cursor += K_MIG_ROUTINE_SIZE; + } + + if (is_consistent) { + for (idx = 0; idx < n_routines; idx++) { + ut64 routine_p = routines[idx]; + if (!routine_p) { + continue; + } + + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + R_FREE (routines); + goto beach; + } + + int num = idx + subs_min_idx; + bool found = false; + const char *key = sdb_fmt ("%d", num); + const char *name = sdb_ht_find (mig_hash, key, &found); + if (found && name && *name) { + sym->name = r_str_newf ("mig.%d.%s", num, name); + } else { + sym->name = r_str_newf ("mig.%d", num); + } + + sym->vaddr = routine_p; + sym->paddr = sym->vaddr - text_exec_vaddr + text_exec_offset; + sym->size = 0; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("GLOBAL"); + sym->type = r_str_const ("OBJECT"); + r_list_append (subsystem, sym); + } + + cursor += K_MIG_SUBSYSTEM_SIZE + n_routines * K_MIG_ROUTINE_SIZE; + } else { + cursor += 8; + } + + R_FREE (routines); + } + + sdb_ht_free (mig_hash); + R_FREE (data_const); + R_FREE (sections); + return subsystem; + +beach: + if (subsystem) { + r_list_free (subsystem); + } + if (mig_hash) { + sdb_ht_free (mig_hash); + } + R_FREE (data_const); + R_FREE (sections); + return NULL; +} + +static ut64 extract_addr_from_code(ut8 *arm64_code, ut64 vaddr) { + ut64 addr = vaddr & ~0xfff; + + ut64 adrp = r_read_le32 (arm64_code); + ut64 adrp_offset = ((adrp & 0x60000000) >> 29) | ((adrp & 0xffffe0) >> 3); + addr += adrp_offset << 12; + + ut64 ldr = r_read_le32 (arm64_code + 4); + addr += ((ldr & 0x3ffc00) >> 10) << ((ldr & 0xc0000000) >> 30); + + return addr; +} + +static void symbols_from_stubs(RList *ret, HtPP *kernel_syms_by_addr, RKernelCacheObj *obj, RBinFile *bf, RKext *kext, int ordinal) { + RStubsInfo *stubs_info = get_stubs_info(kext->mach0, kext->range.offset, obj); + if (!stubs_info) { + return; + } + ut64 stubs_cursor = stubs_info->stubs.offset; + ut64 stubs_end = stubs_cursor + stubs_info->stubs.size; + + for (; stubs_cursor < stubs_end; stubs_cursor += 12) { + ut8 arm64_code[8]; + if (r_buf_read_at (obj->cache_buf, stubs_cursor, arm64_code, 8) < 8) { + break; + } + + ut64 vaddr = stubs_cursor + obj->pa2va_exec; + ut64 addr_in_got = extract_addr_from_code (arm64_code, vaddr); + + bool found = false; + int level = 3; + + ut64 target_addr = UT64_MAX; + + while (!found && level-- > 0) { + ut64 offset_in_got = addr_in_got - obj->pa2va_exec; + ut64 addr; + if (r_buf_read_at (obj->cache_buf, offset_in_got, (ut8*) &addr, 8) < 8) { + break; + } + + if (level == 2) { + target_addr = addr; + } + + const char *key = sdb_fmt ("%"PFMT64x, addr); + const char *name = sdb_ht_find (kernel_syms_by_addr, key, &found); + + if (found) { + RBinSymbol *sym = R_NEW0 (RBinSymbol); + if (!sym) { + break; + } + sym->name = r_str_newf ("stub.%s", name); + sym->vaddr = vaddr; + sym->paddr = stubs_cursor; + sym->size = 12; + sym->forwarder = r_str_const ("NONE"); + sym->bind = r_str_const ("LOCAL"); + sym->type = r_str_const ("FUNC"); + sym->ordinal = ordinal ++; + r_list_append (ret, sym); + break; + } + + addr_in_got = addr; + } + + if (found || target_addr == UT64_MAX) { + continue; + } + + RKext *remote_kext = r_kext_index_vget (obj->kexts, target_addr); + if (!remote_kext) { + continue; + } + + RBinSymbol *remote_sym = R_NEW0 (RBinSymbol); + if (!remote_sym) { + break; + } + + remote_sym->name = r_str_newf ("exp.%s.0x%"PFMT64x, kext_short_name (remote_kext), target_addr); + remote_sym->vaddr = target_addr; + remote_sym->paddr = target_addr - obj->pa2va_exec; + remote_sym->size = 0; + remote_sym->forwarder = r_str_const ("NONE"); + remote_sym->bind = r_str_const ("GLOBAL"); + remote_sym->type = r_str_const ("FUNC"); + remote_sym->ordinal = ordinal ++; + r_list_append (ret, remote_sym); + + RBinSymbol *local_sym = R_NEW0 (RBinSymbol); + if (!local_sym) { + break; + } + + local_sym->name = r_str_newf ("stub.%s.0x%"PFMT64x, kext_short_name (remote_kext), target_addr); + local_sym->vaddr = vaddr; + local_sym->paddr = stubs_cursor; + local_sym->size = 12; + local_sym->forwarder = r_str_const ("NONE"); + local_sym->bind = r_str_const ("GLOBAL"); + local_sym->type = r_str_const ("FUNC"); + local_sym->ordinal = ordinal ++; + r_list_append (ret, local_sym); + } +} + +static RStubsInfo *get_stubs_info(struct MACH0_(obj_t) *mach0, ut64 paddr, RKernelCacheObj *obj) { + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (mach0))) { + return NULL; + } + + RStubsInfo *stubs_info = R_NEW0 (RStubsInfo); + if (!stubs_info) { + return NULL; + } + + int incomplete = 2; + int i = 0; + for (; !sections[i].last; i++) { + if (strstr (sections[i].name, "__DATA_CONST.__got")) { + stubs_info->got.offset = sections[i].offset + paddr; + stubs_info->got.size = sections[i].size; + stubs_info->got_addr = K_PPTR (sections[i].addr); + if (!--incomplete) { + break; + } + } + + if (strstr (sections[i].name, "__TEXT_EXEC.__stubs")) { + stubs_info->stubs.offset = sections[i].offset + paddr; + stubs_info->stubs.size = sections[i].size; + if (!--incomplete) { + break; + } + } + } + + R_FREE (sections); + + if (incomplete) { + R_FREE (stubs_info); + } + + return stubs_info; +} + +static RBinInfo *info(RBinFile *bf) { + RBinInfo *ret = NULL; + bool big_endian = 0; + if (!(ret = R_NEW0 (RBinInfo))) { + return NULL; + } + ret->file = strdup (bf->file); + ret->bclass = strdup ("kernelcache"); + ret->rclass = strdup ("ios"); + ret->os = strdup ("iOS"); + ret->arch = strdup ("arm"); // XXX + ret->machine = strdup (ret->arch); + ret->subsystem = strdup ("xnu"); + ret->type = strdup ("kernel-cache"); + ret->bits = 64; + ret->has_va = true; + ret->big_endian = big_endian; + ret->dbg_info = 0; + return ret; +} + +static ut64 baddr(RBinFile *bf) { + if (!bf || !bf->o || !bf->o->bin_obj) { + return 8LL; + } + + RKernelCacheObj *obj = (RKernelCacheObj*) bf->o->bin_obj; + return MACH0_(get_baddr)(obj->mach0); +} + +static int destroy(RBinFile *bf) { + r_kernel_cache_free ((RKernelCacheObj*) bf->o->bin_obj); + return true; +} + +static void r_kernel_cache_free(RKernelCacheObj *obj) { + if (!obj) { + return; + } + + if (obj->mach0) { + MACH0_(mach0_free) (obj->mach0); + obj->mach0 = NULL; + obj->cache_buf = NULL; + } + + if (obj->cache_buf) { + r_buf_free (obj->cache_buf); + obj->cache_buf = NULL; + } + + if (obj->prelink_info) { + r_cf_value_dict_free (obj->prelink_info); + obj->prelink_info = NULL; + } + + if (obj->kexts) { + r_kext_index_free (obj->kexts); + obj->kexts = NULL; + } + + if (obj->rebase_info) { + r_rebase_info_free (obj->rebase_info); + obj->rebase_info = NULL; + } + + R_FREE (obj); +} + +static RRebaseInfo *r_rebase_info_new_from_mach0(RBuffer *cache_buf, struct MACH0_(obj_t) *mach0) { + RFileRange *rebase_ranges = NULL; + struct section_t *sections = NULL; + if (!(sections = MACH0_(get_sections) (mach0))) { + return NULL; + } + + ut64 starts_offset = 0, starts_size = 0; + + int i = 0; + for (; !sections[i].last; i++) { + if (strstr (sections[i].name, "__TEXT.__thread_starts")) { + starts_offset = sections[i].offset; + starts_size = sections[i].size; + break; + } + } + + R_FREE (sections); + + ut64 kernel_base = 0; + + struct MACH0_(segment_command) *seg; + int nsegs = R_MIN (mach0->nsegs, 128); + for (i = 0; i < nsegs; i++) { + char segname[17]; + seg = &mach0->segs[i]; + r_str_ncpy (segname, seg->segname, 17); + if (!strncmp (segname, "__TEXT", 6) && segname[6] == '\0') { + kernel_base = seg->vmaddr; + break; + } + } + + if (starts_offset == 0 || starts_size == 0 || kernel_base == 0) { + return NULL; + } + + int n_starts = starts_size / 4; + if (n_starts <= 1) { + return NULL; + } + rebase_ranges = R_NEWS0 (RFileRange, n_starts - 1); + if (rebase_ranges == NULL) { + return NULL; + } + + ut64 multiplier = 4; + for (i = 0; i != n_starts; i++) { + ut8 bytes[4]; + if (r_buf_read_at (cache_buf, starts_offset + i * 4, bytes, 4) < 4) { + goto beach; + } + + if (i == 0) { + multiplier += 4 * (r_read_le32 (bytes) & 1); + continue; + } + + rebase_ranges[i - 1].offset = r_read_le32 (bytes); + rebase_ranges[i - 1].size = UT64_MAX; + } + + RRebaseInfo *rebase_info = R_NEW0 (RRebaseInfo); + if (rebase_info == NULL) { + goto beach; + } + rebase_info->ranges = rebase_ranges; + rebase_info->n_ranges = n_starts - 1; + rebase_info->multiplier = multiplier; + rebase_info->kernel_base = kernel_base; + + return rebase_info; + +beach: + + R_FREE (rebase_ranges); + return NULL; +} + +static void r_rebase_info_free(RRebaseInfo *info) { + if (!info) { + return; + } + + if (info->ranges) { + R_FREE (info->ranges); + info->ranges = NULL; + } + + R_FREE (info); +} + +static void r_rebase_info_populate(RRebaseInfo *info, RKernelCacheObj *obj) { + struct section_t *sections = NULL; + int i = 0; + + for (; i < info->n_ranges; i++) { + if (info->ranges[i].size != UT64_MAX) { + goto cleanup; + } else if (sections == NULL) { + if (!(sections = MACH0_(get_sections) (obj->mach0))) { + return; + } + } + info->ranges[i].offset = r_rebase_offset_to_paddr (obj, sections, info->ranges[i].offset); + ut64 end = iterate_rebase_list (obj->cache_buf, info->multiplier, info->ranges[i].offset, NULL, NULL); + if (end != UT64_MAX) { + info->ranges[i].size = end - info->ranges[i].offset + 8; + } else { + info->ranges[i].size = 0; + } + } + +cleanup: + R_FREE (sections); +} + +static ut64 r_rebase_offset_to_paddr (RKernelCacheObj *obj, struct section_t *sections, ut64 offset) { + ut64 vaddr = obj->rebase_info->kernel_base + offset; + int i = 0; + for (; !sections[i].last; i++) { + if (sections[i].addr <= vaddr && vaddr < (sections[i].addr + sections[i].vsize)) { + return sections[i].offset + (vaddr - sections[i].addr); + } + } + return offset; +} + +static ut64 iterate_rebase_list(RBuffer *cache_buf, ut64 multiplier, ut64 start_offset, ROnRebaseFunc func, void *user_data) { + ut8 bytes[8]; + ut64 cursor = start_offset; + + while (true) { + if (r_buf_read_at (cache_buf, cursor, bytes, 8) < 8) { + return UT64_MAX; + } + + ut64 decorated_addr = r_read_le64 (bytes); + + if (func) { + bool carry_on = func (cursor, decorated_addr, user_data); + if (!carry_on) { + break; + } + } + + ut64 delta = ((decorated_addr >> 51) & 0x7ff) * multiplier; + if (delta == 0) { + break; + } + cursor += delta; + } + + return cursor; +} + +static void swizzle_io_read(RKernelCacheObj *obj, RIO *io) { + if (!io || !io->desc || !io->desc->plugin) { + return; + } + + RIOPlugin *plugin = io->desc->plugin; + obj->original_io_read = plugin->read; + plugin->read = &kernelcache_io_read; +} + +static int kernelcache_io_read(RIO *io, RIODesc *fd, ut8 *buf, int count) { + if (!io) { + return -1; + } + RCore *core = (RCore*) io->user; + + if (!core || !core->bin || !core->bin->binfiles) { + return -1; + } + + RKernelCacheObj *cache = NULL; + RListIter *iter; + RBinFile *bf; + r_list_foreach (core->bin->binfiles, iter, bf) { + if (bf->fd == fd->fd ) { + cache = bf->o->bin_obj; + break; + } + } + + if (!cache || !cache->original_io_read) { + return fd->plugin->read (io, fd, buf, count); + } + + r_rebase_info_populate (cache->rebase_info, cache); + + static ut8 *internal_buffer = NULL; + static int internal_buf_size = 0; + if (count > internal_buf_size) { + if (internal_buffer) { + R_FREE (internal_buffer); + internal_buffer = NULL; + } + internal_buffer = (ut8 *) malloc (count); + internal_buf_size = count; + } + + int result = cache->original_io_read (io, fd, internal_buffer, count); + + if (result == count) { + rebase_buffer (cache, io, fd, internal_buffer, count); + memcpy (buf, internal_buffer, result); + } + + return result; +} + +static void rebase_buffer(RKernelCacheObj *obj, RIO *io, RIODesc *fd, ut8 *buf, int count) { + ut64 off = io->off; + ut64 eob = off + count; + int i = 0; + RRebaseCtx ctx; + + ctx.off = off; + ctx.eob = eob; + ctx.buf = buf; + ctx.count = count; + ctx.obj = obj; + + for (; i < obj->rebase_info->n_ranges; i++) { + ut64 start = obj->rebase_info->ranges[i].offset; + ut64 end = start + obj->rebase_info->ranges[i].size; + if (end >= off && start <= eob) { + iterate_rebase_list (obj->cache_buf, obj->rebase_info->multiplier, start, + (ROnRebaseFunc) on_rebase_pointer, &ctx); + } + } +} + +static bool on_rebase_pointer (ut64 offset, ut64 decorated_addr, RRebaseCtx *ctx) { + if (offset < ctx->off) { + return true; + } + if (offset >= ctx->eob) { + return false; + } + ut64 in_buf = offset - ctx->off; + if (in_buf >= ctx->count || (in_buf + 8) > ctx->count) { + return false; + } + + RParsedPointer ptr; + r_parse_pointer (&ptr, decorated_addr, ctx->obj); + + r_write_le64 (&ctx->buf[in_buf], ptr.address); + + return true; +} + +static bool r_parse_pointer(RParsedPointer *ptr, ut64 decorated_addr, RKernelCacheObj *obj) { + /* + * Logic taken from: + * https://github.com/Synacktiv/kernelcache-laundering/blob/master/ios12_kernel_cache_helper.py + */ + + if ((decorated_addr & 0x4000000000000000LL) == 0 && obj->rebase_info) { + if (decorated_addr & 0x8000000000000000LL) { + ptr->address = obj->rebase_info->kernel_base + (decorated_addr & 0xFFFFFFFFLL); + } else { + ptr->address = ((decorated_addr << 13) & 0xFF00000000000000LL) | (decorated_addr & 0x7ffffffffffLL); + if (decorated_addr & 0x40000000000LL) { + ptr->address |= 0xfffc0000000000LL; + } + } + } else { + ptr->address = decorated_addr; + } + + return true; +} + +RBinPlugin r_bin_plugin_xnu_kernelcache = { + .name = "kernelcache", + .desc = "kernelcache bin plugin", + .license = "LGPL3", + .destroy = &destroy, + .load_bytes = &load_bytes, + .load_buffer = &load_buffer, + .entries = &entries, + .baddr = &baddr, + .symbols = &symbols, + .sections = §ions, + .check_bytes = &check_bytes, + .info = &info +}; + +#ifndef CORELIB +RLibStruct radare_plugin = { + .type = R_LIB_TYPE_BIN, + .data = &r_bin_plugin_kernelcache, + .version = R2_VERSION +}; +#endif diff --git a/libr/bin/p/xnu_kernelcache.mk b/libr/bin/p/xnu_kernelcache.mk new file mode 100644 index 0000000000000..7c89b1f78715e --- /dev/null +++ b/libr/bin/p/xnu_kernelcache.mk @@ -0,0 +1,15 @@ +OBJ_XNU_KERNELCACHE=bin_xnu_kernelcache.o +OBJ_XNU_KERNELCACHE+=../format/xnu/yxml.o +OBJ_XNU_KERNELCACHE+=../format/xnu/r_cf_dict.o + +STATIC_OBJ+=${OBJ_XNU_KERNELCACHE} +TARGET_XNU_KERNELCACHE=bin_xnu_kernelcache.${EXT_SO} + +ifeq ($(WITHPIC),1) +ALL_TARGETS+=${TARGET_XNU_KERNELCACHE} + +${TARGET_XNU_KERNELCACHE}: ${OBJ_XNU_KERNELCACHE} + -${CC} $(call libname,bin_xnu_kernelcache) -shared ${CFLAGS} \ + -o ${TARGET_XNU_KERNELCACHE} ${OBJ_XNU_KERNELCACHE} $(LINK) $(LDFLAGS) \ + ${LDFLAGS_LINKPATH}../../syscall -L../../util -lr_syscall +endif diff --git a/libr/include/r_bin.h b/libr/include/r_bin.h index 616e672020773..e5a0a7e9f0ff1 100644 --- a/libr/include/r_bin.h +++ b/libr/include/r_bin.h @@ -780,7 +780,7 @@ extern RBinPlugin r_bin_plugin_xbe; extern RBinPlugin r_bin_plugin_bflt; extern RBinXtrPlugin r_bin_xtr_plugin_xtr_fatmach0; extern RBinXtrPlugin r_bin_xtr_plugin_xtr_dyldcache; -extern RBinXtrPlugin r_bin_xtr_plugin_xtr_pemixed; +extern RBinXtrPlugin r_bin_xtr_plugin_xtr_pemixed; extern RBinLdrPlugin r_bin_ldr_plugin_ldr_linux; extern RBinPlugin r_bin_plugin_zimg; extern RBinPlugin r_bin_plugin_omf; @@ -796,6 +796,7 @@ extern RBinPlugin r_bin_plugin_psxexe; extern RBinPlugin r_bin_plugin_spc700; extern RBinPlugin r_bin_plugin_vsf; extern RBinPlugin r_bin_plugin_dyldcache; +extern RBinPlugin r_bin_plugin_xnu_kernelcache; extern RBinPlugin r_bin_plugin_avr; extern RBinPlugin r_bin_plugin_menuet; extern RBinPlugin r_bin_plugin_wasm; diff --git a/libr/meson.build b/libr/meson.build index 7d107729f6909..e2cdd8a0fc6a8 100644 --- a/libr/meson.build +++ b/libr/meson.build @@ -158,6 +158,7 @@ bin_plugins = [ 'vsf', 'wasm', 'xbe', + 'xnu_kernelcache', 'z64', 'zimg', ] diff --git a/plugins.def.cfg b/plugins.def.cfg index 33a4eed31e182..47a0aa5cf9ea2 100644 --- a/plugins.def.cfg +++ b/plugins.def.cfg @@ -155,6 +155,7 @@ bin.vsf bin.xbe bin.z64 bin.dyldcache +bin.xnu_kernelcache bin_xtr.xtr_dyldcache bin_xtr.xtr_fatmach0 bin_xtr.xtr_pemixed diff --git a/plugins.static.cfg b/plugins.static.cfg index fbe1427d11837..fe5fdb5d74372 100644 --- a/plugins.static.cfg +++ b/plugins.static.cfg @@ -120,6 +120,7 @@ bin.vsf bin.xbe bin.z64 bin.dyldcache +bin.xnu_kernelcache bin_xtr.xtr_dyldcache bin_xtr.xtr_fatmach0 bin_ldr.ldr_linux diff --git a/plugins.static.nogpl.cfg b/plugins.static.nogpl.cfg index ca97219d0bd50..9fd183b972e66 100644 --- a/plugins.static.nogpl.cfg +++ b/plugins.static.nogpl.cfg @@ -120,6 +120,7 @@ bin.vsf bin.xbe bin.z64 bin.dyldcache +bin.xnu_kernelcache bin_xtr.xtr_dyldcache bin_xtr.xtr_fatmach0 bin_ldr.ldr_linux