├── .gitignore ├── c-style.md ├── div_zero.tif ├── readme.md ├── src ├── .mach_gen │ ├── mach_exc.defs │ ├── mach_exc.h │ ├── mach_excServer.c │ └── mach_excUser.c ├── C_Example │ ├── Makefile │ ├── auto_test │ │ ├── Makefile │ │ ├── auto_test_prog.c │ │ └── automated_test.c │ ├── macdbg_attach_test.c │ ├── macdbg_test.c │ ├── macdbg_test_thread.c │ ├── test_prog.c │ └── thread_test_prog.c ├── LICENSE.txt ├── Makefile ├── breakpoint.c ├── breakpoint.h ├── debug_main.c ├── debug_main.h ├── dyldcache_parser.c ├── dyldcache_parser.h ├── exception.c ├── exception.h ├── mcdb.h ├── memory.c ├── memory.h ├── python │ ├── examples │ │ ├── aslr_example.py │ │ ├── basic_example.py │ │ ├── dump_bin_sip.py │ │ ├── dump_binary.py │ │ ├── libmcdb.dylib │ │ ├── malloc │ │ ├── malloc.c │ │ ├── malloc.py │ │ ├── my_mal.py │ │ ├── pid │ │ ├── process_info_example.py │ │ ├── read_write_example.py │ │ ├── search_mem.py │ │ ├── search_multiple.py │ │ ├── sip_run │ │ ├── sip_run.c │ │ ├── test_prog.app │ │ ├── thread_test_prog │ │ └── thread_test_prog.c │ ├── interface │ │ ├── interface.py │ │ └── interface_commands.py │ ├── libs │ │ ├── .test │ │ │ ├── a.out │ │ │ ├── de.py │ │ │ ├── testprog.cpp │ │ │ └── tst.py │ │ ├── __init__.py │ │ ├── const.py │ │ ├── macdbg.py │ │ ├── parse │ │ │ ├── BinaryData.py │ │ │ ├── __init__.py │ │ │ ├── macho.py │ │ │ └── structure.py │ │ └── util.py │ ├── run_tests.sh │ └── tests │ │ ├── __init__.py │ │ ├── __init__.pyc │ │ ├── test_base.py │ │ ├── test_base.pyc │ │ ├── test_exceptions.py │ │ ├── test_exceptions.pyc │ │ ├── test_memory.py │ │ ├── test_memory.pyc │ │ ├── test_process_info.py │ │ ├── test_process_info.pyc │ │ ├── test_thread_info.py │ │ └── test_thread_info.pyc ├── thread.c ├── thread.h └── util.c └── talks_slides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | *.app 2 | *.dylib 3 | *.pyc 4 | -------------------------------------------------------------------------------- /c-style.md: -------------------------------------------------------------------------------- 1 | # C-Style Guide 2 | This style guide is meant for team development between **Tyler**, **Gayathri**, and **Kenny** on the ***Mac Debugger***. In no way is this guide meant to be universal or extremely strict. Listed are only the most important styles used most frequently when coding that will define the overall structure. **Anything left out, intentionally or not, is left to the coder's best judgements.** Any additions or changes should be discussed with the team via [bae-slack]. 3 | 4 | **Work in progress* 5 | 6 | ### Top Core Philosophies: 7 | 1. Maintainability 8 | 2. Consistency 9 | 3. Readability 10 | 4. Code Density 11 | 12 | With maintainability taking priority to all coding styles preceding it. 13 | ### Coding 101: 14 | All functions should be readable and obvious. When a function gets too complicated, use seperate functions to augment that. 15 | 16 | ## Style: 17 | ### Naming Conventions: 18 | When in doubt: lower-case seperated by underscores. Such as: 19 | ```sh 20 | int this_is_a_variable; 21 | ``` 22 | 23 | To name a few, this should adhere to 24 | - local variables 25 | - function names 26 | - file names 27 | 28 | Exceptions: 29 | - defines should all be UPPERCASE, seperated by _ underscores. 30 | - globals 31 | ```sh 32 | #define MAX_BREAKS 100 33 | ``` 34 | 35 | ### Variables: 36 | - All declarations should be defined at the top of the function, then assigned when most appropriate. Besides knowing what variables are being used, the reader will know the types the function uses (mach_port_t, threads, etc.) 37 | 38 | ```sh 39 | vm_address_t get_base_address(mach_port_t task) { 40 | kern_return_t kret; 41 | vm_region_basic_info_data_t info; 42 | vm_size_t size; 43 | mach_port_t object_name; 44 | ... 45 | 46 | mach_vm_address_t address = 1; 47 | 48 | count = VM_REGION_BASIC_INFO_COUNT_64; 49 | 50 | ... 51 | ``` 52 | - Pointer declarations (*) in variables or as arguments should be next to the variable name and not the type. Unless you're defining a function. 53 | ```sh 54 | void* kqueue_loop(void *kp) { 55 | x86_thread_state64_t *break_state; 56 | ... 57 | ``` 58 | 59 | ### To space or not to space 60 | To space: 61 | - Between closing brace ( and { open bracket. 62 | - Between arithmetic or conditionals 63 | - Before an after an assignemnt = 64 | ```sh 65 | if(patch_addr <= MAX_BREAKS + 1) { 66 | int size = 10; 67 | ``` 68 | 69 | not to: 70 | - Between a function call or decleration and parenthasis. printf( .. ) and not printf (...); 71 | - Inside of parenthesis. if( a < b ) should be if(a < b) 72 | ### Brackets: 73 | - Are inline to the function or conditional, with a space between closing ) and open {. Closing bracket } should be on its seperator line, tab-aligned with the function its closing. The exception is for else if and else statements, which are defined in line with the closing brace. 74 | ```sh 75 | int add_breakpoint(mach_port_t task) { 76 | if(patch_addr <= MAX_BREAKS) { 77 | DEBUG_PRINT("[-add_breakpoint] INVALID BREAKPOINT ADDRESS %lx\n", patch_addr); 78 | return -1; 79 | } else if(current_break >= MAX_BREAKS) { 80 | DEBUG_PRINT("[-add_breakpoint] Max %d breaks reached!\n", MAX_BREAKS); 81 | return -1; 82 | } 83 | } 84 | ``` 85 | [bae-slack]: -------------------------------------------------------------------------------- /div_zero.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/div_zero.tif -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Mac Debugger 2 | --- 3 | Mac Debugger was created with the focus on giving the programmer a powerful framework to programatically create scripts to debug programs on Mac OSX not found in other products for the platform. 4 | 5 | The core of macdbg, written in C, is kept as minimal as possible to provide enough basic interaction between the kernel and userland to create powerful features. Higher level functionality is left to the Python implementation such as stack tracing, dissasembly, watchpoints, thread state, and more. A wrapper class is provided for the programmer to create a scripting program. 6 | 7 | NOTE: This is an Alpha implementation more showing concepts and ideas but not fully refined. Expect the Beta version in the coming months with more refined variable naming cleaner code and more useable examples. Thanks for all the interest suggestions comments and help welcome! 8 | 9 | ## Getting Started 10 | --- 11 | ```sh 12 | $ git clone https://github.com/blankwall/MacDBG.git 13 | ``` 14 | 15 | Navigate to the MacDBG directory, then `cd src` and `make`. The shared library libmcdb.dylib will be compiled and several binaries. 16 | 17 | ## Plugins 18 | --- 19 | - [Capstone disassembler](http://www.capstone-engine.org) -- optional 20 | 21 | ## Usage 22 | --- 23 | #### Running a test debugger on a test program. 24 | 1. `cd Python/examples` and open another terminal. 25 | 2. Run `sudo ./basic_example.py` 26 | 3. This is just a test rogram which we will be attach and set breakpoints on. Hit `f` to fork the program enter to continue or press `c` to crash it. You should see breakpoint output like seen below. 27 | 4. There are many other examples in the examples directory most having at least a small description available, nore descriptions and usage coming soon. 28 | 5. Most examples will spawn a program but nearly all of them can take a PID and attach to that. Please note some were written specifaclly for a specific program such as malloc.py was written to track mallocs on Firefox changes would need to be made to make it work on other programs. 29 | -------------------------------------------------------------------------------- /src/.mach_gen/mach_exc.defs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 | * 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. The rights granted to you under the License 10 | * may not be used to create, or enable the creation or redistribution of, 11 | * unlawful or unlicensed copies of an Apple operating system, or to 12 | * circumvent, violate, or enable the circumvention or violation of, any 13 | * terms of an Apple operating system software license agreement. 14 | * 15 | * Please obtain a copy of the License at 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 | * 18 | * The Original Code and all software distributed under the License are 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 | * Please see the License for the specific language governing rights and 24 | * limitations under the License. 25 | * 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 | */ 28 | /* 29 | * @OSF_COPYRIGHT@ 30 | */ 31 | /* 32 | * Mach Operating System 33 | * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University 34 | * All Rights Reserved. 35 | * 36 | * Permission to use, copy, modify and distribute this software and its 37 | * documentation is hereby granted, provided that both the copyright 38 | * notice and this permission notice appear in all copies of the 39 | * software, derivative works or modified versions, and any portions 40 | * thereof, and that both notices appear in supporting documentation. 41 | * 42 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 43 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 44 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 45 | * 46 | * Carnegie Mellon requests users of this software to return to 47 | * 48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 49 | * School of Computer Science 50 | * Carnegie Mellon University 51 | * Pittsburgh PA 15213-3890 52 | * 53 | * any improvements or extensions that they make and grant Carnegie Mellon 54 | * the rights to redistribute these changes. 55 | */ 56 | /* 57 | */ 58 | /* 59 | * Abstract: 60 | * MiG definitions file for Mach exception interface. 61 | */ 62 | 63 | subsystem 64 | #if KERNEL_USER 65 | KernelUser 66 | #endif 67 | mach_exc 2405; 68 | 69 | #include 70 | #include 71 | 72 | ServerPrefix catch_; 73 | 74 | type mach_exception_data_t = array[*:2] of int64_t; 75 | type exception_type_t = int; 76 | 77 | routine mach_exception_raise( 78 | #if KERNEL_USER 79 | exception_port : mach_port_move_send_t; 80 | thread : mach_port_move_send_t; 81 | task : mach_port_move_send_t; 82 | #else /* KERNEL_USER */ 83 | exception_port : mach_port_t; 84 | thread : mach_port_t; 85 | task : mach_port_t; 86 | #endif /* KERNEL_USER */ 87 | exception : exception_type_t; 88 | code : mach_exception_data_t 89 | ); 90 | 91 | routine mach_exception_raise_state( 92 | #if KERNEL_USER 93 | exception_port : mach_port_move_send_t; 94 | #else /* KERNEL_USER */ 95 | exception_port : mach_port_t; 96 | #endif /* KERNEL_USER */ 97 | exception : exception_type_t; 98 | code : mach_exception_data_t, const; 99 | inout flavor : int; 100 | old_state : thread_state_t, const; 101 | out new_state : thread_state_t); 102 | 103 | routine mach_exception_raise_state_identity( 104 | #if KERNEL_USER 105 | exception_port : mach_port_move_send_t; 106 | thread : mach_port_move_send_t; 107 | task : mach_port_move_send_t; 108 | #else /* KERNEL_USER */ 109 | exception_port : mach_port_t; 110 | thread : mach_port_t; 111 | task : mach_port_t; 112 | #endif /* KERNEL_USER */ 113 | exception : exception_type_t; 114 | code : mach_exception_data_t; 115 | inout flavor : int; 116 | old_state : thread_state_t; 117 | out new_state : thread_state_t); 118 | 119 | /* vim: set ft=c : */ 120 | -------------------------------------------------------------------------------- /src/.mach_gen/mach_exc.h: -------------------------------------------------------------------------------- 1 | #ifndef _mach_exc_user_ 2 | #define _mach_exc_user_ 3 | 4 | /* Module mach_exc */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* BEGIN VOUCHER CODE */ 17 | 18 | #ifndef KERNEL 19 | #if defined(__has_include) 20 | #if __has_include() 21 | #ifndef USING_VOUCHERS 22 | #define USING_VOUCHERS 23 | #endif 24 | #ifndef __VOUCHER_FORWARD_TYPE_DECLS__ 25 | #define __VOUCHER_FORWARD_TYPE_DECLS__ 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import)); 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | #endif // __VOUCHER_FORWARD_TYPE_DECLS__ 34 | #endif // __has_include() 35 | #endif // __has_include 36 | #endif // !KERNEL 37 | 38 | /* END VOUCHER CODE */ 39 | 40 | 41 | #ifdef AUTOTEST 42 | #ifndef FUNCTION_PTR_T 43 | #define FUNCTION_PTR_T 44 | typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t); 45 | typedef struct { 46 | char *name; 47 | function_ptr_t function; 48 | } function_table_entry; 49 | typedef function_table_entry *function_table_t; 50 | #endif /* FUNCTION_PTR_T */ 51 | #endif /* AUTOTEST */ 52 | 53 | #ifndef mach_exc_MSG_COUNT 54 | #define mach_exc_MSG_COUNT 3 55 | #endif /* mach_exc_MSG_COUNT */ 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #ifdef __BeforeMigUserHeader 63 | __BeforeMigUserHeader 64 | #endif /* __BeforeMigUserHeader */ 65 | 66 | #include 67 | __BEGIN_DECLS 68 | 69 | 70 | /* Routine mach_exception_raise */ 71 | #ifdef mig_external 72 | mig_external 73 | #else 74 | extern 75 | #endif /* mig_external */ 76 | kern_return_t mach_exception_raise 77 | ( 78 | mach_port_t exception_port, 79 | mach_port_t thread, 80 | mach_port_t task, 81 | exception_type_t exception, 82 | mach_exception_data_t code, 83 | mach_msg_type_number_t codeCnt 84 | ); 85 | 86 | /* Routine mach_exception_raise_state */ 87 | #ifdef mig_external 88 | mig_external 89 | #else 90 | extern 91 | #endif /* mig_external */ 92 | kern_return_t mach_exception_raise_state 93 | ( 94 | mach_port_t exception_port, 95 | exception_type_t exception, 96 | const mach_exception_data_t code, 97 | mach_msg_type_number_t codeCnt, 98 | int *flavor, 99 | const thread_state_t old_state, 100 | mach_msg_type_number_t old_stateCnt, 101 | thread_state_t new_state, 102 | mach_msg_type_number_t *new_stateCnt 103 | ); 104 | 105 | /* Routine mach_exception_raise_state_identity */ 106 | #ifdef mig_external 107 | mig_external 108 | #else 109 | extern 110 | #endif /* mig_external */ 111 | kern_return_t mach_exception_raise_state_identity 112 | ( 113 | mach_port_t exception_port, 114 | mach_port_t thread, 115 | mach_port_t task, 116 | exception_type_t exception, 117 | mach_exception_data_t code, 118 | mach_msg_type_number_t codeCnt, 119 | int *flavor, 120 | thread_state_t old_state, 121 | mach_msg_type_number_t old_stateCnt, 122 | thread_state_t new_state, 123 | mach_msg_type_number_t *new_stateCnt 124 | ); 125 | 126 | __END_DECLS 127 | 128 | /********************** Caution **************************/ 129 | /* The following data types should be used to calculate */ 130 | /* maximum message sizes only. The actual message may be */ 131 | /* smaller, and the position of the arguments within the */ 132 | /* message layout may vary from what is presented here. */ 133 | /* For example, if any of the arguments are variable- */ 134 | /* sized, and less than the maximum is sent, the data */ 135 | /* will be packed tight in the actual message to reduce */ 136 | /* the presence of holes. */ 137 | /********************** Caution **************************/ 138 | 139 | /* typedefs for all requests */ 140 | 141 | #ifndef __Request__mach_exc_subsystem__defined 142 | #define __Request__mach_exc_subsystem__defined 143 | 144 | #ifdef __MigPackStructs 145 | #pragma pack(4) 146 | #endif 147 | typedef struct { 148 | mach_msg_header_t Head; 149 | /* start of the kernel processed data */ 150 | mach_msg_body_t msgh_body; 151 | mach_msg_port_descriptor_t thread; 152 | mach_msg_port_descriptor_t task; 153 | /* end of the kernel processed data */ 154 | NDR_record_t NDR; 155 | exception_type_t exception; 156 | mach_msg_type_number_t codeCnt; 157 | int64_t code[2]; 158 | } __Request__mach_exception_raise_t; 159 | #ifdef __MigPackStructs 160 | #pragma pack() 161 | #endif 162 | 163 | #ifdef __MigPackStructs 164 | #pragma pack(4) 165 | #endif 166 | typedef struct { 167 | mach_msg_header_t Head; 168 | NDR_record_t NDR; 169 | exception_type_t exception; 170 | mach_msg_type_number_t codeCnt; 171 | int64_t code[2]; 172 | int flavor; 173 | mach_msg_type_number_t old_stateCnt; 174 | natural_t old_state[224]; 175 | } __Request__mach_exception_raise_state_t; 176 | #ifdef __MigPackStructs 177 | #pragma pack() 178 | #endif 179 | 180 | #ifdef __MigPackStructs 181 | #pragma pack(4) 182 | #endif 183 | typedef struct { 184 | mach_msg_header_t Head; 185 | /* start of the kernel processed data */ 186 | mach_msg_body_t msgh_body; 187 | mach_msg_port_descriptor_t thread; 188 | mach_msg_port_descriptor_t task; 189 | /* end of the kernel processed data */ 190 | NDR_record_t NDR; 191 | exception_type_t exception; 192 | mach_msg_type_number_t codeCnt; 193 | int64_t code[2]; 194 | int flavor; 195 | mach_msg_type_number_t old_stateCnt; 196 | natural_t old_state[224]; 197 | } __Request__mach_exception_raise_state_identity_t; 198 | #ifdef __MigPackStructs 199 | #pragma pack() 200 | #endif 201 | #endif /* !__Request__mach_exc_subsystem__defined */ 202 | 203 | /* union of all requests */ 204 | 205 | #ifndef __RequestUnion__mach_exc_subsystem__defined 206 | #define __RequestUnion__mach_exc_subsystem__defined 207 | union __RequestUnion__mach_exc_subsystem { 208 | __Request__mach_exception_raise_t Request_mach_exception_raise; 209 | __Request__mach_exception_raise_state_t Request_mach_exception_raise_state; 210 | __Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity; 211 | }; 212 | #endif /* !__RequestUnion__mach_exc_subsystem__defined */ 213 | /* typedefs for all replies */ 214 | 215 | #ifndef __Reply__mach_exc_subsystem__defined 216 | #define __Reply__mach_exc_subsystem__defined 217 | 218 | #ifdef __MigPackStructs 219 | #pragma pack(4) 220 | #endif 221 | typedef struct { 222 | mach_msg_header_t Head; 223 | NDR_record_t NDR; 224 | kern_return_t RetCode; 225 | } __Reply__mach_exception_raise_t; 226 | #ifdef __MigPackStructs 227 | #pragma pack() 228 | #endif 229 | 230 | #ifdef __MigPackStructs 231 | #pragma pack(4) 232 | #endif 233 | typedef struct { 234 | mach_msg_header_t Head; 235 | NDR_record_t NDR; 236 | kern_return_t RetCode; 237 | int flavor; 238 | mach_msg_type_number_t new_stateCnt; 239 | natural_t new_state[224]; 240 | } __Reply__mach_exception_raise_state_t; 241 | #ifdef __MigPackStructs 242 | #pragma pack() 243 | #endif 244 | 245 | #ifdef __MigPackStructs 246 | #pragma pack(4) 247 | #endif 248 | typedef struct { 249 | mach_msg_header_t Head; 250 | NDR_record_t NDR; 251 | kern_return_t RetCode; 252 | int flavor; 253 | mach_msg_type_number_t new_stateCnt; 254 | natural_t new_state[224]; 255 | } __Reply__mach_exception_raise_state_identity_t; 256 | #ifdef __MigPackStructs 257 | #pragma pack() 258 | #endif 259 | #endif /* !__Reply__mach_exc_subsystem__defined */ 260 | 261 | /* union of all replies */ 262 | 263 | #ifndef __ReplyUnion__mach_exc_subsystem__defined 264 | #define __ReplyUnion__mach_exc_subsystem__defined 265 | union __ReplyUnion__mach_exc_subsystem { 266 | __Reply__mach_exception_raise_t Reply_mach_exception_raise; 267 | __Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state; 268 | __Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity; 269 | }; 270 | #endif /* !__RequestUnion__mach_exc_subsystem__defined */ 271 | 272 | #ifndef subsystem_to_name_map_mach_exc 273 | #define subsystem_to_name_map_mach_exc \ 274 | { "mach_exception_raise", 2405 },\ 275 | { "mach_exception_raise_state", 2406 },\ 276 | { "mach_exception_raise_state_identity", 2407 } 277 | #endif 278 | 279 | #ifdef __AfterMigUserHeader 280 | __AfterMigUserHeader 281 | #endif /* __AfterMigUserHeader */ 282 | 283 | #endif /* _mach_exc_user_ */ 284 | -------------------------------------------------------------------------------- /src/C_Example/Makefile: -------------------------------------------------------------------------------- 1 | ## DIRECTORIES ## 2 | PYTHON_DIR = ../python 3 | 4 | ## DEPENDENCIES ## 5 | OBJ1 = macdbg_test.o 6 | OBJ2 = test_prog.o 7 | 8 | ## BINARIES ## 9 | BIN1 = debugger.app 10 | BIN2 = test_prog.app 11 | 12 | LIB = libmcdb.dylib 13 | 14 | ## COMPILER 15 | CC = clang 16 | CFLAGS = -std=gnu99 -Wall 17 | 18 | all: $(BIN1) $(BIN2) 19 | 20 | # debugger binary using macdbg_test.c 21 | $(BIN1): $(OBJ1) 22 | $(CC) $(CFLAGS) $^ -o $@ $(LIB) 23 | 24 | # test_prog binary using test_prog.c 25 | $(BIN2): $(OBJ2) 26 | $(CC) $(CFLAGS) $^ -o $@ 27 | @-cp $@ $(PYTHON_DIR) 28 | 29 | $(OBJ1): ../mcdb.h 30 | 31 | $(OBJ2): test_prog.c 32 | $(CC) -c $^ 33 | 34 | .PHONY : clean cleanobj cleanbin 35 | 36 | clean: cleanobj cleanbin 37 | 38 | cleanobj: 39 | rm *.o 40 | 41 | cleanbin: 42 | rm $(BIN1) $(BIN2) $(PYTHON_DIR)/$(BIN2) -------------------------------------------------------------------------------- /src/C_Example/auto_test/Makefile: -------------------------------------------------------------------------------- 1 | ## Dependencies ## 2 | OBJ1 = auto_test_prog.o 3 | OBJ2 = automated_test.o 4 | 5 | ## Binaries ## 6 | BIN1 = auto_test.app 7 | BIN2 = auto_debug.app 8 | 9 | LIB = libmcdb.dylib 10 | 11 | ## Compiler ## 12 | CC = clang 13 | CFLAGS = -std=gnu99 -Wall 14 | 15 | all: $(BIN1) $(BIN2) 16 | 17 | # auto_test binary using auto_test_prog.c 18 | $(BIN1): $(OBJ1) 19 | $(CC) $(CFLAGS) $^ -o $@ 20 | 21 | # auto_debug binary using automated_test.c 22 | $(BIN2): $(OBJ2) 23 | $(CC) $(CFLAGS) $^ -o $@ $(LIB) 24 | 25 | $(OBJ1): auto_test_prog.c 26 | $(CC) $(CFLAGS) -c $^ 27 | 28 | $(OBJ2): ../../mcdb.h ../../memory.h 29 | 30 | .PHONY : clean cleanobj cleanbin 31 | 32 | clean: cleanobj cleanbin 33 | 34 | cleanobj: 35 | rm *.o 36 | 37 | cleanbin: 38 | rm $(BIN1) $(BIN2) -------------------------------------------------------------------------------- /src/C_Example/auto_test/auto_test_prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test file to debug. Using sudo ./debugger and attach to pid. 3 | * Breakpoint is added after user inputs f or c 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | char* blue = "blue"; 11 | char k[5] = {'a', 'a', 'a', 'f','c'}; 12 | 13 | int abc(int x, int y, int z){ 14 | printf("%d %d %d\n", x,y,z); 15 | return 0; 16 | } 17 | 18 | int main() { 19 | char c; 20 | int pid; 21 | int l = 1; 22 | c = *blue; 23 | printf("My process ID : %d\n", getpid()); 24 | printf("ADDRESS OF ABC: %p\n", abc); 25 | printf("ENTER f for fork c for crash or anything else to continue\n"); 26 | while(1) { 27 | if(l > 6){ 28 | exit(-10); 29 | } 30 | sleep(2); 31 | // c = k[l++]; 32 | if(l == 4){ 33 | c = 'f'; 34 | } else if (l == 5){ 35 | c = 'c'; 36 | } else { 37 | c = ' '; 38 | } 39 | l++; 40 | 41 | if(c == 'f') { 42 | printf("FORK\n"); 43 | pid = fork(); 44 | if(pid == 0) { 45 | exit(-1); 46 | } 47 | } 48 | 49 | if (c == 'c') { 50 | blue = 0; 51 | *blue = 99; 52 | } 53 | 54 | printf("%c\n", c ); 55 | abc(1,2,3); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/C_Example/auto_test/automated_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "../../mcdb.h" 20 | #include "../../memory.h" 21 | 22 | int crash_ = 0; 23 | int exec_ = 0; 24 | int fork_ = 0; 25 | int signal_ = 0; 26 | int exit_ = 0; 27 | int breaks_ = 0; 28 | int reg_change = 0; 29 | int allocate_ = 0; 30 | int free_ =0; 31 | int base_ = 0; 32 | int state_ = 0; 33 | int protect_ = 0; 34 | 35 | int generic_callback_2(exc_msg_t *info_struct) { 36 | fprintf(stderr, "\nFORK CALLBACK\n"); 37 | fork_ = 1; 38 | // getchar(); 39 | return 1; 40 | } 41 | 42 | int generic_callback_3(exc_msg_t *info_struct) { 43 | exec_ = 1; 44 | // getchar(); 45 | return 1; 46 | } 47 | 48 | int generic_callback_4(exc_msg_t *info_struct) { 49 | exit_ = 1; 50 | exit(-1); 51 | } 52 | 53 | int generic_callback_5(exc_msg_t *info_struct) { 54 | signal_ = 1; 55 | // getchar(); 56 | return 1; 57 | } 58 | 59 | int generic_callback(exc_msg_t *info_struct) { 60 | printf("\nBREAKPOINT CALLBACK %p\n", info_struct->code); 61 | x86_thread_state64_t *break_state = get_state(info_struct->thread); 62 | // enumerate_reg_simple(break_state); 63 | mach_vm_address_t x = break_state->__rip & 0xfff; 64 | 65 | breaks_ += 1; 66 | //FUNCTION OFFSETS MAY NEED TO BE CHANGED 67 | if(x == 0xd20) { 68 | reg_change += 1; 69 | break_state->__rsi = 99; 70 | // break_state->__rip &= 0xffffff000; 71 | // break_state->__rip += 0xd70; 72 | 73 | 74 | // 0x101117d50 75 | set_thread_state(info_struct->thread, break_state); 76 | // printf("IN FUNCTION!\n"); 77 | } 78 | // getchar(); 79 | return 1; 80 | } 81 | 82 | int generic_callback_9(exc_msg_t *info_struct) { 83 | mach_port_t task = info_struct->task; 84 | long xen = get_base_address(task); 85 | int xeny = 0xF22; 86 | mach_vm_address_t k = xen + xeny; 87 | fprintf(stderr, "\nTEST COMPLETE\nRESULTS:\n"); 88 | 89 | if(strncmp((const char *)read_memory(task, k, 4), "blue", 4)) { 90 | fprintf(stderr, "FAILED [read_memory]\n"); 91 | } else { 92 | fprintf(stderr, "PASSED [read_memory]\n"); 93 | } 94 | 95 | if(strncmp((const char *)read_memory_allocate(task, k, 4), "blue", 4)) { 96 | fprintf(stderr, "FAILED [read_memory_allocate]\n"); 97 | } else { 98 | fprintf(stderr, "PASSED [read_memory_allocate]\n"); 99 | } 100 | 101 | if(!(fork_ & 1)) { 102 | fprintf(stderr, "FAILED [fork]\n"); 103 | } else { 104 | fprintf(stderr, "PASSED [fork]\n"); 105 | } 106 | 107 | if(!(signal_ & 1)) { 108 | fprintf(stderr, "FAILED [signal]\n"); 109 | } else { 110 | fprintf(stderr, "PASSED [signal]\n"); 111 | } 112 | 113 | if(breaks_ != 5 || reg_change != 4) { 114 | fprintf(stderr, "FAILED [breakpoints]\n"); 115 | } else { 116 | fprintf(stderr, "PASSED [breakpoints]\n"); 117 | } 118 | 119 | if(!(allocate_ & 1)) { 120 | fprintf(stderr, "FAILED [allocate] \n"); 121 | } else { 122 | fprintf(stderr, "PASSED [allocate_]\n"); 123 | } 124 | 125 | if(!(base_&1)) { 126 | fprintf(stderr, "FAILED [base_addr]\n"); 127 | } else { 128 | fprintf(stderr, "PASSED [base_addr]\n"); 129 | } 130 | 131 | if(!(state_)) { 132 | fprintf(stderr, "FAILED [thread_state]\n"); 133 | } else { 134 | fprintf(stderr, "PASSED [thread_state]\n"); 135 | } 136 | 137 | if(!(protect_)) { 138 | fprintf(stderr, "FAILED [change_page_protection]\n"); 139 | } else { 140 | fprintf(stderr, "PASSED [change_page_protection]\n"); 141 | } 142 | 143 | if(!(free_ & 1)) { 144 | fprintf(stderr, "FAILED [free memory] \n"); 145 | } else { 146 | fprintf(stderr, "PASSED [free memory]\n"); 147 | } 148 | 149 | fprintf(stderr, "PASSED [crash_callback]\n"); 150 | 151 | task_terminate(info_struct->task); 152 | 153 | exit(-1); 154 | } 155 | 156 | int main(int argc, char* *argv) { 157 | mach_port_t task; 158 | x86_thread_state64_t *state; 159 | 160 | // infoPid = spawn_process("test"); 161 | 162 | char *my_args[2]; 163 | my_args[0] = "auto_test.app"; 164 | my_args[1] = NULL; 165 | 166 | task = run("auto_test.app", my_args); 167 | 168 | long base_addr = get_base_address(task); 169 | 170 | // for(int i = 0; i < 10000000; i += 4096) { 171 | // change_page_protection(task, base_addr, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 172 | // base_addr += i; 173 | // } 174 | 175 | change_page_protection(task, base_addr, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 176 | 177 | vm_region_basic_info_data_64_t *region_info= NULL; 178 | 179 | region_info = get_region_info(task, base_addr); 180 | printf("PROTECTIONS OF REGION ARE: %s\n", get_protection(region_info->protection)); 181 | 182 | if(!strncmp(get_protection(region_info->protection), "rwx", 3)) { 183 | protect_ = 1; 184 | } 185 | 186 | void *x = allocate(task, 0, 4096, 1); 187 | 188 | if(x) { 189 | allocate_ = 1; 190 | } 191 | 192 | if(free_memory(task, (mach_vm_address_t)x, 4096)) { 193 | free_ = 1; 194 | } 195 | 196 | if((base_addr & 0xfff) == 0) { 197 | base_ = 1; 198 | } 199 | 200 | state = thread_state(task, 0); 201 | 202 | if(state) { 203 | state_ = 1; 204 | } 205 | // vm_region_t *vm_region_list[MAX_REGION]; 206 | 207 | // get_region_info(task, base_addr, vm_region_list); 208 | 209 | // vm_region_basic_info_data_64_t *region_info= vm_region_list[0]; 210 | 211 | //printf("PROTECTIONS OF REGION ARE: %s\n", get_protection(region_info->protection)); 212 | 213 | mach_vm_address_t patch_addr = base_addr+0xd20; 214 | 215 | add_breakpoint(task, patch_addr, PERSISTENT, generic_callback); 216 | add_breakpoint(task, base_addr+0xe0e, ONE_TIME, generic_callback); 217 | add_breakpoint(task, base_addr+0xe3a, PERSISTENT, generic_callback); 218 | // add_breakpoint(task, base_addr+0xd55, PERSISTENT, generic_callback); 219 | 220 | add_exception_callback(task, generic_callback_9, EXC_BAD_ACCESS); 221 | 222 | // tracking process creation 223 | add_exception_callback(task,generic_callback_3, NOTE_EXEC); 224 | add_exception_callback(task,generic_callback_2, NOTE_FORK); 225 | add_exception_callback(task,generic_callback_5, NOTE_SIGNAL); 226 | add_exception_callback(task,generic_callback_4, NOTE_EXIT); 227 | 228 | // start(task, infoPid); 229 | continue_(task); 230 | while(1); 231 | 232 | return 0; 233 | } 234 | 235 | -------------------------------------------------------------------------------- /src/C_Example/macdbg_attach_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "../mcdb.h" 20 | 21 | /* 22 | Showing how to attach to multiple processes at once 23 | */ 24 | 25 | int generic_callback(exc_msg_t *info_struct){ 26 | printf("\nBREAKPOINT CALLBACK %p\n", info_struct->code); 27 | // getchar(); 28 | return 1; 29 | } 30 | int generic_callback_2(exc_msg_t *info_struct){ 31 | printf("\nFORK CALLBACK\n"); 32 | getchar(); 33 | return 1; 34 | } 35 | int generic_callback_3(exc_msg_t *info_struct){ 36 | printf("\nEXEC CALLBACK\n"); 37 | getchar(); 38 | return 1; 39 | } 40 | int generic_callback_4(exc_msg_t *info_struct){ 41 | printf("\nEXIT CALLBACK\n"); 42 | exit(-1); 43 | } 44 | int generic_callback_5(exc_msg_t *info_struct){ 45 | printf("\nSIGNAL CALLBACK\n"); 46 | getchar(); 47 | return 1; 48 | } 49 | int generic_callback_9(exc_msg_t *info_struct){ 50 | printf("OMG A CRASH CALLBACK\n"); 51 | getchar(); 52 | task_terminate(info_struct->task); 53 | exit(-1); 54 | } 55 | 56 | int main(int argc, char** argv){ 57 | mach_port_t task; 58 | pid_t infoPid; 59 | //kern_return_t kret; 60 | //thread_act_port_array_t threadList; 61 | //mach_msg_type_number_t threadCount; 62 | x86_thread_state64_t *state; 63 | int task_[9]; 64 | 65 | 66 | int pid[] = { 67 | 24556, 68 | 24581, 69 | 24606, 70 | 24631, 71 | 24656, 72 | 24681, 73 | 24706, 74 | 24731, 75 | 24756}; 76 | 77 | int count = 0; 78 | 79 | while(count < 9){ 80 | task = attach(pid[count]); 81 | printf("ATTACHED TO PROCESS %d WITH TASK %d\n", pid[count], task); 82 | task_[count] = task; 83 | count += 1; 84 | } 85 | 86 | mach_vm_address_t k; 87 | mach_vm_address_t patch_addr; 88 | 89 | count= 0; 90 | while(count < 9){ 91 | task = task_[count]; 92 | k = get_base_address(task) + 0xf1e; 93 | printf("READ MEMORY TEST: %s\n", (char*)read_memory(task, k, 4)); 94 | patch_addr = get_base_address(task)+0xd90; 95 | add_breakpoint(task, patch_addr, ONE_TIME, generic_callback); 96 | count += 1; 97 | } 98 | 99 | while(1); 100 | return 0; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /src/C_Example/macdbg_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "../mcdb.h" 20 | 21 | /* 22 | Generic test showing off how to use some of the features 23 | */ 24 | int generic_callback_44(exc_msg_t *info_struct){ 25 | printf("\nMALCALLBACK\n"); 26 | getchar(); 27 | return 1; 28 | } 29 | 30 | 31 | int generic_callback(exc_msg_t *info_struct){ 32 | x86_thread_state64_t* state = get_state(info_struct->thread); 33 | char*k = read_memory(info_struct->task, state->__rsp, 8); 34 | printf("MALLOC CALLED SIZE: %llx\t", state->__rdi); 35 | printf("%llx\n", k); 36 | return 1; 37 | } 38 | int generic_callback_2(exc_msg_t *info_struct){ 39 | printf("\nFORK CALLBACK\n"); 40 | getchar(); 41 | return 1; 42 | } 43 | int generic_callback_3(exc_msg_t *info_struct){ 44 | printf("\nEXEC CALLBACK\n"); 45 | getchar(); 46 | return 1; 47 | } 48 | int generic_callback_4(exc_msg_t *info_struct){ 49 | printf("\nEXIT CALLBACK\n"); 50 | exit(-1); 51 | } 52 | int generic_callback_5(exc_msg_t *info_struct){ 53 | printf("\nSIGNAL CALLBACK\n"); 54 | getchar(); 55 | return 1; 56 | } 57 | int generic_callback_9(exc_msg_t *info_struct){ 58 | printf("OMG A CRASH CALLBACK\n"); 59 | getchar(); 60 | task_terminate(info_struct->task); 61 | exit(-1); 62 | } 63 | 64 | 65 | int main(int argc, char** argv){ 66 | mach_port_t task; 67 | pid_t infoPid; 68 | //kern_return_t kret; 69 | //thread_act_port_array_t threadList; 70 | //mach_msg_type_number_t threadCount; 71 | x86_thread_state64_t *state; 72 | vm_region_basic_info_data_64_t* region_info= NULL; 73 | 74 | 75 | if(argc < 2) { 76 | puts("Usage: sudo ./debugger [pid]"); 77 | exit(-1); 78 | } 79 | unsigned char shellcode[] = 80 | "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"; 81 | 82 | 83 | infoPid = atoi(argv[1]); 84 | task = attach(infoPid); 85 | 86 | mach_vm_address_t xello = inject_code(task, shellcode, strlen(shellcode)); 87 | printf("CODE INJECTED @ %p\n", xello); 88 | 89 | long xen = get_base_address(task); 90 | 91 | 92 | printf("%lld\n", get_image_size(task, xen)); 93 | 94 | mach_vm_address_t patch_addr = allocate_space(task, 4000, VM_FLAGS_ANYWHERE); 95 | 96 | char* code = "hello"; 97 | 98 | int xe = change_page_protection(task, patch_addr, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 99 | if(xe == 0){ 100 | printf("PAGE PROT FAILED 2\n"); 101 | } else { 102 | printf("PAGE PROT SUCCESS \n"); 103 | } 104 | 105 | printf("PAGE ALLOCATED @ %p\n WRITING HELLO!\n", patch_addr); 106 | write_bytes(task, patch_addr, "hello", 5); 107 | 108 | char* xop = read_memory(task, patch_addr, 20); 109 | printf("XOP (%p)\n", xop); 110 | 111 | print_byte(xop); 112 | 113 | 114 | // print_byte(xop); 115 | 116 | printf("READING THE HEADER: "); 117 | print_byte(read_memory(task, xen, 20)); 118 | 119 | 120 | int xeny = 0xf1e; 121 | 122 | void* x = allocate(task, 0, 4096, 1); 123 | //Should fail 124 | void* N = allocate(task, 0, 9999999999999999999, 1); 125 | 126 | 127 | region_info = get_region_info(task, x); 128 | printf("ALLOCATED PROTECTIONS OF REGION ARE: %s\n", get_page_protection(task, x)); 129 | 130 | 131 | for(int i = 0; i < 10000000; i += 4096) { 132 | change_page_protection(task, xen, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 133 | xen += i; 134 | } 135 | 136 | 137 | 138 | mach_vm_address_t k = get_base_address(task) + xeny; 139 | 140 | printf("READ MEMORY TEST: %s\n", read_memory(task, k, 4)); 141 | printf("READ MEMORY OVERWRITE TEST: %s\n", (unsigned long long)read_memory_allocate(task, k, 4)); 142 | 143 | free_memory(task, (mach_vm_address_t)x, 4096); //free the allocated memory 144 | 145 | state = thread_state(task, 0); 146 | mach_vm_address_t base_addr = get_base_address(task); 147 | 148 | // printf("BASE ADDRESS1: %llx\n", base_addr); 149 | 150 | // vm_region_t *vm_region_list[MAX_REGION]; 151 | 152 | int regions; 153 | vm_region_t ** vm_region_list = get_memory_map(task, base_addr, ®ions); 154 | vm_region_t* vm_region_1 = vm_region_list[0]; 155 | printf("# of Regions == %d -- FIRST REGION INFO: \n", regions); 156 | 157 | printf("TYPE:%s BASE ADDRESS: 0x%016llx END ADDRESS: 0x%016llx SIZE: 0x%llx PROT:%s\n", 158 | user_tag_to_string(vm_region_1->region_type) ,vm_region_1->address_start, 159 | vm_region_1->address_start+vm_region_1->size,vm_region_1->size, 160 | get_protection(vm_region_1->protection)); 161 | 162 | 163 | region_info = get_region_info(task, xen); 164 | printf("PROTECTIONS OF REGION ARE: %s\n", get_protection(region_info->protection)); 165 | 166 | printf("PROTECTIONS FOR BASE_ADDR ARE: %s\n", get_page_protection(task, base_addr)); 167 | 168 | uint32_t no_dyld; 169 | dyld_info_struct **infos = get_dyld_map(task, &no_dyld); 170 | const char* region[]={"__TEXT", "__DATA"}; 171 | 172 | for (uint32_t i=0;i< no_dyld; i++){ 173 | dyld_info_struct *dyldinfo = infos[i]; 174 | // if (dyldinfo->start_address == base_addr) { 175 | printf("%s %llx %llx %luk %s %s\n", region[dyldinfo->type], dyldinfo->start_address, 176 | dyldinfo->end_address, dyldinfo->size/1024, 177 | dyldinfo->fpath, get_protection(dyldinfo->protection)); 178 | break; 179 | // } 180 | } 181 | 182 | printf("# of Regions == %d -- # of dyld's: %d\n", regions, no_dyld); 183 | 184 | 185 | patch_addr = base_addr+0xd90; 186 | 187 | add_breakpoint(task, patch_addr, ONE_TIME, generic_callback); 188 | add_breakpoint(task, base_addr+0xe0e, PERSISTENT, generic_callback); 189 | add_breakpoint(task, base_addr+0xe3a, PERSISTENT, generic_callback); 190 | // list_breaks(task); 191 | 192 | add_exception_callback(task, generic_callback_9, EXC_BAD_ACCESS); 193 | 194 | //tracking process creation 195 | add_exception_callback(task, generic_callback_3, NOTE_EXEC); 196 | add_exception_callback(task, generic_callback_2, NOTE_FORK); 197 | add_exception_callback(task, generic_callback_5, NOTE_SIGNAL); 198 | add_exception_callback(task, generic_callback_4, NOTE_EXIT); 199 | 200 | thread_array_t thread_list; 201 | mach_msg_type_number_t tc; 202 | thread_list_info(task, &thread_list, &tc); 203 | 204 | // printf("THREADS: %d\n", tc); 205 | thread_identifier_info_data_t* identifier_info = get_thread_identifier_info (thread_list[0]); 206 | thread_basic_info_t basic_info = get_thread_basic_info (thread_list[0]); 207 | 208 | 209 | fprintf(stderr, "TID: %llu\n", identifier_info->thread_id); 210 | printf("USER TIME: %d\n", basic_info->user_time.seconds); 211 | 212 | 213 | continue_(task); 214 | sleep(10); 215 | detach(task); 216 | task = attach(infoPid); 217 | if(task != 0){ 218 | printf("REATTACHED SUCCESFULLY!\n"); 219 | } 220 | exit(1); 221 | return 0; 222 | } 223 | 224 | -------------------------------------------------------------------------------- /src/C_Example/macdbg_test_thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "../mcdb.h" 20 | 21 | /* 22 | Generic test showing off how to threads 23 | */ 24 | 25 | 26 | int generic_callback(exc_msg_t *info_struct){ 27 | printf("\nBREAKPOINT CALLBACK %p\n", info_struct->code); 28 | // getchar(); 29 | return 1; 30 | } 31 | int generic_callback_2(exc_msg_t *info_struct){ 32 | printf("\nFORK CALLBACK\n"); 33 | getchar(); 34 | return 1; 35 | } 36 | int generic_callback_3(exc_msg_t *info_struct){ 37 | printf("\nEXEC CALLBACK\n"); 38 | getchar(); 39 | return 1; 40 | } 41 | int generic_callback_4(exc_msg_t *info_struct){ 42 | printf("\nEXIT CALLBACK\n"); 43 | exit(-1); 44 | } 45 | int generic_callback_5(exc_msg_t *info_struct){ 46 | printf("\nSIGNAL CALLBACK\n"); 47 | getchar(); 48 | return 1; 49 | } 50 | int generic_callback_9(exc_msg_t *info_struct){ 51 | printf("OMG A CRASH CALLBACK\n"); 52 | getchar(); 53 | task_terminate(info_struct->task); 54 | exit(-1); 55 | } 56 | 57 | int main(int argc, char** argv){ 58 | pid_t infoPid; 59 | kern_return_t kret; 60 | mach_port_t task, server_port; 61 | thread_act_port_array_t threadList; 62 | mach_msg_type_number_t threadCount; 63 | x86_thread_state64_t *state; 64 | 65 | if(argc < 2) { 66 | puts("Usage: sudo ./debugger [pid]"); 67 | exit(-1); 68 | } 69 | 70 | infoPid = atoi(argv[1]); 71 | 72 | task = attach(infoPid); 73 | long xen = get_base_address(task); 74 | 75 | int xeny = 0xf1e; 76 | 77 | void* x = allocate(task, 0, 4096, 1); 78 | 79 | for(int i = 0; i < 10000000; i += 4096) { 80 | change_page_protection(task, xen, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 81 | xen += i; 82 | } 83 | 84 | mach_vm_address_t k = get_base_address(task) + xeny; 85 | 86 | printf("READ MEMORY TEST: %lu\n", read_memory(task, k, 4)); 87 | printf("READ MEMORY OVERWRITE TEST: %llu\n", (unsigned long long)read_memory_allocate(task, k, 4)); 88 | 89 | free_memory(task, (mach_vm_address_t)x, 4096); //free the allocated memory 90 | 91 | state = thread_state(task, 0); 92 | mach_vm_address_t base_addr = get_base_address(task); 93 | 94 | // printf("BASE ADDRESS1: %llx\n", base_addr); 95 | 96 | vm_region_t *vm_region_list[MAX_REGION]; 97 | 98 | // get_memory_map(task, base_addr, vm_region_list); 99 | 100 | // vm_region_basic_info_data_64_t* region_info= NULL; 101 | // region_info = get_region_info(task, xen); 102 | // printf("PROTECTIONS OF REGION ARE: %s\n", get_protection(region_info->protection)); 103 | 104 | mach_vm_address_t patch_addr = base_addr+0xc70; 105 | 106 | add_breakpoint(task, patch_addr, PERSISTENT, generic_callback); 107 | // add_breakpoint(task, base_addr+0xe0e, PERSISTENT, generic_callback); 108 | // add_breakpoint(task, base_addr+0xe3a, PERSISTENT, generic_callback); 109 | list_breaks(task); 110 | 111 | add_exception_callback(task, generic_callback_9, EXC_BAD_ACCESS); 112 | 113 | //tracking process creation 114 | add_exception_callback(task, generic_callback_3, NOTE_EXEC); 115 | add_exception_callback(task, generic_callback_2, NOTE_FORK); 116 | add_exception_callback(task, generic_callback_5, NOTE_SIGNAL); 117 | add_exception_callback(task, generic_callback_4, NOTE_EXIT); 118 | 119 | // thread_act_port_array_t* list_threads = thread_list(task); 120 | // mach_msg_type_number_t tc = thread_count(task); 121 | // printf("THREADS: %d\n", tc); 122 | 123 | thread_array_t thread_list; 124 | mach_msg_type_number_t tc; 125 | thread_list_info(task, &thread_list, &tc); 126 | printf("THREADS: %d\n", tc); 127 | 128 | 129 | continue_(task); 130 | sleep(1); 131 | 132 | int i; 133 | int verbose = 1; 134 | int wordsize = 8; 135 | for(i = 0; i < tc; i++){ 136 | 137 | //DEFINED HERE http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/mach/thread_info.h 138 | thread_basic_info_t basic_info = get_thread_basic_info (thread_list[i]); 139 | thread_identifier_info_data_t identifier_info = get_thread_identifier_info (thread_list[i]); 140 | 141 | int wordsize; 142 | state = thread_state(task, i); 143 | uint64_t pc = state->__rip; 144 | 145 | printf ("thread #%d, system-wide-unique-tid %lld, suspend count is %d, ", i, 146 | identifier_info.thread_id, 147 | basic_info->suspend_count); 148 | 149 | printf ("pc 0x%016llx, ", pc); 150 | 151 | printf ("run state is "); 152 | switch (basic_info->run_state) { 153 | case TH_STATE_RUNNING: puts ("running"); break; 154 | case TH_STATE_STOPPED: puts ("stopped"); break; 155 | case TH_STATE_WAITING: puts ("waiting"); break; 156 | case TH_STATE_UNINTERRUPTIBLE: puts ("uninterruptible"); break; 157 | case TH_STATE_HALTED: puts ("halted"); break; 158 | default: puts (""); 159 | } 160 | 161 | printf (" pthread handle id 0x%llx (not the same value as pthread_self() returns)\n", (uint64_t) identifier_info.thread_handle); 162 | 163 | 164 | 165 | free ((void *) basic_info); 166 | } 167 | 168 | // start(task, infoPid); 169 | // while(1); 170 | sleep(10); 171 | detach(task); 172 | 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /src/C_Example/test_prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test file to debug. Using sudo ./debugger and attach to pid. 3 | * Breakpoint is added after user inputs f or c 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | char* blue = "blue"; 10 | 11 | int abc(int x, int y, int z) { 12 | printf("%d %d %d\n", x,y,z); 13 | return 0; 14 | } 15 | 16 | int main() { 17 | char c; 18 | int pid; 19 | c = *blue; 20 | printf("My process ID : %d\n", getpid()); 21 | printf("ADDRESS OF ABC: %p\n", abc); 22 | printf("ENTER f for fork c for crash or anything else to continue\n"); 23 | while(1) { 24 | c = getchar(); 25 | 26 | if(c == 'f') { 27 | pid = fork(); 28 | if(pid == 0) { 29 | exit(-1); 30 | } 31 | } 32 | 33 | if (c == 'c') { 34 | blue = 0; 35 | *blue = 99; 36 | } 37 | 38 | printf("%c\n", c ); 39 | abc(1,2,3); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/C_Example/thread_test_prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test file to debug. Using sudo ./debugger and attach to pid. 3 | * Breakpoint is added after user inputs f or c 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | char* blue = "blue"; 10 | 11 | int abc(int x, int y, int z) { 12 | printf("%d %d %d\n", x,y,z); 13 | return 0; 14 | } 15 | 16 | int cde() { 17 | while(1){ 18 | sleep(10); 19 | printf("HELLO\n"); 20 | } 21 | return 0; 22 | } 23 | 24 | int main() { 25 | int tid; 26 | int err = pthread_create(&tid, NULL, (void *(*)(void*))cde, 0); 27 | if (err != 0) 28 | printf("\n[-start] can't create thread :[%s]", strerror(err)); 29 | else 30 | printf("\n[-start] Thread created successfully %d\n", 0); 31 | 32 | char c; 33 | int pid; 34 | c = *blue; 35 | printf("My process ID : %d\n", getpid()); 36 | printf("ADDRESS OF ABC: %p\n", abc); 37 | printf("ENTER f for fork c for crash or anything else to continue\n"); 38 | while(1) { 39 | c = getchar(); 40 | 41 | if(c == 'f') { 42 | pid = fork(); 43 | if(pid == 0) { 44 | exit(-1); 45 | } 46 | } 47 | 48 | if (c == 'c') { 49 | blue = 0; 50 | *blue = 99; 51 | } 52 | 53 | printf("%c\n", c ); 54 | abc(1,2,3); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Tyler Bohan 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | ## Directories ## 2 | MACH_DIR = .mach_gen 3 | TEST_DIR = C_Example 4 | AUTO_DIR = $(TEST_DIR)/auto_test 5 | PYTHON_DIR = python 6 | PYTHON_DIR_EX = python/examples 7 | 8 | ## libmcdb Dependencies ## 9 | MACH_OBJS = $(MACH_DIR)/mach_excServer.o $(MACH_DIR)/mach_excUser.o 10 | OBJECTS = debug_main.o util.o breakpoint.o exception.o memory.o thread.o dyldcache_parser.o $(MACH_OBJS) 11 | 12 | ## Binary ## 13 | LIB = libmcdb.dylib 14 | 15 | ## Compiler ## 16 | CC = clang 17 | 18 | ## FLAGS ## 19 | CFLAGS = -std=gnu99 -Wall -fvisibility=hidden -Qunused-arguments -current_version 1.0 -compatibility_version 1.0 20 | 21 | all: $(TEST_DIR)/$(LIB) tst auto_test 22 | 23 | debug: CFLAGS += -DDEBUG -g 24 | debug: all 25 | 26 | no_o: all cleanobj_all 27 | 28 | $(TEST_DIR)/$(LIB): $(OBJECTS) 29 | $(CC) $(CFLAGS) -dynamiclib $^ -o $(LIB) 30 | cp $(LIB) $(PYTHON_DIR) 31 | cp $(LIB) $(PYTHON_DIR_EX) 32 | mv $(LIB) $(TEST_DIR) 33 | cp $@ $(AUTO_DIR) 34 | 35 | # test directory make 36 | tst: 37 | @-$(MAKE) -C $(TEST_DIR) 38 | 39 | # test/auto_test directory make 40 | auto_test: 41 | @-$(MAKE) -C $(AUTO_DIR) 42 | 43 | ## DEPENDENCIES ## 44 | debug_main.o: debug_main.h thread.h 45 | 46 | util.o: mcdb.h 47 | 48 | breakpoint.o: breakpoint.h memory.h 49 | 50 | exception.o: exception.h 51 | 52 | memory.o: memory.h 53 | 54 | thread.o: thread.h 55 | 56 | dyldcache_parser.o: dyldcache_parser.h 57 | 58 | mach_excUser.o: $(MACH_DIR)/mach_exc.h 59 | 60 | mach_excServer.o: $(MACH_DIR)/mach_excServer.c 61 | $(CC) -c $^ 62 | 63 | .PHONY : clean cleanobj_all cleanbin cleanobj cleanpyc 64 | 65 | # remove all .o and binaries recursively by calling all make clean's 66 | clean: cleanobj_all cleanbin 67 | 68 | # remove all .o but keep binaries 69 | cleanobj_all: cleanobj 70 | @-$(MAKE) -C $(TEST_DIR) cleanobj 71 | @-$(MAKE) -C $(AUTO_DIR) cleanobj 72 | 73 | # remove all .o made from this makefile 74 | cleanobj: 75 | rm *.o $(MACH_DIR)/*.o 76 | 77 | # remove all binaries 78 | cleanbin: 79 | rm $(TEST_DIR)/$(LIB) $(AUTO_DIR)/$(LIB) $(PYTHON_DIR)/$(LIB) 80 | @-$(MAKE) -C $(TEST_DIR) cleanbin 81 | @-$(MAKE) -C $(AUTO_DIR) cleanbin 82 | 83 | # remove all pyc files (nothing depends on this) 84 | cleanpyc: 85 | rm $(PYTHON_DIR)/libs/*.pyc $(PYTHON_DIR)/libs/parse/*.pyc 86 | -------------------------------------------------------------------------------- /src/breakpoint.c: -------------------------------------------------------------------------------- 1 | #include "breakpoint.h" 2 | #include "memory.h" 3 | 4 | 5 | EXPORT 6 | int add_breakpoint(mach_port_t task, vm_address_t patch_addr, int cont, callback handler) { 7 | kern_return_t kret; 8 | char *tmp; 9 | mach_vm_size_t len = 1; // number of bytes to write 10 | uint8_t opcode = 0xcc; // the CC byte to write 11 | interface *face; 12 | 13 | face = find_interface(task); 14 | if(face->registered_exception_handler == 0) { 15 | DEBUG_PRINT("[+add_breakpoint] HERE IN ADD BREAK\n %d", 0); 16 | register_(task); 17 | } 18 | 19 | kret = mach_vm_protect(task, patch_addr, len, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 20 | RETURN_ON_MACH_ERROR("[-add_breakpoint] mach_vm_protect()", kret); 21 | 22 | if (patch_addr <= MAX_BREAKS) { 23 | DEBUG_PRINT("[-add_breakpoint] INVALID BREAKPOINT ADDRESS %lx\n", patch_addr); 24 | return -1; 25 | } else if(face->current_break >= MAX_BREAKS) { 26 | DEBUG_PRINT("[-add_breakpoint] Max %d breaks reached!\n", MAX_BREAKS); 27 | return -1; 28 | } 29 | 30 | DEBUG_PRINT("[+add_breakpoint] Breakpoint %u: %lx added\n", face->current_break, patch_addr); 31 | tmp = (char*) read_memory(task, patch_addr, 1); 32 | 33 | breakpoint_struct *new_break = safe_malloc(sizeof(breakpoint_struct)); 34 | new_break->address = patch_addr; 35 | new_break->original = tmp[0] & 0xff; 36 | new_break->handler = handler; 37 | if(face->single_step) { 38 | new_break->index = face->single_step_index; 39 | } 40 | else { 41 | new_break->index = face->current_break == 0 ? 0 : face->breaks[face->current_break-1]->index + 1; 42 | } 43 | new_break->flags = cont; 44 | 45 | 46 | if(face->max_break == 0) { 47 | face->max_break = 1; 48 | } 49 | 50 | if(face->current_break >= (face->max_break - 1)) { 51 | DEBUG_PRINT("[+add_breakpoint] ALLOCATING MORE BP! CURRENTLY: %d\n", face->current_break); 52 | face->breaks = safe_realloc(face->breaks, sizeof(breakpoint_struct*) *(face->max_break*2)); 53 | face->max_break *= 2; 54 | } 55 | 56 | // face->breaks = safe_realloc(face->breaks, sizeof(breakpoint_struct*) *(face->current_break+1)); 57 | face->breaks[face->current_break++] = new_break; 58 | 59 | write_memory(task, patch_addr, opcode, len); // write the byte 60 | kret = mach_vm_protect(task, patch_addr, (mach_vm_size_t)1, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 61 | RETURN_ON_MACH_ERROR("[-add_breakpoint] RESTORE mach_vm_protect()", kret); 62 | 63 | return 1; 64 | } 65 | 66 | /* 67 | * Removes a breakpoint from breaks array indicated by index or address (both are unsigned long long) 68 | */ 69 | EXPORT 70 | int remove_breakpoint(mach_port_t task, vm_address_t bp) { 71 | kern_return_t kret; 72 | int c, position, index; 73 | mach_vm_address_t address; 74 | interface *face; 75 | 76 | face = find_interface(task); 77 | if(!face->registered_exception_handler) { 78 | DEBUG_PRINT("SHOULD NEVER HAPPEN :| %d\n", 1); 79 | return -1; 80 | } 81 | 82 | position = find_break(task, bp); 83 | if(position == -1) { 84 | DEBUG_PRINT("[-remove_breakpoint] Failed find_break %d\n", position); 85 | return -1; 86 | } 87 | 88 | breakpoint_struct *breakpoint = face->breaks[position]; 89 | 90 | uint8_t opcode = breakpoint->original; // CC byte to write 91 | mach_vm_size_t len = 1; // number of bytes to write 92 | 93 | kret = mach_vm_protect(task, breakpoint->address, len, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); 94 | RETURN_ON_MACH_ERROR("[-remove_breakpoint] mach_vm_protect()", kret); 95 | 96 | write_memory(task, breakpoint->address, opcode, len); // and write the byte 97 | 98 | kret = mach_vm_protect(task, breakpoint->address, len, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 99 | RETURN_ON_MACH_ERROR("[-remove_breakpoint] RESTORE mach_vm_protect()", kret); 100 | 101 | address = face->breaks[position]->address; 102 | index = face->breaks[position]->index; 103 | 104 | if(face->single_step) face->single_step_index = index; 105 | 106 | for(c = position; c < face->current_break; c++) { 107 | face->breaks[c] = face->breaks[c+1]; 108 | } 109 | 110 | DEBUG_PRINT("[-remove_breakpoint] Breakpoint %x at %llx removed\n", index, address); 111 | face->current_break -= 1; // decrement counter 112 | 113 | return 1; 114 | } 115 | 116 | EXPORT 117 | int remove_all_breaks(mach_port_t task) { 118 | interface *face = find_interface(task); 119 | 120 | int i = face->current_break-1; 121 | while(i>=0) { 122 | remove_breakpoint(task, face->breaks[i]->address); 123 | --i; 124 | } 125 | 126 | return 1; 127 | } 128 | 129 | EXPORT 130 | vm_address_t* list_breaks(mach_port_t task, int *count) { 131 | interface *face = find_interface(task); 132 | vm_address_t *list = safe_malloc(sizeof(vm_address_t) * face->current_break); 133 | int i = 0; 134 | 135 | while(i < face->current_break) { 136 | DEBUG_PRINT("[+list_breaks] Breakpoint [%lu] %lx\n", face->breaks[i]->index, face->breaks[i]->address); 137 | list[i] = face->breaks[i]->address; 138 | ++i; 139 | } 140 | *count = i; 141 | return list; 142 | } 143 | 144 | 145 | int find_break(mach_port_t task, vm_address_t find) { 146 | interface *face = find_interface(task); 147 | int i = 0; 148 | //DEBUG_PRINT("[+find_break] Searching FOR -- %lx\n", address); 149 | 150 | while(i < face->current_break) { 151 | //DEBUG_PRINT(("%d\n", i)); 152 | if(face->breaks[i]) { 153 | if(face->breaks[i]->address == find) 154 | return i; 155 | else if(face->breaks[i]->index == find) 156 | return i; 157 | } else { 158 | DEBUG_PRINT("[-find_break] INVALID Address or Index %d", i); 159 | getchar(); 160 | } 161 | i++; 162 | } 163 | 164 | return -1; 165 | } 166 | -------------------------------------------------------------------------------- /src/breakpoint.h: -------------------------------------------------------------------------------- 1 | #ifndef B_H 2 | #define B_H 3 | 4 | #include "mcdb.h" 5 | 6 | #define ONE_TIME 0 7 | #define PERSISTENT 1 8 | 9 | 10 | int add_breakpoint(mach_port_t task, vm_address_t patch_addr, int cont, callback handler); 11 | int remove_breakpoint(mach_port_t task, vm_address_t address); 12 | int remove_all_breaks(mach_port_t task); 13 | int find_break(mach_port_t task, vm_address_t address); 14 | vm_address_t* list_breaks(mach_port_t task, int* count); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/debug_main.c: -------------------------------------------------------------------------------- 1 | #include "debug_main.h" 2 | 3 | interface *find_interface(mach_port_t task) { 4 | int count = 0; 5 | 6 | while(count < bad_list.x) { 7 | if(bad_list.y[count]->task == task) { 8 | return bad_list.y[count]; 9 | } 10 | count++; 11 | } 12 | return NULL; 13 | } 14 | 15 | EXPORT 16 | int generic_callback(exc_msg_t *info_struct) { 17 | printf("\n[+generic_callback] BREAKPOINT CALLBACK %p\n", info_struct->code); 18 | return 1; 19 | } 20 | 21 | int handle_break(exc_msg_t *exc) { 22 | interface* face; 23 | int ret, our_break; 24 | kern_return_t kret; 25 | x86_thread_state64_t *break_state; 26 | 27 | break_state = get_state(exc->thread); 28 | our_break = find_break(exc->task, break_state->__rip-1); 29 | 30 | //find_break returns -1 aka big unsigned int 31 | if(our_break == -1) { 32 | return -1; 33 | } 34 | 35 | face = find_interface(exc->task); 36 | face->breaks[our_break]->hit++; 37 | breakpoint_struct *actual = face->breaks[our_break]; 38 | 39 | if(actual->flags == ONE_TIME) { 40 | remove_breakpoint(exc->task, actual->address); //restore original byte 41 | break_state->__rip = actual->address; //Restore original rip -- 1 byte back 42 | } else if(actual->flags == PERSISTENT) { 43 | face->single_step = actual; 44 | break_state->__rflags |= 0x100; //TRAP FLAG 45 | remove_breakpoint(exc->task, actual->address); 46 | break_state->__rip = actual->address; 47 | } 48 | 49 | kret = thread_set_state(exc->thread, x86_THREAD_STATE64, (thread_state_t)break_state, x86_THREAD_STATE64_COUNT); 50 | RETURN_ON_MACH_ERROR("[-handle_break] failed setting thread state", kret); 51 | 52 | if(actual->handler) { 53 | ret = actual->handler(exc); 54 | if(ret == -1) { 55 | return -1; 56 | } 57 | } 58 | 59 | return 1; 60 | } 61 | 62 | EXPORT 63 | int find_pid(mach_port_t task) { 64 | interface *face = find_interface(task); 65 | return face->pid; 66 | } 67 | 68 | int persistent_break(exc_msg_t *exc) { 69 | interface *face; 70 | x86_thread_state64_t *break_state; 71 | kern_return_t kret; 72 | 73 | break_state = get_state(exc->thread); 74 | face = find_interface(exc->task); 75 | DEBUG_PRINT("[+presistent_break] single_step %lx\n", face->single_step->address); 76 | 77 | add_breakpoint(exc->task, face->single_step->address, PERSISTENT, face->single_step->handler); 78 | 79 | free(face->single_step); 80 | face->single_step = NULL; 81 | break_state->__rflags &= ~0x100; 82 | 83 | kret = thread_set_state(exc->thread, x86_THREAD_STATE64, (thread_state_t)break_state, x86_THREAD_STATE64_COUNT); 84 | RETURN_ON_MACH_ERROR("[-persistent_break] failed setting thread state", kret); 85 | 86 | return 1; 87 | } 88 | 89 | // Handle EXCEPTION_DEFAULT behavior 90 | kern_return_t catch_mach_exception_raise(mach_port_t exception_port, 91 | mach_port_t thread, 92 | mach_port_t task, 93 | exception_type_t exception, 94 | mach_exception_data_t code, 95 | mach_msg_type_number_t codeCnt) { 96 | 97 | int ret, list; 98 | char *except; 99 | exc_msg_t *exc; 100 | interface *face; 101 | 102 | task_suspend(thread); 103 | 104 | // Populate structure to give to callback 105 | exc = safe_malloc(sizeof(exc_msg_t)); 106 | exc->exception_port = exception_port; 107 | exc->thread = thread; 108 | exc->task = task; 109 | exc->exception = exception; 110 | exc->code = code; 111 | exc->codeCnt = codeCnt; 112 | 113 | // -1 == failure to handle pass fail to kernel 114 | // 0 == continue on 115 | // 1 == all done 116 | 117 | ret = -1; 118 | face = find_interface(task); 119 | //REGISTER EXCEPTION HANDLER LOOP THROUGH EXCEPTION LIST CALLING EXCEPTION HANDLED FUNCITONS FIRST 120 | for(list = 0; list < face->current_exception; list++) { 121 | if(face->except_list[list]->exception == exception) { 122 | ret = face->except_list[list]->handler(exc); 123 | if(ret == -1) { 124 | return KERN_FAILURE; 125 | } else if(ret == 1){ 126 | return KERN_SUCCESS; 127 | } 128 | } 129 | } 130 | 131 | switch(exception) { 132 | case EXC_BREAKPOINT: //DEFAULT BREAKPOINT HANDLE HANDLED AFTER EXCEPTIONS ARE HANDLED 133 | if(face->single_step == NULL) { 134 | ret = handle_break(exc); 135 | if(ret == -1) { 136 | DEBUG_PRINT("[-catch_mach_exception_raise] HANDLE BREAK FAILURE %d\n", -1); 137 | return KERN_FAILURE; 138 | } 139 | } else { 140 | persistent_break(exc); 141 | break; 142 | } 143 | break; 144 | case EXC_SOFTWARE : /* Software generated exception */ //INT3 145 | case EXC_BAD_ACCESS : /* Could not access memory */ 146 | case EXC_BAD_INSTRUCTION: /* Instruction failed */ 147 | case EXC_ARITHMETIC : /* Arithmetic exception */ 148 | case EXC_EMULATION : /* Emulation instruction */ 149 | case EXC_SYSCALL : /* System calls. */ 150 | case EXC_MACH_SYSCALL : /* Mach system calls. */ 151 | case EXC_RPC_ALERT : /* RPC alert */ 152 | case EXC_CRASH : /* Abnormal process exit */ 153 | case EXC_RESOURCE : /* Hit resource consumption limit */ 154 | case EXC_GUARD : /* Violated guarded resource protections */ 155 | default: 156 | if (ret == 0){ 157 | break; 158 | } 159 | except = exception_to_string(exception); 160 | DEBUG_PRINT("[-catch_exception_raise] UNHANDLED EXCEPTION %s\n", except); 161 | thread_terminate(thread); 162 | break; 163 | } 164 | 165 | task_resume(thread); 166 | free(exc); 167 | 168 | return KERN_SUCCESS; 169 | } 170 | 171 | // Handle EXCEPTION_DEFAULT behavior 172 | kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port, 173 | mach_port_t thread, 174 | mach_port_t task, 175 | exception_type_t exception, 176 | mach_exception_data_t code, 177 | mach_msg_type_number_t codeCnt) { 178 | return KERN_FAILURE; 179 | } 180 | 181 | // Handle EXCEPTION_DEFAULT behavior 182 | kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, 183 | mach_port_t thread, 184 | mach_port_t task, 185 | exception_type_t exception, 186 | mach_exception_data_t code, 187 | mach_msg_type_number_t codeCnt) { 188 | return KERN_FAILURE; 189 | } 190 | 191 | static void* exception_server (mach_port_t exceptionPort) { 192 | mach_msg_return_t rt; 193 | mach_msg_header_t *msg; 194 | mach_msg_header_t *reply; 195 | 196 | msg = safe_malloc(sizeof(union __RequestUnion__mach_exc_subsystem)); 197 | reply = safe_malloc(sizeof(union __ReplyUnion__mach_exc_subsystem)); 198 | 199 | while (1) { 200 | rt = mach_msg(msg, MACH_RCV_MSG, 0, sizeof(union __RequestUnion__mach_exc_subsystem), exceptionPort, 0, MACH_PORT_NULL); 201 | 202 | if (rt!= MACH_MSG_SUCCESS) { 203 | DEBUG_PRINT("[-exception_server] MACH_RCV_MSG stopped, exit from exception_server thread :%d\n", 1); 204 | return "MACH_RCV_MSG_FAILURE"; 205 | } 206 | /* 207 | * Call out to the mach_exc_server generated by mig and mach_exc.defs. 208 | * This will in turn invoke one of: 209 | * mach_catch_exception_raise() 210 | * mach_catch_exception_raise_state() 211 | * mach_catch_exception_raise_state_identity() 212 | * .. depending on the behavior specified when registering the Mach exception port. 213 | */ 214 | mach_exc_server(msg, reply); 215 | 216 | // Send the now-initialized reply 217 | rt = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 218 | 219 | if (rt!= MACH_MSG_SUCCESS) { 220 | return "MACH_SEND_MSG_FAILURE"; 221 | } 222 | } 223 | } 224 | 225 | EXPORT 226 | void* kqueue_loop(int kp) { 227 | struct kevent ke; /* event that was triggered */ 228 | int i, list, ret, handled; 229 | mach_port_t task; 230 | exc_msg_t *exc; 231 | 232 | int infoPid = kp; 233 | task_for_pid(current_task(), infoPid, &task); 234 | interface* face = find_interface(task); 235 | 236 | 237 | DEBUG_PRINT("[+kqueue_loop] KQUEUE PID of prog: %d\n", infoPid); 238 | face->kq = kqueue(); /* create a new kernel event queue */ 239 | if (face->kq == -1) { 240 | fprintf(stderr, "[-kqueue_loop] %d\n", face->kq); 241 | } 242 | 243 | /* initalize kevent structure */ 244 | EV_SET(&ke, infoPid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_SIGNAL | NOTE_FORK | NOTE_EXIT | NOTE_EXEC , 0, 0); 245 | 246 | i = kevent(face->kq, &ke, 1, NULL, 0, NULL); //adding event 247 | 248 | if (face->kq == -1) { 249 | fprintf(stderr, "[-kqueue_loop] %d\n", face->kq); 250 | } 251 | 252 | for(;;) { 253 | i = kevent(face->kq, NULL, 0, &ke, 1, NULL); //checking for change kevent 254 | if (i==-1) { 255 | DEBUG_PRINT("[+kqueue_loop] kevent stopped, exit from kqueue_loop thread: %d\n", 1); 256 | return "KEVENT_STOPPED"; 257 | } 258 | 259 | task_suspend(task); 260 | 261 | exc = malloc(sizeof(exc_msg_t)); 262 | exc->task = task; 263 | exc->exception = ke.fflags; 264 | 265 | //REGISTER EXCEPTION HANDLER LOOP THROUGH EXCEPTION LIST CALLING EXCEPTION HANDLED FUNCITONS FIRST 266 | for(list = 0; list < face->current_exception; list++) { 267 | if(face->except_list[list]->exception & ke.fflags) { 268 | handled = 1; 269 | ret = face->except_list[list]->handler(exc); 270 | if(ret == -1) { 271 | fprintf(stderr, "[-kqueue_loop] UNABLE TO HANDLE EVENT: %d\n", ke.fflags); 272 | exit(-1); 273 | } 274 | } 275 | } 276 | if(handled == 0) { 277 | DEBUG_PRINT("[-kqueue_loop] FAILED TO HANDLE :/ %d\n", 0); 278 | task_resume(task); 279 | continue; 280 | } 281 | 282 | handled = 0; 283 | task_resume(task); 284 | } 285 | } 286 | 287 | mach_port_t get_task(pid_t infoPid) { 288 | kern_return_t kret; 289 | mach_port_t task; 290 | 291 | DEBUG_PRINT("[+getstate] Trying pid %d\n", infoPid); 292 | 293 | kret = task_for_pid(current_task(), infoPid, &task); 294 | RETURN_ON_MACH_ERROR("[-get_state] task_for_pid failed", kret); 295 | 296 | return task; 297 | } 298 | 299 | EXPORT 300 | int terminate_(mach_port_t task) { 301 | kern_return_t kret; 302 | pid_t pid; 303 | 304 | pid_for_task(task, &pid); 305 | kret = kill(pid, PT_KILL); 306 | mach_error("[-terminate] kill process status:" , kret); 307 | 308 | return 1; 309 | } 310 | 311 | EXPORT 312 | void test() { 313 | printf("[+test] KERN_SUCCESS\n"); 314 | } 315 | 316 | EXPORT 317 | mach_port_t attach(pid_t infoPid) { 318 | mach_port_t task; 319 | int count = 0; 320 | 321 | task = get_task(infoPid); 322 | if(task == 0) { 323 | int kret = 0; 324 | RETURN_ON_MACH_ERROR("[-attach] invalid pid", kret); 325 | } 326 | 327 | if(bad_list.max_attach == 0) { 328 | bad_list.max_attach = 1; 329 | } 330 | 331 | while(count < bad_list.x) { 332 | if(bad_list.y[count]->task == task) { 333 | int kret = 0; 334 | RETURN_ON_MACH_ERROR("[-attach] duplicate pid", kret); 335 | } 336 | count++; 337 | } 338 | 339 | if(bad_list.x >= (bad_list.max_attach - 1)) { 340 | DEBUG_PRINT("ALLOCATING MORE! CURRENTLY: %d\n", bad_list.max_attach); 341 | bad_list.y = realloc(bad_list.y, sizeof(interface*) * (bad_list.max_attach*2)); 342 | bad_list.max_attach *= 2; 343 | } 344 | 345 | 346 | bad_list.y[bad_list.x] = malloc(sizeof(interface*)); 347 | 348 | interface* tmp = malloc(sizeof(interface)); 349 | memset(tmp, 0, sizeof(interface)); 350 | 351 | tmp->task = task; 352 | tmp->pid = infoPid; 353 | tmp->current_break = 0; 354 | tmp->current_exception = 0; 355 | tmp->single_step = NULL; 356 | tmp->registered_exception_handler = 0; 357 | 358 | bad_list.y[bad_list.x++] = tmp; 359 | 360 | DEBUG_PRINT("ATTACHING TO PROCESS # %d\n", bad_list.x); 361 | return task; 362 | } 363 | 364 | void register_(mach_port_t task) { 365 | interface* face = find_interface(task); 366 | start(task, face->pid); 367 | face->registered_exception_handler = 1; 368 | } 369 | 370 | EXPORT 371 | mach_port_t suspend(mach_port_t task) { 372 | printf("SUSPENDING TASK!\n"); 373 | task_suspend(task); 374 | 375 | return task; 376 | } 377 | 378 | EXPORT 379 | mach_port_t continue_(mach_port_t task) { 380 | task_resume(task); 381 | 382 | return task; 383 | } 384 | 385 | EXPORT 386 | mach_port_t detach(mach_port_t task) { 387 | int ret=0; 388 | 389 | ret = remove_all_breaks(task); 390 | 391 | if (!ret) { 392 | DEBUG_PRINT("[-detach] Failed to remove all breaks %d\n", ret); 393 | } 394 | DEBUG_PRINT("[+detach] Removed all breaks %d\n", ret); 395 | ret = remove_all_exception_callbacks(task); 396 | 397 | if (!ret) { 398 | DEBUG_PRINT("[-detach] Failed to remove all exception callbacks %d\n", ret); 399 | } 400 | DEBUG_PRINT("[+detach] Removed all exception callbacks %d\n", ret); 401 | ret = stop(task); //stop debugger threads 402 | 403 | if (!ret) { 404 | DEBUG_PRINT("[-detach] Failed to detach debugger threads %d\n", ret); 405 | } 406 | DEBUG_PRINT("[+detach] Done detaching Debugger threads %d\n", ret); 407 | 408 | return 1; 409 | } 410 | 411 | EXPORT 412 | pid_t spawn_process(char *command, char *args[]) { 413 | pid_t pid = fork(); 414 | 415 | switch (pid) { 416 | case -1: // error 417 | perror("fork"); 418 | exit(1); 419 | case 0: // child process 420 | execv(command, args); // run the command 421 | perror("[-spawn_process] execl"); // execl doesn't return unless there is a problem 422 | exit(1); 423 | default: 424 | 425 | return pid; 426 | } 427 | 428 | } 429 | 430 | EXPORT 431 | mach_port_t run(char *command, char *args[]) { 432 | pid_t infoPid; 433 | 434 | infoPid = spawn_process(command, args); 435 | usleep(1 * 1000); 436 | return attach(infoPid); 437 | } 438 | 439 | EXPORT 440 | int start(mach_port_t task, pid_t infoPid) { 441 | kern_return_t kret; 442 | pthread_t tid[2]; 443 | interface* face = find_interface(task); 444 | 445 | kret = mach_port_allocate(current_task(), MACH_PORT_RIGHT_RECEIVE, &face->server_port); 446 | RETURN_ON_MACH_ERROR("[-start] mach_port_allocate failed", kret); 447 | 448 | kret = mach_port_insert_right(current_task(), face->server_port, face->server_port, MACH_MSG_TYPE_MAKE_SEND); 449 | RETURN_ON_MACH_ERROR("[-start] mach_port_insert_right failed", kret); 450 | 451 | kret = task_set_exception_ports(task, EXC_MASK_ALL, face->server_port, EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES, THREAD_STATE_NONE); 452 | RETURN_ON_MACH_ERROR("[-start] task_set_exception_ports failed", kret); 453 | 454 | int err = pthread_create(&tid[0], NULL, (void *(*)(void*))kqueue_loop, (void *)(unsigned long long)infoPid); 455 | if (err != 0) 456 | DEBUG_PRINT("\n[-start] can't create thread :[%s]", strerror(err)); 457 | else 458 | DEBUG_PRINT("\n[-start] Thread created successfully %d\n", 0); 459 | 460 | err = pthread_create(&tid[1], NULL, (void *(*)(void*))exception_server, (void *(*)(void*))(unsigned long long)face->server_port); 461 | if (err != 0) 462 | DEBUG_PRINT("\n[-start] can't create thread :[%s]", strerror(err)); 463 | else 464 | DEBUG_PRINT("\n[-start] Thread created successfully %d\n", 0); 465 | 466 | return 1; 467 | } 468 | 469 | //No synchronization issue so far, need to use synchronize if we run into any issues 470 | int stop(mach_port_t task) { 471 | MachExceptionHandlerData old_handler; 472 | thread_act_port_array_t threadList; 473 | mach_msg_type_number_t threadCount; 474 | unsigned int count; 475 | kern_return_t kret; 476 | interface *face; 477 | 478 | threadCount=0; 479 | task_threads(current_task(), &threadList, &threadCount); 480 | DEBUG_PRINT("[+stop] Thread count before detaching %d\n", threadCount); 481 | 482 | face = find_interface(task); 483 | close(face->kq); //close kqueue 484 | count = 1; 485 | kret = task_swap_exception_ports(current_task(), 486 | EXC_MASK_ALL, 487 | MACH_PORT_NULL, 488 | EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES, 489 | THREAD_STATE_NONE, 490 | (exception_mask_array_t) old_handler.masks, 491 | (mach_msg_type_number_t *) &old_handler.count, 492 | (exception_handler_array_t) old_handler.ports, 493 | (exception_behavior_array_t) old_handler.behaviors, 494 | (exception_flavor_array_t) old_handler.flavors); 495 | 496 | kret = mach_port_mod_refs(mach_task_self(), face->server_port, MACH_PORT_RIGHT_RECEIVE, -1); 497 | 498 | if (kret != KERN_SUCCESS) { 499 | RETURN_ON_MACH_ERROR("[-stop] mach_port_mod_refs failed", kret); 500 | } 501 | 502 | kret = mach_port_get_refs(mach_task_self(), face->server_port, MACH_PORT_RIGHT_RECEIVE, &count ); 503 | RETURN_ON_MACH_ERROR("[-stop] mach_port_get_refs failed", kret); 504 | 505 | 506 | if (face->server_port) { 507 | kret = mach_port_deallocate(current_task(),face->server_port); 508 | RETURN_ON_MACH_ERROR("[-stop] mach_port_deallocate failed", kret); 509 | } 510 | 511 | if (count) { 512 | DEBUG_PRINT("[-stop] failed to reset server port ref count exp:0 actual: %d\n", count); 513 | return 0; 514 | } 515 | 516 | task_threads(task, &threadList, &threadCount); 517 | DEBUG_PRINT("[+stop] Thread count after detaching %d\n", threadCount); 518 | 519 | face->registered_exception_handler = 0; 520 | count = 0; 521 | 522 | //REMOVE PID FROM BAD LIST TO ALLOW REATTACHING 523 | while(count < bad_list.x) { 524 | if(bad_list.y[count]->task == task) { 525 | break; 526 | } 527 | count++; 528 | } 529 | 530 | DEBUG_PRINT("TASK IS NUMBER: %d\n", count); 531 | 532 | int c; 533 | for(c = count; c < bad_list.x; c++) { 534 | bad_list.y[c] = bad_list.y[c+1]; 535 | } 536 | bad_list.x -= 1; 537 | 538 | return 1; 539 | } 540 | 541 | -------------------------------------------------------------------------------- /src/debug_main.h: -------------------------------------------------------------------------------- 1 | #ifndef D_H 2 | #define D_H 3 | #include "mcdb.h" 4 | #include "thread.h" 5 | 6 | extern boolean_t mach_exc_server (mach_msg_header_t *msg, mach_msg_header_t *reply); 7 | int handle_break(exc_msg_t *exc); 8 | mach_port_t get_task(pid_t infoPid); 9 | int start(mach_port_t task, pid_t infoPid); 10 | int generic_callback(exc_msg_t *info_struct); 11 | int generic_callback_2(exc_msg_t *info_struct); 12 | mach_port_t attach(pid_t infoPid); 13 | mach_port_t detach(mach_port_t task); 14 | int stop(mach_port_t task); 15 | int terminate_(mach_port_t task); 16 | void test(); 17 | void* kqueue_loop(int kp); 18 | int persistent_break(exc_msg_t *exc); 19 | mach_port_t suspend(mach_port_t task); 20 | mach_port_t continue_(mach_port_t task); 21 | pid_t spawn_process(char *command, char *args[]); 22 | mach_port_t run(char *command, char *args[]); 23 | interface* find_interface(mach_port_t task); 24 | void register_(mach_port_t thread); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/dyldcache_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights 7 | * Reserved. This file contains Original Code and/or Modifications of 8 | * Original Code as defined in and that are subject to the Apple Public 9 | * Source License Version 1.0 (the 'License'). You may not use this file 10 | * except in compliance with the License. Please obtain a copy of the 11 | * License at http://www.apple.com/publicsource and read it before using 12 | * this file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 19 | * License for the specific language governing rights and limitations 20 | * under the License." 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | 25 | /* 26 | * http://www.opensource.apple.com/source/system_cmds/system_cmds-496/fs_usage.tproj/fs_usage.c?txt 27 | * ReadSharedCacheMap has been modified to retrieve the dyld address info from dyld cache map file 28 | */ 29 | 30 | 31 | #include "dyldcache_parser.h" 32 | 33 | int scanline(char *inputstring, char **argv, int maxtokens) { 34 | int n = 0; 35 | char **ap = argv, *p, *val; 36 | 37 | for (p = inputstring; n < maxtokens && p != NULL; ) { 38 | 39 | while ((val = strsep(&p, " \t")) != NULL && *val == '\0'); 40 | 41 | *ap++ = val; 42 | n++; 43 | } 44 | *ap = 0; 45 | 46 | return n; 47 | } 48 | 49 | /* 50 | * caller is responsible for freeing the memory allocated here 51 | */ 52 | 53 | library_all_infos_struct* ReadSharedCacheMap(const char *path) { 54 | uint64_t b_address, e_address; 55 | char frameworkName[256]; 56 | char *tokens[64]; 57 | char buf[1024]; 58 | char *fnp; 59 | FILE *fd; 60 | int ntokens; 61 | int type; 62 | int linkedit_found = 0; 63 | char *substring, *ptr; 64 | int numFrameworks = 0; 65 | 66 | bzero(buf, sizeof(buf)); 67 | bzero(tokens, sizeof(tokens)); 68 | 69 | if ((fd = fopen(path, "r")) == 0) 70 | { 71 | return 0; 72 | } 73 | while (fgets(buf, 1023, fd)) { 74 | if (strncmp(buf, "mapping", 7)) 75 | break; 76 | } 77 | buf[strlen(buf)-1] = 0; 78 | 79 | frameworkName[0] = 0; 80 | 81 | int start; 82 | 83 | library_all_infos_struct* lib_all_infos =safe_malloc(sizeof(library_all_infos_struct)); 84 | lib_all_infos->count = 0; 85 | for (;;) { 86 | //Extract lib name from path name 87 | 88 | ptr = buf; 89 | substring = ptr; 90 | start = 0; 91 | while (*ptr) { 92 | if (*ptr == '/' && start == 0) { 93 | substring = ptr; 94 | start = 1; 95 | } 96 | ptr++; 97 | } 98 | 99 | strncpy(frameworkName, substring, 256); 100 | frameworkName[255] = 0; 101 | 102 | fnp = (char *)malloc(strlen(frameworkName) + 1); 103 | strcpy(fnp, frameworkName); 104 | 105 | while (fgets(buf, 1023, fd) && numFrameworks < (MAXINDEX - 2)) { 106 | /* 107 | * Get rid of EOL 108 | */ 109 | buf[strlen(buf)-1] = 0; 110 | 111 | ntokens = scanline(buf, tokens, 64); 112 | 113 | if (ntokens < 4) 114 | continue; 115 | 116 | if (strncmp(tokens[0], "__TEXT", 6) == 0) 117 | type = TEXT_R; 118 | else if (strncmp(tokens[0], "__DATA", 6) == 0) 119 | type = DATA_R; 120 | else if (strncmp(tokens[0], "__OBJC", 6) == 0) 121 | type = OBJC_R; 122 | else if (strncmp(tokens[0], "__IMPORT", 8) == 0) 123 | type = IMPORT_R; 124 | else if (strncmp(tokens[0], "__UNICODE", 9) == 0) 125 | type = UNICODE_R; 126 | else if (strncmp(tokens[0], "__IMAGE", 7) == 0) 127 | type = IMAGE_R; 128 | else if (strncmp(tokens[0], "__LINKEDIT", 10) == 0) 129 | type = LINKEDIT_R; 130 | else 131 | type = -1; 132 | 133 | if (type == LINKEDIT_R && linkedit_found) 134 | break; 135 | 136 | if (type != -1) { 137 | b_address = strtoull(tokens[1], 0, 16); 138 | e_address = strtoull(tokens[3], 0, 16); 139 | 140 | library_info_struct* new_lib_info = safe_malloc(sizeof(library_info_struct)); 141 | 142 | new_lib_info->b_address = b_address; 143 | new_lib_info->e_address = e_address; 144 | new_lib_info->r_type = type; 145 | new_lib_info->name = fnp; 146 | 147 | lib_all_infos->lib_info[lib_all_infos->count++] = new_lib_info; 148 | 149 | if (type == LINKEDIT_R) { 150 | linkedit_found = 1; 151 | } 152 | // #if DEBUG 153 | // printf("%s(%d): %qx-%qx\n", frameworkInfo[numFrameworks].name, type, b_address, e_address); 154 | // #endif 155 | 156 | numFrameworks++; 157 | } 158 | if (type == LINKEDIT_R) 159 | break; 160 | } 161 | if (fgets(buf, 1023, fd) == 0) 162 | break; 163 | 164 | buf[strlen(buf)-1] = 0; 165 | } 166 | fclose(fd); 167 | 168 | // #if DEBUG 169 | // for(int i=0;icount;i++) 170 | // { 171 | // library_info_struct* dyldinfo = lib_all_infos->lib_info[i]; 172 | 173 | // printf("%p %p %d %s \n", dyldinfo->b_address, dyldinfo->e_address, dyldinfo->r_type, dyldinfo->name); 174 | 175 | // } 176 | // #endif 177 | 178 | return lib_all_infos; 179 | } 180 | 181 | /* 182 | * caller is responsible for freeing the memory allocated here 183 | */ 184 | 185 | dyld_map* find_dyld_map(library_all_infos_struct* dyld_infos, char *dylib_path) { 186 | 187 | dyld_map* dyld_map_info = safe_malloc(sizeof(dyld_map)); 188 | int text_found=0, data_found=0; 189 | 190 | for(uint32_t i=0; i<=dyld_infos->count; i++) { 191 | library_info_struct* info = dyld_infos->lib_info[i]; 192 | 193 | if (!(strncmp(info->name, dylib_path, strlen(dylib_path)))) { 194 | 195 | if (info->r_type == TEXT_R) { 196 | dyld_map_info->name = info->name; 197 | dyld_map_info->image_start_address = info->b_address; 198 | dyld_map_info->image_end_address = info->e_address; 199 | 200 | text_found =1; 201 | } 202 | else if (info->r_type == DATA_R) { 203 | dyld_map_info->name = info->name; 204 | dyld_map_info->data_start_address = info->b_address; 205 | dyld_map_info->data_end_address = info->b_address; 206 | data_found =1; 207 | 208 | } 209 | 210 | } 211 | if (text_found==1 && data_found==1) { 212 | return dyld_map_info; 213 | 214 | } 215 | } 216 | return NULL; 217 | } 218 | -------------------------------------------------------------------------------- /src/dyldcache_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef DC_H 2 | #define DC_H 3 | 4 | #include "mcdb.h" 5 | 6 | #define MAXINDEX 2040 7 | 8 | #define TEXT_R 0 9 | #define DATA_R 1 10 | #define OBJC_R 2 11 | #define IMPORT_R 3 12 | #define UNICODE_R 4 13 | #define IMAGE_R 5 14 | #define LINKEDIT_R 6 15 | 16 | typedef struct { 17 | uint64_t b_address; 18 | uint64_t e_address; 19 | int r_type; 20 | char *name; 21 | }library_info_struct; 22 | 23 | typedef struct { 24 | library_info_struct *lib_info[MAXINDEX]; 25 | unsigned int count; 26 | }library_all_infos_struct; 27 | 28 | typedef struct { 29 | char* name; 30 | mach_vm_address_t image_start_address; 31 | mach_vm_address_t image_end_address; 32 | mach_vm_address_t data_start_address; 33 | mach_vm_address_t data_end_address; 34 | }dyld_map; 35 | 36 | int scanline(char *inputstring, char **argv, int maxtokens); 37 | library_all_infos_struct* ReadSharedCacheMap(const char *path); 38 | dyld_map* find_dyld_map(library_all_infos_struct* dyld_infos, char *dylib_path); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/exception.c: -------------------------------------------------------------------------------- 1 | #include "exception.h" 2 | 3 | EXPORT 4 | int add_exception_callback(mach_port_t task, callback handler, exception_type_t exception) { 5 | interface *face; 6 | exception_handler *my_exc; 7 | 8 | face = find_interface(task); 9 | if(!face->registered_exception_handler) { 10 | register_(task); 11 | } 12 | 13 | if(face->current_exception > MAX_EXCEPTION) { 14 | DEBUG_PRINT("[-add_exception_callback] TOO MANY EXCEPTION HANDLERS -- %d\n", face->current_exception); 15 | return -1; 16 | } 17 | 18 | my_exc = safe_malloc(sizeof(exception_handler)); 19 | my_exc->exception = exception; 20 | my_exc->handler = handler; 21 | my_exc->task = task; 22 | 23 | if(face->max_exception == 0) { 24 | face->max_exception = 1; 25 | } 26 | 27 | if(face->current_exception >= (face->max_exception - 1)) { 28 | DEBUG_PRINT("ALLOCATING MORE EXC! CURRENTLY: %d\n", face->current_exception); 29 | face->except_list = safe_realloc(face->except_list, sizeof(exception_handler*) * (face->max_exception*2)); 30 | face->max_exception *= 2; 31 | } 32 | 33 | face->except_list[face->current_exception] = my_exc; 34 | face->current_exception += 1; 35 | 36 | return 1; 37 | } 38 | 39 | EXPORT 40 | int remove_exception_callback(exception_handler *exc) { 41 | interface *face; 42 | int c, position; 43 | 44 | face = find_interface(exc->task); 45 | position = -1; 46 | for(c = 0; c < face->current_exception; c++) { 47 | if(face->except_list[c] == exc) { 48 | position = c; 49 | break; 50 | } 51 | } 52 | 53 | if(position == -1) { 54 | DEBUG_PRINT("[-remove_exception_callback] ERROR finding exception callback %d", 1 ); 55 | return -1; 56 | } 57 | 58 | for(c = position; c < face->current_exception; c++) { 59 | face->except_list[c] = face->except_list[c+1]; 60 | } 61 | face->current_exception -= 1; 62 | DEBUG_PRINT("[+remove_exception_callback] EXCEPTION REMOVED %d\n", 1); 63 | 64 | return 1; 65 | } 66 | 67 | EXPORT 68 | int remove_all_exception_callbacks(mach_port_t task) { 69 | interface *face = find_interface(task); 70 | int i = 0; 71 | while(i < face->current_exception) { 72 | DEBUG_PRINT("[+remove_all_exception_callbacks] exception callback: %s removed\n", 73 | exception_to_string(face->except_list[i]->exception)); 74 | face->except_list[i]=NULL; 75 | ++i; 76 | } 77 | face->current_exception = 0; 78 | 79 | return 1; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/exception.h: -------------------------------------------------------------------------------- 1 | #ifndef E_H 2 | #define E_H 3 | #include "mcdb.h" 4 | 5 | int add_exception_callback(mach_port_t task, callback handler, exception_type_t exception); 6 | int remove_exception_callback(exception_handler *exc); 7 | int remove_all_exception_callbacks(mach_port_t task); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/mcdb.h: -------------------------------------------------------------------------------- 1 | #ifndef U_H 2 | #define U_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "./.mach_gen/mach_exc.h" 26 | #include "dyldcache_parser.h" 27 | 28 | #define EXPORT __attribute__((visibility("default"))) 29 | 30 | #define MAX_BREAKS 100 31 | 32 | #define DEBUG_PRINT(fmt, ...) \ 33 | do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) 34 | 35 | #define EXIT_ON_MACH_ERROR(msg, retval) \ 36 | if (kret != KERN_SUCCESS) {mach_error(msg ":" , kret); exit((retval)); } 37 | 38 | #define RETURN_ON_MACH_ERROR(msg, retval) \ 39 | if (kret != KERN_SUCCESS) {\ 40 | return 0;\ 41 | } 42 | 43 | #define UNREFERENCED_PARAMETER(x) x 44 | 45 | #define MAX_EXCEPTION 100 46 | 47 | #ifndef DEBUG 48 | #define DEBUG 0 49 | #endif 50 | 51 | enum state {SUSPEND, CONTINUE}; 52 | 53 | typedef struct exc_msg { 54 | mach_port_t exception_port; 55 | mach_port_t thread; 56 | mach_port_t task; 57 | exception_type_t exception; 58 | mach_exception_data_t code; 59 | mach_msg_type_number_t codeCnt; 60 | }exc_msg_t; 61 | 62 | typedef int (*callback)(exc_msg_t *info_struct); 63 | 64 | // change 65 | typedef struct { 66 | vm_offset_t original; 67 | vm_address_t address; 68 | callback handler; 69 | int flags; 70 | vm_address_t index; // same type as address for remove_breakpoint 71 | unsigned int hit; 72 | }breakpoint_struct; 73 | 74 | // breakpoint_struct *breaks[MAX_BREAKS]; 75 | // unsigned int current_break; 76 | // unsigned int single_step_index; // HACK FOR NOW FIX ME LATER 77 | // breakpoint_struct *single_step; 78 | 79 | 80 | #include "breakpoint.h" 81 | 82 | 83 | typedef struct exception_handler_type { 84 | exception_type_t exception; 85 | callback handler; 86 | mach_port_t task; 87 | }exception_handler; 88 | 89 | 90 | // exception_handler* except_list[MAX_EXCEPTION]; 91 | // int current_exception; 92 | 93 | #include "exception.h" 94 | #include "memory.h" 95 | 96 | typedef struct interface { 97 | mach_port_t task; 98 | pid_t pid; 99 | 100 | breakpoint_struct** breaks; 101 | unsigned int current_break; 102 | unsigned int max_break; 103 | 104 | unsigned int single_step_index; // HACK FOR NOW FIX ME LATER 105 | breakpoint_struct *single_step; 106 | 107 | exception_handler** except_list; 108 | unsigned int current_exception; 109 | unsigned int max_exception; 110 | 111 | 112 | int registered_exception_handler; 113 | int kq; 114 | mach_port_t server_port; 115 | }interface; 116 | 117 | typedef struct { 118 | interface **y; 119 | unsigned int x; 120 | unsigned int max_attach; 121 | }point_list; 122 | 123 | point_list bad_list; 124 | 125 | 126 | #include "debug_main.h" 127 | 128 | void* safe_malloc(size_t x); 129 | void* safe_realloc(void *ptr, size_t x); 130 | void print_byte(char *byte); 131 | char* exception_to_string(exception_type_t exc); 132 | void print_bytes(char *byte, int len); 133 | 134 | #define MAX_EXCEPTION_PORTS 32 135 | 136 | typedef struct { 137 | exception_mask_t masks[MAX_EXCEPTION_PORTS]; 138 | exception_handler_t ports[MAX_EXCEPTION_PORTS]; 139 | exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; 140 | thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; 141 | mach_msg_type_number_t count; 142 | }MachExceptionHandlerData; 143 | 144 | // int kq; 145 | 146 | // mach_port_t server_port; 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef M_H 2 | #define M_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "mcdb.h" 8 | 9 | #define MAX_REGION 100 10 | 11 | static jmp_buf jbuf; 12 | 13 | typedef enum {__TEXT, __DATA} region_type; 14 | 15 | #define MAX_NO 2048 16 | 17 | #define DYLD_CACHE_MAP "/var/db/dyld/dyld_shared_cache_x86_64h.map" 18 | 19 | #define MAX_REGION 100 20 | 21 | typedef struct vm_region_info{ 22 | unsigned int region_type; 23 | mach_vm_address_t address_start; 24 | mach_vm_address_t address_end; 25 | mach_vm_size_t size; 26 | unsigned int protection; 27 | unsigned int max_protection; 28 | unsigned int share_mode; 29 | char region_detail[PATH_MAX]; //it could be a path to loaded library or other info about the region 30 | }vm_region_t; 31 | 32 | typedef struct { 33 | mach_vm_address_t start_address; 34 | mach_vm_address_t end_address; 35 | region_type type; 36 | size_t size; 37 | char *fpath; 38 | unsigned int protection; 39 | //todo prot, moddate 40 | }dyld_info_struct; 41 | 42 | typedef struct { 43 | dyld_info_struct **dyld; 44 | unsigned int count; 45 | }dyld_all_infos_struct; 46 | 47 | 48 | vm_offset_t read_memory(mach_port_t task, vm_address_t address, size_t size); 49 | int write_memory(mach_port_t task, vm_address_t address, vm_offset_t data, mach_vm_size_t len); 50 | mach_vm_address_t* read_memory_allocate(mach_port_t task, mach_vm_address_t address, size_t size); 51 | vm_address_t get_base_address(mach_port_t task); 52 | char* get_protection(vm_prot_t protection); 53 | kern_return_t change_page_protection(mach_port_t task, vm_address_t patch_addr, vm_prot_t new_protection); 54 | mach_vm_address_t* allocate(mach_port_t task, vm_address_t patch_addr, size_t size, int flags); 55 | int free_memory(mach_port_t task, vm_address_t address, size_t size); 56 | vm_region_t** get_memory_map(mach_port_t task, mach_vm_address_t address, int* region); 57 | dyld_info_struct** get_dyld_map(mach_port_t task, uint32_t *no_of_dyld); 58 | vm_region_basic_info_data_64_t* get_region_info(mach_port_t task, mach_vm_address_t address); 59 | char* user_tag_to_string(unsigned int user_tag); 60 | char* get_page_protection(mach_port_t task, vm_address_t patch_addr); 61 | mach_vm_address_t allocate_space(mach_port_t task, size_t size, int flags); 62 | mach_vm_address_t inject_code(mach_port_t task, char* code, int length); 63 | mach_vm_address_t write_bytes(mach_port_t task, mach_vm_address_t address, char* code, int length); 64 | uint64_t get_image_size(mach_port_t task, mach_vm_address_t address); 65 | #endif 66 | -------------------------------------------------------------------------------- /src/python/examples/aslr_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import time 4 | import struct 5 | import os.path 6 | sys.path.append( 7 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 8 | 9 | from libs import MacDbg 10 | from libs.util import * 11 | from libs.const import * 12 | from subprocess import Popen, PIPE 13 | 14 | 15 | # Scan system and check for libraries loaded at same base address 16 | 17 | def base_addr(dbg, name): 18 | 19 | if dbg.base_address == 0x100000000: 20 | print dbg.color_red("BASE ADDRESS == LOAD ADDRESS :( -- PID - " + str(dbg.pid)), dbg.color_green(" - NAME " + name) 21 | 22 | dbg.detach() 23 | 24 | if __name__ == "__main__": 25 | 26 | tmp = MacDbg() 27 | 28 | process = Popen(["ps", "aux"], stdout=PIPE) 29 | (output, err) = process.communicate() 30 | pids = output.split("\n") 31 | for i in pids: 32 | x = i.split() 33 | 34 | try: 35 | pid = x[1] 36 | name = x[10] 37 | name = name[name.find("/")+1:] 38 | tmp.attach(int(pid), 1) 39 | except: 40 | continue 41 | 42 | if tmp.task == 0: 43 | tmp.color_red("BAD PID CONTINUING") 44 | continue 45 | base_addr(tmp, name) 46 | 47 | -------------------------------------------------------------------------------- /src/python/examples/basic_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import struct 6 | from ctypes import CFUNCTYPE 7 | import os.path 8 | sys.path.append( 9 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 10 | 11 | from libs import MacDbg 12 | from libs.util import * 13 | from libs.const import * 14 | 15 | def generic_callback(info_struct): 16 | #turn info struct pointer into a dict 17 | info_struct = dbg.exc_dict(info_struct.contents) 18 | 19 | print LOG("Breakpoint callback: @ rip == "), 20 | x = dbg.get_thread_state(info_struct["thread"]) 21 | print hex(x["rip"]) 22 | dbg.info_pretty_print(info_struct["thread"]) 23 | 24 | #set state example 25 | #change first number to be printed out from 1 to be 99 26 | a = x["rip"] & 0x000000fff 27 | if a == 0xd90: 28 | x["rdi"] = 99 29 | dbg.set_thread_state(info_struct["thread"], x) 30 | 31 | return 1 32 | 33 | def generic_callback_2(info_struct): 34 | print dbg.color_pink(LOG("FORK Callback")) 35 | raw_input("") 36 | return 1 37 | 38 | def generic_callback_3(info_struct): 39 | print dbg.color_pink(LOG("EXEC Callback")) 40 | raw_input("") 41 | return 1 42 | 43 | def generic_callback_4(info_struct): 44 | print dbg.color_red(LOG("EXIT Callback")) 45 | raw_input("") 46 | exit(-1) 47 | 48 | def generic_callback_5(info_struct): 49 | print dbg.color_pink(LOG("SIGNAL Callback")) 50 | raw_input("") 51 | return 1 52 | 53 | def generic_callback_9(info_struct): 54 | print dbg.color_red(LOG("OMG A CRASH")) 55 | raw_input("") 56 | #dbg.task_terminate(info_struct.contents.task); 57 | exit(1) 58 | 59 | 60 | def debugger(dbg, kill = 0): 61 | try: 62 | prog_base_addr = dbg.base_address 63 | print "[+] Base address: " + hex(prog_base_addr) 64 | 65 | gen_callback = dbg.make_callback(generic_callback) 66 | 67 | #Breakpoint by address 68 | dbg.add_breakpoint(prog_base_addr+0xe3a, PERSISTENT, gen_callback) 69 | #Breakpoint by offset from base address 70 | dbg.add_breakpoint(0xeb6, PERSISTENT, gen_callback) 71 | #Breakpoint by name 72 | dbg.add_breakpoint("abc", PERSISTENT, gen_callback) 73 | #Breakpoint by name plus offset 74 | dbg.add_breakpoint("main", PERSISTENT, gen_callback, 62) 75 | 76 | dbg.list_breaks(); 77 | 78 | gen_callback_2 = dbg.make_callback(generic_callback_2) 79 | gen_callback_3 = dbg.make_callback(generic_callback_3) 80 | gen_callback_4 = dbg.make_callback(generic_callback_4) 81 | gen_callback_5 = dbg.make_callback(generic_callback_5) 82 | gen_callback_9 = dbg.make_callback(generic_callback_9) 83 | 84 | dbg.add_exception_callback(gen_callback_9, EXC_BAD_ACCESS) 85 | 86 | # tracking process creation 87 | dbg.add_exception_callback(gen_callback_3, NOTE_EXEC) 88 | dbg.add_exception_callback(gen_callback_2, NOTE_FORK) 89 | dbg.add_exception_callback(gen_callback_5, NOTE_SIGNAL) 90 | dbg.add_exception_callback(gen_callback_4, NOTE_EXIT) 91 | 92 | dbg.disass(prog_base_addr, 4) 93 | 94 | 95 | print "CONTINUING" 96 | #start(task, infoPid); 97 | dbg.resume() 98 | time.sleep(10) 99 | dbg.detach(kill) 100 | except NameError as e: 101 | # except: 102 | # e = sys.exc_info()[0] 103 | print e 104 | raw_input("?") 105 | 106 | if __name__ == "__main__": 107 | argv = sys.argv 108 | cmd = "./test_prog.app" 109 | dbg = MacDbg() 110 | 111 | if (len(argv) < 2): 112 | arr = (c_char_p * 2)() 113 | arr[0] = cmd 114 | arr[1] = 0 115 | 116 | dbg.run(arr[0], arr) 117 | pid = dbg.pid 118 | else: 119 | pid = int(argv[1]) 120 | dbg.attach(pid) 121 | 122 | if dbg.task == 0: 123 | print "Failed to attach Check PID" 124 | exit(0) 125 | 126 | pid = dbg.pid 127 | print "[+] Attached to task # %s\n" % str(dbg.task) 128 | 129 | debugger(dbg, 1) 130 | 131 | dbg.terminate_() 132 | 133 | print "\n[+] Done!" 134 | -------------------------------------------------------------------------------- /src/python/examples/dump_bin_sip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import time 4 | import struct 5 | import os.path 6 | sys.path.append( 7 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 8 | 9 | from libs import MacDbg 10 | from libs.util import * 11 | from libs.const import * 12 | 13 | 14 | def debugger(dbg, kill = 0): 15 | dbg.suspend() 16 | prog_base_addr = dbg.base_address 17 | print "[+] Base address: " + hex(prog_base_addr) 18 | 19 | print hex(dbg.base_address) 20 | 21 | program = dbg.dump_binary() 22 | output = file("decrypt.bin", "w+").write(program) 23 | print "ALL DONE!" 24 | dbg.detach() 25 | 26 | if __name__ == "__main__": 27 | if len(sys.argv) < 2: 28 | print "USAGE [pid]" 29 | exit() 30 | 31 | pid = int(sys.argv[1]) 32 | dbg = MacDbg() 33 | dbg.attach(pid) 34 | 35 | if dbg.task == 0: 36 | print "Failed to attach Check PID" 37 | exit(0) 38 | 39 | pid = dbg.pid 40 | print "[+] Attached to task # %s\n" % str(dbg.task) 41 | 42 | raw_input("press enter to continue") 43 | dbg.reload() 44 | debugger(dbg, 1) 45 | 46 | -------------------------------------------------------------------------------- /src/python/examples/dump_binary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import time 4 | import struct 5 | import os.path 6 | sys.path.append( 7 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 8 | 9 | from libs import MacDbg 10 | from libs.util import * 11 | from libs.const import * 12 | 13 | 14 | def debugger(dbg, kill = 0): 15 | dbg.suspend() 16 | prog_base_addr = dbg.base_address 17 | print "[+] Base address: " + hex(prog_base_addr) 18 | 19 | print hex(dbg.base_address) 20 | 21 | program = dbg.dump_binary() 22 | output = file("output.bin", "w+").write(program) 23 | print "ALL DONE!" 24 | dbg.detach() 25 | 26 | if __name__ == "__main__": 27 | if len(sys.argv) < 2: 28 | print "USAGE [pid]" 29 | exit() 30 | 31 | pid = int(sys.argv[1]) 32 | dbg = MacDbg() 33 | dbg.attach(pid) 34 | 35 | if dbg.task == 0: 36 | print "Failed to attach Check PID" 37 | exit(0) 38 | 39 | pid = dbg.pid 40 | print "[+] Attached to task # %s\n" % str(dbg.task) 41 | 42 | debugger(dbg, 1) 43 | 44 | -------------------------------------------------------------------------------- /src/python/examples/libmcdb.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/examples/libmcdb.dylib -------------------------------------------------------------------------------- /src/python/examples/malloc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/examples/malloc -------------------------------------------------------------------------------- /src/python/examples/malloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void* x(int y){ 7 | return malloc(y); 8 | } 9 | 10 | int main(){ 11 | for(int i = 0; i < 10; i++){ 12 | sleep(2); 13 | x(5); 14 | } 15 | printf("DONE\n"); 16 | } -------------------------------------------------------------------------------- /src/python/examples/malloc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import struct 6 | from ctypes import CFUNCTYPE 7 | import os.path 8 | sys.path.append( 9 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 10 | 11 | from libs import MacDbg 12 | from libs.util import * 13 | from libs.const import * 14 | 15 | #MALLOC TRACER FOR FIREFOX ATTACH TO FIREFOX AND IT WILL HOOK ON ALL MALLOCS AND SHOW RETURN ADDRESSES 16 | #WARNING THIS IS SLOW MALLOC IS CALLED A LOT 17 | 18 | def ret_callback(info): 19 | x = dbg.get_thread_state(info.contents.thread) 20 | print dbg.color_white(" RETURN --> " ), 21 | print dbg.color_yellow(hex(x["rax"])) 22 | return 1 23 | 24 | def generic_callback(info_struct): 25 | x = dbg.get_thread_state(info_struct.contents.thread) 26 | size = x["rdi"] 27 | k = struct.unpack(" " ), 19 | print dbg.color_yellow(hex(x["rax"])) 20 | return 1 21 | 22 | def generic_callback(info_struct): 23 | x = dbg.get_thread_state(info_struct.contents.thread) 24 | size = x["rdi"] 25 | print 26 | k = struct.unpack(" " + dbg.color_pink(dbg.read_memory(i, 200)) 24 | 25 | print "Done" 26 | dbg.detach() 27 | 28 | if __name__ == "__main__": 29 | if len(sys.argv) < 3: 30 | print "USAGE [pid] [search_string]" 31 | exit() 32 | dbg = MacDbg() 33 | 34 | pid = int(sys.argv[1]) 35 | dbg.attach(pid) 36 | 37 | if dbg.task == 0: 38 | exit(0) 39 | 40 | print "[+] Attached to task # %s\n" % str(dbg.task) 41 | 42 | debugger(dbg, sys.argv[2]) 43 | 44 | print "\n[+] Done!" 45 | -------------------------------------------------------------------------------- /src/python/examples/search_multiple.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import time 4 | import struct 5 | import os.path 6 | sys.path.append( 7 | os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 8 | 9 | from libs import MacDbg 10 | from libs.util import * 11 | from libs.const import * 12 | 13 | # Search multiple programs at once put pids in file called pid 14 | 15 | def search_mem(dbg, search): 16 | 17 | print dbg.color_white("BASE ADDRESS: " + hex(dbg.base_address)) 18 | 19 | search_results = dbg.search_string(search, dbg.base_address, dbg.get_image_size()*1000) 20 | 21 | if len(search_results) > 0: 22 | for i in search_results: 23 | print dbg.color_green(hex(i)) + " --> " + dbg.color_pink(dbg.read_memory(i, 40)) 24 | dbg.detach() 25 | return 1 26 | else: 27 | dbg.detach() 28 | return 0 29 | 30 | if __name__ == "__main__": 31 | 32 | print "Usage ./search_multiple.py [search]" 33 | search = sys.argv[1] 34 | tmp = MacDbg() 35 | 36 | pids = file("pid").readlines() 37 | debuggers = [] 38 | print tmp.color_red("Searching for string: " + search) 39 | count = 0 40 | for i in pids: 41 | print tmp.color_green("ATTACHING TO: " + str(int(i))) 42 | tmp.attach(int(i), 1) 43 | if tmp.task == 0: 44 | raw_input("????") 45 | tmp.color_red("BAD PID EXITING") 46 | x = search_mem(tmp, search) 47 | if x == 1: 48 | print tmp.color_pink("FOUND PROG PID = " + str(i)) 49 | 50 | -------------------------------------------------------------------------------- /src/python/examples/sip_run: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/examples/sip_run -------------------------------------------------------------------------------- /src/python/examples/sip_run.c: -------------------------------------------------------------------------------- 1 | //Pass in the program to run attach saf.py and press enter to bypass sip 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | pid_t spawn_process(char *command, char *args[]) { 8 | execv(command, args); // run the command 9 | } 10 | 11 | int main(int argc, char** argv){ 12 | pid_t pid; 13 | char *my_args[2]; 14 | my_args[0] = argv[1]; 15 | my_args[1] = NULL; 16 | 17 | printf("My process ID : %d\n Hit enter to spawn process\n", getpid()); 18 | getchar(); 19 | pid = spawn_process(argv[1], my_args); 20 | printf("%d\n", pid); 21 | } -------------------------------------------------------------------------------- /src/python/examples/test_prog.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/examples/test_prog.app -------------------------------------------------------------------------------- /src/python/examples/thread_test_prog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/examples/thread_test_prog -------------------------------------------------------------------------------- /src/python/examples/thread_test_prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test file to debug. Using sudo ./debugger and attach to pid. 3 | * Breakpoint is added after user inputs f or c 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | char* blue = "blue"; 10 | 11 | int abc(int x, int y, int z) { 12 | printf("%d %d %d\n", x,y,z); 13 | return 0; 14 | } 15 | 16 | int cde() { 17 | while(1){ 18 | sleep(10); 19 | printf("HELLO\n"); 20 | } 21 | return 0; 22 | } 23 | 24 | int main() { 25 | int tid; 26 | int err = pthread_create(&tid, NULL, (void *(*)(void*))cde, 0); 27 | if (err != 0) 28 | printf("\n[-start] can't create thread :[%s]", strerror(err)); 29 | else 30 | printf("\n[-start] Thread created successfully %d\n", 0); 31 | 32 | char c; 33 | int pid; 34 | c = *blue; 35 | printf("My process ID : %d\n", getpid()); 36 | printf("ADDRESS OF ABC: %p\n", abc); 37 | printf("ENTER f for fork c for crash or anything else to continue\n"); 38 | while(1) { 39 | c = getchar(); 40 | 41 | if(c == 'f') { 42 | pid = fork(); 43 | if(pid == 0) { 44 | exit(-1); 45 | } 46 | } 47 | 48 | if (c == 'c') { 49 | blue = 0; 50 | *blue = 99; 51 | } 52 | 53 | printf("%c\n", c ); 54 | abc(1,2,3); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/python/interface/interface.py: -------------------------------------------------------------------------------- 1 | from interface_commands import * 2 | from py_libs.utils.util import * 3 | 4 | from ctypes import * 5 | import sys 6 | from ctypes import * 7 | 8 | from interface_commands import * 9 | from libs.util import * 10 | 11 | PROGRAM_RUNNING = False 12 | 13 | # Command to function association 14 | commands = { 15 | "info": info, "help": help, 16 | "run": run, "quit": quit, "detach": detach, 17 | "print": do_print, "attach": attach, 18 | "kill": kill, "process list": process_list, 19 | "break": set_break, "tbreak": set_temp_break, 20 | "examine": examine, "disassemble": disassemble, 21 | "x": examine, "clear": clear, "delete": delete, 22 | "continue": do_continue, "c": do_continue, 23 | "step": step, "next": next, "read_regs": read_regs, 24 | "write_regs": write_regs, "k": backtrace 25 | } 26 | 27 | # Given command string, determines what function to run 28 | def interpret_command(dbg, cmd_line): 29 | global PROGRAM_RUNNING 30 | 31 | cmd = cmd_line.split(" ") 32 | command_params = cmd[1:] 33 | command_func = [c for c in commands.keys() if c.find(cmd[0]) == 0] 34 | single_char_cmd = [c for c in command_func if len(c) == 1] 35 | command_func = single_char_cmd if len(single_char_cmd) == 1 else command_func 36 | if len(command_func) == 0: 37 | ERR("Command not found: {0}".format(cmd[0])) 38 | return 39 | if len(command_func) > 1: 40 | ERR("Ambiguous command given: {0}, could match: {1}".format(cmd[0], ", ".join(command_func))) 41 | return 42 | 43 | command_func = command_func[0] 44 | cmd_ret = commands[command_func](dbg, command_params) 45 | if cmd_ret != None: 46 | PROGRAM_RUNNING = cmd_ret 47 | 48 | # TODO: Handle ctrl+c: send sigint to program 49 | 50 | def debug_shell(): 51 | global PROGRAM_RUNNING 52 | 53 | debugger = MacDbg() 54 | 55 | while True: 56 | if not PROGRAM_RUNNING: 57 | cmd = raw_input("mdbg$ ") 58 | interpret_command(debugger, cmd) 59 | else: 60 | # Waiting for program to hit breakpoint 61 | pass 62 | 63 | if __name__ == "__main__": 64 | debug_shell() 65 | -------------------------------------------------------------------------------- /src/python/interface/interface_commands.py: -------------------------------------------------------------------------------- 1 | from py_libs.macdbg import MacDbg 2 | from py_libs.structs import ExcMsg, X86ThreadState64, BreakpointStruct, ExceptionHandler, VmRegionBasicInfo64, VmRegion 3 | from py_libs.utils.enums import * 4 | 5 | from py_libs.utils.util import * 6 | 7 | from ctypes import * 8 | import os, psutil, time 9 | 10 | def generic_callback(info_struct): 11 | LOG("Breakpoint hit") 12 | print info_struct.contents 13 | return 1 14 | 15 | def info(dbg, params): 16 | usage = "\tUsage: info proc" 17 | 18 | if len(params) == 0: 19 | print usage 20 | return 21 | 22 | if params[0] == "proc": 23 | base_addr = dbg.get_base_address() 24 | if base_addr: 25 | print "Process Base: {0}".format(base_addr) 26 | 27 | def help(dbg, params): 28 | pass 29 | 30 | def run(dbg, params): 31 | usage = "\tUsage: run [cmd] [args...]" 32 | 33 | if len(params) == 0: 34 | print usage 35 | return 36 | 37 | p = subprocess.Popen(params, 38 | stdout=subprocess.PIPE, 39 | stderr=subprocess.STDOUT) 40 | return iter(p.stdout.readline, b'') 41 | 42 | def quit(dbg, params): 43 | try: 44 | dbg.detach() 45 | except: 46 | pass 47 | print "Bye" 48 | exit(0) 49 | 50 | def do_print(dbg, params): 51 | usage = "\tUsage: print [args...]" 52 | 53 | if len(params) == 0: 54 | print usage 55 | return 56 | 57 | return eval(" ".join(params)) 58 | 59 | def process_list(dbg, params): 60 | def print_processes(process=""): 61 | for proc in psutil.process_iter(): 62 | try: 63 | pinfo = proc.as_dict(attrs=['pid', 'name']) 64 | except psutil.NoSuchProcess: 65 | pass 66 | else: 67 | if process in pinfo["name"]: 68 | print "[{0}]: {1}".format(pinfo["pid"], pinfo["name"]) 69 | 70 | if len(params) == 1: 71 | print_processes(params[0]) 72 | else: 73 | print_processes() 74 | 75 | def attach(dbg, params): 76 | usage = "\tUsage: attach [pid]" 77 | 78 | if len(params) == 0: 79 | print usage 80 | return 81 | 82 | try: 83 | pid = int(params[0]) 84 | except ValueError: 85 | ERR("Given pid is not valid: {0}".format(pid)) 86 | 87 | dbg.attach(pid) 88 | 89 | CALLBKFUNC = CFUNCTYPE(c_int, POINTER(ExcMsg)) 90 | gen_callback = CALLBKFUNC(generic_callback) 91 | 92 | dbg.add_exception_callback(gen_callback, EXC_BAD_ACCESS) 93 | dbg.add_exception_callback(gen_callback, NOTE_EXEC) 94 | dbg.add_exception_callback(gen_callback, NOTE_FORK) 95 | dbg.add_exception_callback(gen_callback, NOTE_SIGNAL) 96 | dbg.add_exception_callback(gen_callback, NOTE_EXIT) 97 | 98 | def detach(dbg, params): 99 | dbg.detach() 100 | 101 | def kill(dbg, params): 102 | os.kill(dbg.pid, signal.SIGKILL) 103 | 104 | def set_break(dbg, params): 105 | usage = "\tUsage: break [addr]" 106 | 107 | if len(params) == 0: 108 | print usage 109 | return 110 | 111 | CALLBKFUNC = CFUNCTYPE(c_int, POINTER(ExcMsg)) 112 | gen_callback = CALLBKFUNC(generic_callback) 113 | 114 | try: 115 | addr = int(params[0], 0) 116 | except ValueError: 117 | ERR("Given address is not valid: {0}".format(params[0])) 118 | return 119 | 120 | CALLBKFUNC = CFUNCTYPE(c_int, POINTER(ExcMsg)) 121 | gen_callback = CALLBKFUNC(generic_callback) 122 | 123 | base_addr = dbg.get_base_address() 124 | dbg.add_breakpoint(base_addr+0xe0e, PERSISTENT, gen_callback) 125 | dbg.add_breakpoint(base_addr+0xe3a, PERSISTENT, gen_callback) 126 | dbg.list_breaks(); 127 | 128 | gen_callback_2 = CALLBKFUNC(generic_callback) 129 | gen_callback_3 = CALLBKFUNC(generic_callback) 130 | gen_callback_4 = CALLBKFUNC(generic_callback) 131 | gen_callback_5 = CALLBKFUNC(generic_callback) 132 | gen_callback_9 = CALLBKFUNC(generic_callback) 133 | 134 | dbg.add_exception_callback(gen_callback_9, EXC_BAD_ACCESS) 135 | 136 | # tracking process creation 137 | dbg.add_exception_callback(gen_callback_3, NOTE_EXEC) 138 | dbg.add_exception_callback(gen_callback_2, NOTE_FORK) 139 | dbg.add_exception_callback(gen_callback_5, NOTE_SIGNAL) 140 | dbg.add_exception_callback(gen_callback_4, NOTE_EXIT) 141 | 142 | #start(task, infoPid); 143 | dbg.continue_() 144 | #while(1); 145 | time.sleep(10) 146 | 147 | def set_temp_break(dbg, params): 148 | usage = "\tUsage: tbreak [addr]" 149 | 150 | if len(params) == 0: 151 | print usage 152 | return 153 | 154 | CALLBKFUNC = CFUNCTYPE(c_int, POINTER(ExcMsg)) 155 | gen_callback = CALLBKFUNC(generic_callback) 156 | 157 | try: 158 | addr = int(params[0], 0) 159 | except ValueError: 160 | ERR("Given address is not valid: {0}".format(params[0])) 161 | return 162 | 163 | dbg.add_breakpoint(addr, ONE_TIME, gen_callback) 164 | 165 | def examine(dbg, params): 166 | usage = "\tUsage: examine [size] [type] [addr]" 167 | 168 | if len(params) == 0: 169 | print usage 170 | return 171 | 172 | def disassemble(dbg, params): 173 | pass 174 | 175 | def clear(dbg, params): 176 | pass 177 | 178 | def delete(dbg, params): 179 | pass 180 | 181 | def do_continue(dbg, params): 182 | LOG("Continuing...") 183 | dbg.continue_() 184 | return True 185 | 186 | def step(dbg, params): 187 | pass 188 | 189 | def next(dbg, params): 190 | pass 191 | 192 | def read_regs(dbg, params): 193 | pass 194 | 195 | def write_regs(dbg, params): 196 | pass 197 | 198 | def read_memory(dbg, params): 199 | pass 200 | 201 | def write_memory(dbg, params): 202 | pass 203 | 204 | def backtrace(dbg, params): 205 | pass 206 | -------------------------------------------------------------------------------- /src/python/libs/.test/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/libs/.test/a.out -------------------------------------------------------------------------------- /src/python/libs/.test/de.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | def demangle(names): 4 | args = ['c++filt'] 5 | args.extend(names) 6 | pipe = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 7 | stdout, _ = pipe.communicate() 8 | demangled = stdout.split("\n") 9 | 10 | # Each line ends with a newline, so the final entry of the split output 11 | # will always be ''. 12 | assert len(demangled) == len(names)+1 13 | return demangled[:-1] 14 | 15 | print demangle(['__ZN5hello2xeEi']) -------------------------------------------------------------------------------- /src/python/libs/.test/testprog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class hello { 5 | public: 6 | int x; 7 | int y; 8 | int xe(int y){ 9 | return y + 5; 10 | } 11 | }; 12 | 13 | int main(){ 14 | hello* g = new hello(); 15 | g->y = 9; 16 | cout << g->xe(5) << endl; 17 | } -------------------------------------------------------------------------------- /src/python/libs/.test/tst.py: -------------------------------------------------------------------------------- 1 | from parse.macho import * 2 | from parse.BinaryData import * 3 | import subprocess 4 | 5 | def demangle(names): 6 | args = ['c++filt'] 7 | args.extend(names) 8 | pipe = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 9 | stdout, _ = pipe.communicate() 10 | demangled = stdout.split("\n") 11 | 12 | # Each line ends with a newline, so the final entry of the split output 13 | # will always be ''. 14 | assert len(demangled) == len(names)+1 15 | return demangled[:-1] 16 | 17 | a = open("a.out").read() 18 | # print len(a) 19 | b = BinaryData(a) 20 | c = MachOFile(b) 21 | 22 | for i in c.symbols_by_name: 23 | print demangle([i]) , i -------------------------------------------------------------------------------- /src/python/libs/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Librarires for macdbg which provides wrappers for C functions of the mac debugger and functionality. 3 | """ 4 | 5 | from macdbg import MacDbg -------------------------------------------------------------------------------- /src/python/libs/const.py: -------------------------------------------------------------------------------- 1 | """ 2 | Structs, defines, and enums defined in C code. 3 | """ 4 | from ctypes import Structure, c_byte, c_ubyte, c_char, c_char_p, c_short, c_ushort, c_int, c_uint, c_int64, \ 5 | c_float, c_double, c_long, c_ulong, c_longlong, c_ulonglong, POINTER 6 | 7 | """ 8 | Structs and definitions 9 | """ 10 | # Ctype structure for exc_msg_t 11 | class ExcMsg(Structure): 12 | _fields_ = [("exception_port" , c_uint), 13 | ("thread" , c_uint), 14 | ("task" , c_uint), 15 | ("exception" , c_int), 16 | ("code" , c_int64), 17 | ("codeCnt" , c_uint)] 18 | 19 | # Ctype structure for x86_thread_state64_t 20 | class X86ThreadState64(Structure): 21 | _fields_ = [("rax" , c_ulonglong), 22 | ("rbx" , c_ulonglong), 23 | ("rcx" , c_ulonglong), 24 | ("rdx" , c_ulonglong), 25 | ("rdi" , c_ulonglong), 26 | ("rsi" , c_ulonglong), 27 | ("rbp" , c_ulonglong), 28 | ("rsp" , c_ulonglong), 29 | ("r8" , c_ulonglong), 30 | ("r9" , c_ulonglong), 31 | ("r10" , c_ulonglong), 32 | ("r11" , c_ulonglong), 33 | ("r12" , c_ulonglong), 34 | ("r13" , c_ulonglong), 35 | ("r14" , c_ulonglong), 36 | ("r15" , c_ulonglong), 37 | ("rip" , c_ulonglong), 38 | ("rflags" , c_ulonglong), 39 | ("cs" , c_ulonglong), 40 | ("fs" , c_ulonglong), 41 | ("gs" , c_ulonglong)] 42 | 43 | # Ctype structure for breakpoint_struct 44 | class BreakpointStruct(Structure): 45 | _fields_ = [("original" , c_ulonglong), 46 | ("address" , c_ulonglong), 47 | ("handler" , c_ulong), 48 | ("flags" , c_int)] 49 | 50 | # Ctype structure for exception_handler 51 | class ExceptionHandler(Structure): 52 | _fields_ = [("exception", c_int), 53 | ("handler" , c_ulong)] 54 | 55 | # Ctype structure for vm_region_basic_info_data_64_t 56 | class VmRegionBasicInfo64(Structure): 57 | _fields_ = [("protection" , c_int), 58 | ("max_protection" , c_int), 59 | ("inheritance" , c_uint), 60 | ("shared" , c_uint), 61 | ("reserved" , c_uint), 62 | ("offset" , c_ulonglong), 63 | ("behavior" , c_int), 64 | ("user_wired_count" , c_ushort)] 65 | 66 | # Ctype structure for vm_region_t 67 | class VmRegion(Structure): 68 | _fields_ = [("region_type" , c_uint), 69 | ("address_start" , c_ulonglong), 70 | ("address_end" , c_ulonglong), 71 | ("size" , c_ulonglong), 72 | ("protection" , c_uint), 73 | ("max_protection" , c_uint), 74 | ("share_mode" , c_uint), 75 | ("region_detail" , c_char_p)] 76 | 77 | 78 | #Ctype structure for dyld_info_struct 79 | class DyldInfo(Structure): 80 | _fields_ = [("start_addr" , c_ulonglong), 81 | ("end_addr" , c_ulonglong), 82 | ("region_type" , c_uint), 83 | ("size" , c_ulonglong), 84 | ("path" , c_char_p), 85 | ("protection" , c_uint)] 86 | 87 | class time_value_t(Structure): 88 | _fields_ = [("seconds" , c_int), 89 | ("microseconds" , c_int)] 90 | 91 | #Ctype structure for thread_basic_info_t 92 | class ThreadBasicInfo(Structure): 93 | _fields_ = [("user_time" , time_value_t), 94 | ("system_time" , time_value_t), 95 | ("cpu_usuage" , c_int), 96 | ("policy" , c_int), 97 | ("run_state" , c_int), 98 | ("flags" , c_int), 99 | ("suspend_count" , c_int), 100 | ("sleep_time" , c_int)] 101 | 102 | #Ctype structure for thread_identifier_info_data_t 103 | class ThreadIdentInfo(Structure): 104 | _fields_ = [("thread_id" , c_ulonglong), 105 | ("thread_handle" , c_ulonglong), 106 | ("dispatch_qaddr" , c_ulonglong)] 107 | 108 | class ProcThreadInfo(Structure): 109 | _fields_ = [("pth_user_time" , c_ulonglong), 110 | ("pth_system_time" , c_ulonglong), 111 | ("pth_cpu_usage" , c_int), 112 | ("pth_policy" , c_int), 113 | ("pth_run_state" , c_int), 114 | ("pth_flags" , c_int), 115 | ("pth_sleep_time" , c_int), 116 | ("pth_curpri" , c_int), 117 | ("pth_priority" , c_int), 118 | ("pth_maxpriority" , c_int), 119 | ("pth_name" , c_char_p)] 120 | 121 | """ 122 | Enum and defines 123 | """ 124 | 125 | # breakpoint flags 126 | ONE_TIME = 0 127 | PERSISTENT = 1 128 | 129 | TEXT = 0 130 | DATA = 1 131 | 132 | # kernel events 133 | NOTE_FORK = 0x40000000 134 | NOTE_EXEC = 0x20000000 135 | NOTE_SIGNAL = 0x08000000 136 | NOTE_EXIT = 0x80000000 137 | 138 | # exception types 139 | EXC_BAD_ACCESS = 1 # Could not access memory 140 | EXC_BAD_INSTRUCTION = 2 # Instruction failed 141 | EXC_ARITHMETIC = 3 # Arithmetic exception 142 | EXC_EMULATION = 4 # Emulation instruction 143 | EXC_SOFTWARE = 5 # Software generated exception 144 | EXC_BREAKPOINT = 6 # Trace breakpoint etc. 145 | EXC_SYSCALL = 7 # System calls. 146 | EXC_MACH_SYSCALL = 8 # Mach system calls. 147 | EXC_RPC_ALERT = 9 # RPC alert 148 | EXC_CRASH = 10 # Abnormal process exit 149 | EXC_RESOURCE = 11 # Hit resource consumption limit 150 | EXC_GUARD = 12 # Violated guarded resource protections 151 | EXC_CORPSE_NOTIFY = 13 # Abnormal process exited to corpse state 152 | 153 | 154 | VM_PROT_NONE = 0x00 155 | VM_PROT_READ = 0x01 # read permission 156 | VM_PROT_WRITE = 0x02 # write permission 157 | VM_PROT_EXECUTE = 0x04 # execute permission 158 | VM_PROT_NO_CHANGE = 0x08 159 | VM_PROT_COPY = 0x10 160 | VM_PROT_WANTS_COPY = 0x10 161 | VM_PROT_IS_MASK = 0x40 162 | 163 | VM_FLAGS_ANYWHERE = 0x01 164 | 165 | # Mapping ctypes to python types 166 | SEARCH_TYPES = {c_char : 'c', 167 | c_byte : 'b', 168 | c_ubyte : 'B', 169 | c_short : 'h', 170 | c_ushort : 'H', 171 | c_int : 'i', 172 | c_uint : 'I', 173 | c_long : 'l', 174 | c_ulong : 'L', 175 | c_longlong : 'q', 176 | c_ulonglong : 'Q', 177 | c_float : 'f', 178 | c_double : 'd'} 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/python/libs/parse/BinaryData.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Rusty Wagner 2 | #All rights reserved. 3 | 4 | #Redistribution and use in source and binary forms are permitted 5 | #provided that the above copyright notice and this paragraph are 6 | #duplicated in all such forms and that any documentation, 7 | #advertising materials, and other materials related to such 8 | #distribution and use acknowledge that the software was developed 9 | #by the Rusty Wagner. The name of the 10 | # Rusty Wagner may not be used to endorse or promote products derived 11 | #from this software without specific prior written permission. 12 | #THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 13 | #IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 14 | #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 15 | import struct 16 | import io 17 | import thread 18 | # import Threads 19 | 20 | DATA_ORIGINAL = 0 21 | DATA_CHANGED = 1 22 | DATA_INSERTED = 2 23 | 24 | 25 | class BinaryAccessor: 26 | def read_uint8(self, ofs): 27 | return struct.unpack('B', self.read(ofs, 1))[0] 28 | 29 | def read_uint16(self, ofs): 30 | return struct.unpack('H', self.read(ofs, 2))[0] 49 | 50 | def read_uint32_be(self, ofs): 51 | return struct.unpack('>I', self.read(ofs, 4))[0] 52 | 53 | def read_uint64_be(self, ofs): 54 | return struct.unpack('>Q', self.read(ofs, 8))[0] 55 | 56 | def read_int8(self, ofs): 57 | return struct.unpack('b', self.read(ofs, 1))[0] 58 | 59 | def read_int16(self, ofs): 60 | return struct.unpack('h', self.read(ofs, 2))[0] 79 | 80 | def read_int32_be(self, ofs): 81 | return struct.unpack('>i', self.read(ofs, 4))[0] 82 | 83 | def read_int64_be(self, ofs): 84 | return struct.unpack('>q', self.read(ofs, 8))[0] 85 | 86 | def write_uint8(self, ofs, val): 87 | return self.write(ofs, struct.pack('B', val)) == 1 88 | 89 | def write_uint16(self, ofs, val): 90 | return self.write(ofs, struct.pack('H', val)) == 2 109 | 110 | def write_uint32_be(self, ofs, val): 111 | return self.write(ofs, struct.pack('>I', val)) == 4 112 | 113 | def write_uint64_be(self, ofs, val): 114 | return self.write(ofs, struct.pack('>Q', val)) == 8 115 | 116 | def write_int8(self, ofs, val): 117 | return self.write(ofs, struct.pack('b', val)) == 1 118 | 119 | def write_int16(self, ofs, val): 120 | return self.write(ofs, struct.pack('h', val)) == 2 139 | 140 | def write_int32_be(self, ofs, val): 141 | return self.write(ofs, struct.pack('>i', val)) == 4 142 | 143 | def write_int64_be(self, ofs, val): 144 | return self.write(ofs, struct.pack('>q', val)) == 8 145 | 146 | def end(self): 147 | return self.start() + len(self) 148 | 149 | def __str__(self): 150 | return self.read(0, len(self)) 151 | 152 | def __getitem__(self, offset): 153 | if type(offset) == slice: 154 | start = offset.start 155 | end = offset.stop 156 | if start is None: 157 | start = self.start() 158 | if end is None: 159 | end = self.start() + len(self) 160 | if end < 0: 161 | end = self.start() + len(self) + end 162 | 163 | if (offset.step is None) or (offset.step == 1): 164 | return self.read(start, end - start) 165 | else: 166 | result = "" 167 | for i in xrange(start, end, offset.step): 168 | part = self.read(i, 1) 169 | if len(part) == 0: 170 | return result 171 | result += part 172 | return result 173 | 174 | result = self.read(offset, 1) 175 | if len(result) == 0: 176 | raise IndexError 177 | return result 178 | 179 | def __setitem__(self, offset, value): 180 | if type(offset) == slice: 181 | start = offset.start 182 | end = offset.stop 183 | if start is None: 184 | start = self.start() 185 | if end is None: 186 | end = self.start() + len(self) 187 | if end < 0: 188 | end = self.start() + len(self) + end 189 | 190 | if (offset.step is None) or (offset.step == 1): 191 | if end < start: 192 | return 193 | if len(value) != (end - start): 194 | self.remove(start, end - start) 195 | self.insert(start, value) 196 | else: 197 | self.write(start, value) 198 | else: 199 | rel_offset = 0 200 | j = 0 201 | for i in xrange(start, end, offset.step): 202 | if j < len(value): 203 | self.write(i + rel_offset, value[j]) 204 | else: 205 | self.remove(i + rel_offset) 206 | rel_offset -= 1 207 | else: 208 | if self.write(offset, value) == 0: 209 | raise IndexError 210 | 211 | def __delitem__(self, offset): 212 | if type(offset) == slice: 213 | start = offset.start 214 | end = offset.stop 215 | if start is None: 216 | start = self.start() 217 | if end is None: 218 | end = self.start() + len(self) 219 | if end < 0: 220 | end = self.start() + len(self) + end 221 | 222 | if (offset.step is None) or (offset.step == 1): 223 | if end < start: 224 | return 225 | self.remove(start, end - start) 226 | else: 227 | rel_offset = 0 228 | for i in xrange(start, end, offset.step): 229 | self.remove(i + rel_offset) 230 | rel_offset -= 1 231 | else: 232 | if self.remove(offset, 1) == 0: 233 | raise IndexError 234 | 235 | 236 | class WriteUndoEntry: 237 | def __init__(self, data, offset, old_contents, new_contents, old_mod): 238 | self.data = data 239 | self.offset = offset 240 | self.old_contents = old_contents 241 | self.new_contents = new_contents 242 | self.old_mod = old_mod 243 | 244 | class InsertUndoEntry: 245 | def __init__(self, data, offset, contents): 246 | self.data = data 247 | self.offset = offset 248 | self.contents = contents 249 | 250 | class RemoveUndoEntry: 251 | def __init__(self, data, offset, old_contents, old_mod): 252 | self.data = data 253 | self.offset = offset 254 | self.old_contents = old_contents 255 | self.old_mod = old_mod 256 | 257 | 258 | class BinaryData(BinaryAccessor): 259 | def __init__(self, data = ""): 260 | self.data = data 261 | self.modification = [DATA_ORIGINAL] * len(data) 262 | self.modified = False 263 | self.callbacks = [] 264 | self.undo_buffer = [] 265 | self.redo_buffer = [] 266 | self.temp_undo_buffer = [] 267 | self.unmodified_undo_index = 0 268 | self.symbols_by_name = {} 269 | self.symbols_by_addr = {} 270 | self.default_arch = None 271 | 272 | def read(self, ofs, size): 273 | return self.data[ofs:(ofs + size)] 274 | 275 | def write(self, ofs, data): 276 | if len(data) == 0: 277 | return 0 278 | if ofs == len(self.data): 279 | return self.insert(len(self.data), data) 280 | if ofs >= len(self.data): 281 | return 0 282 | append = "" 283 | if (ofs + len(data)) > len(self.data): 284 | append = data[len(self.data)-ofs:] 285 | data = data[0:len(self.data)-ofs] 286 | 287 | undo_entry = WriteUndoEntry(self, ofs, self.data[ofs:ofs+len(data)], data, self.modification[ofs:ofs+len(data)]) 288 | self.insert_undo_entry(undo_entry, self.undo_write, self.redo_write) 289 | 290 | self.data = self.data[0:ofs] + data + self.data[ofs+len(data):] 291 | for i in xrange(ofs, ofs + len(data)): 292 | if self.modification[i] == DATA_ORIGINAL: 293 | self.modification[i] = DATA_CHANGED 294 | for cb in self.callbacks: 295 | if hasattr(cb, "notify_data_write"): 296 | cb.notify_data_write(self, ofs, data) 297 | self.modified = True 298 | if len(append) > 0: 299 | return len(data) + self.insert(len(self.data), append) 300 | return len(data) 301 | 302 | def insert(self, ofs, data): 303 | if len(data) == 0: 304 | return 0 305 | if ofs > len(self.data): 306 | return 0 307 | 308 | undo_entry = InsertUndoEntry(self, ofs, data) 309 | self.insert_undo_entry(undo_entry, self.undo_insert, self.redo_insert) 310 | 311 | self.data = self.data[0:ofs] + data + self.data[ofs:] 312 | self.modification[ofs:ofs] = [DATA_INSERTED] * len(data) 313 | for cb in self.callbacks: 314 | if hasattr(cb, "notify_data_insert"): 315 | cb.notify_data_insert(self, ofs, data) 316 | self.modified = True 317 | return len(data) 318 | 319 | def remove(self, ofs, size): 320 | if size == 0: 321 | return 0 322 | if ofs >= len(self.data): 323 | return 0 324 | if (ofs + size) > len(self.data): 325 | size = len(self.data) - ofs 326 | 327 | undo_entry = RemoveUndoEntry(self, ofs, self.data[ofs:ofs+size], self.modification[ofs:ofs+size]) 328 | self.insert_undo_entry(undo_entry, self.undo_remove, self.redo_remove) 329 | 330 | self.data = self.data[0:ofs] + self.data[ofs+size:] 331 | del self.modification[ofs:ofs+size] 332 | for cb in self.callbacks: 333 | if hasattr(cb, "notify_data_remove"): 334 | cb.notify_data_remove(self, ofs, size) 335 | self.modified = True 336 | return size 337 | 338 | def get_modification(self, ofs, size): 339 | return self.modification[ofs:ofs+size] 340 | 341 | def add_callback(self, cb): 342 | self.callbacks.append(cb) 343 | 344 | def remove_callback(self, cb): 345 | self.callbacks.remove(cb) 346 | 347 | def save(self, filename): 348 | f = io.open(filename, 'wb') 349 | f.write(self.data) 350 | f.close() 351 | self.modification = [DATA_ORIGINAL] * len(self.data) 352 | self.modified = False 353 | self.unmodified_undo_index = len(self.undo_buffer) 354 | 355 | def start(self): 356 | return 0 357 | 358 | def __len__(self): 359 | return len(self.data) 360 | 361 | def is_modified(self): 362 | return self.modified 363 | 364 | def find(self, regex, addr): 365 | match = regex.search(self.data, addr) 366 | if match == None: 367 | return -1 368 | return match.start() 369 | 370 | def commit_undo(self, before_loc, after_loc): 371 | if len(self.temp_undo_buffer) == 0: 372 | return 373 | if len(self.undo_buffer) < self.unmodified_undo_index: 374 | self.unmodified_undo_index = -1 375 | entries = self.temp_undo_buffer 376 | self.temp_undo_buffer = [] 377 | self.undo_buffer.append([before_loc, after_loc, entries]) 378 | self.redo_buffer = [] 379 | 380 | def insert_undo_entry(self, data, undo_func, redo_func): 381 | self.temp_undo_buffer.append([data, undo_func, redo_func]) 382 | 383 | def undo_write(self, entry): 384 | self.data = self.data[0:entry.offset] + entry.old_contents + self.data[entry.offset + len(entry.old_contents):] 385 | self.modification = self.modification[0:entry.offset] + entry.old_mod + self.modification[entry.offset + len(entry.old_mod):] 386 | for cb in self.callbacks: 387 | if hasattr(cb, "notify_data_write"): 388 | cb.notify_data_write(self, entry.offset, entry.old_contents) 389 | 390 | def redo_write(self, entry): 391 | self.data = self.data[0:entry.offset] + entry.new_contents + self.data[entry.offset + len(entry.new_contents):] 392 | for i in xrange(entry.offset, entry.offset + len(entry.new_contents)): 393 | if self.modification[i] == DATA_ORIGINAL: 394 | self.modification[i] = DATA_CHANGED 395 | for cb in self.callbacks: 396 | if hasattr(cb, "notify_data_write"): 397 | cb.notify_data_write(self, entry.offset, entry.new_contents) 398 | self.modified = True 399 | 400 | def undo_insert(self, entry): 401 | self.data = self.data[0:entry.offset] + self.data[entry.offset + len(entry.contents):] 402 | self.modification = self.modification[0:entry.offset] + self.modification[entry.offset + len(entry.contents):] 403 | for cb in self.callbacks: 404 | if hasattr(cb, "notify_data_remove"): 405 | cb.notify_data_remove(self, entry.offset, len(entry.contents)) 406 | 407 | def redo_insert(self, entry): 408 | self.data = self.data[0:entry.offset] + entry.contents + self.data[entry.offset:] 409 | self.modification[entry.offset:entry.offset] = [DATA_INSERTED] * len(entry.contents) 410 | for cb in self.callbacks: 411 | if hasattr(cb, "notify_data_insert"): 412 | cb.notify_data_insert(self, entry.offset, entry.contents) 413 | self.modified = True 414 | 415 | def undo_remove(self, entry): 416 | self.data = self.data[0:entry.offset] + entry.old_contents + self.data[entry.offset:] 417 | self.modification = self.modification[0:entry.offset] + entry.old_mod + self.modification[entry.offset:] 418 | for cb in self.callbacks: 419 | if hasattr(cb, "notify_data_insert"): 420 | cb.notify_data_insert(self, entry.offset, entry.old_contents) 421 | 422 | def redo_remove(self, entry): 423 | self.data = self.data[0:entry.offset] + self.data[entry.offset + len(entry.old_contents):] 424 | self.modification = self.modification[0:entry.offset] + self.modification[entry.offset + len(entry.old_contents):] 425 | for cb in self.callbacks: 426 | if hasattr(cb, "notify_data_remove"): 427 | cb.notify_data_remove(self, entry.offset, len(entry.old_contents)) 428 | self.modified = True 429 | 430 | def undo(self): 431 | if len(self.undo_buffer) == 0: 432 | return None 433 | 434 | undo_desc = self.undo_buffer.pop() 435 | self.redo_buffer.append(undo_desc) 436 | 437 | for entry in undo_desc[2][::-1]: 438 | entry[1](entry[0]) 439 | 440 | self.modified = (len(self.undo_buffer) != self.unmodified_undo_index) 441 | return undo_desc[0] 442 | 443 | def redo(self): 444 | if len(self.redo_buffer) == 0: 445 | return None 446 | 447 | redo_desc = self.redo_buffer.pop() 448 | self.undo_buffer.append(redo_desc) 449 | 450 | for entry in redo_desc[2]: 451 | entry[2](entry[0]) 452 | 453 | self.modified = (len(self.undo_buffer) != self.unmodified_undo_index) 454 | return redo_desc[1] 455 | 456 | def architecture(self): 457 | return self.default_arch 458 | 459 | 460 | class BinaryFile(BinaryData): 461 | def __init__(self, filename): 462 | f = io.open(filename, 'rb') 463 | data = f.read() 464 | f.close() 465 | BinaryData.__init__(self, data) 466 | -------------------------------------------------------------------------------- /src/python/libs/parse/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/libs/parse/__init__.py -------------------------------------------------------------------------------- /src/python/libs/parse/macho.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Rusty Wagner 2 | #All rights reserved. 3 | 4 | #Redistribution and use in source and binary forms are permitted 5 | #provided that the above copyright notice and this paragraph are 6 | #duplicated in all such forms and that any documentation, 7 | #advertising materials, and other materials related to such 8 | #distribution and use acknowledge that the software was developed 9 | #by the Rusty Wagner. The name of the 10 | # Rusty Wagner may not be used to endorse or promote products derived 11 | #from this software without specific prior written permission. 12 | #THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 13 | #IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 14 | #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | from BinaryData import * 17 | from structure import * 18 | # from HexEditor import * 19 | # from View import * 20 | 21 | 22 | class MachOFile(BinaryAccessor): 23 | def __init__(self, data): 24 | self.data = data 25 | self.valid = False 26 | self.callbacks = [] 27 | self.symbols_by_name = {} 28 | self.symbols_by_addr = {} 29 | self.plt = {} 30 | if not self.is_macho(): 31 | return 32 | 33 | try: 34 | self.tree = Structure(self.data) 35 | self.header = self.tree.struct("Mach-O header", "header") 36 | 37 | self.header.uint32_le("magic") 38 | if (self.header.magic == 0xfeedface) or (self.header.magic == 0xfeedfacf): 39 | self.header.uint32_le("cputype") 40 | self.header.uint32_le("cpusubtype") 41 | self.header.uint32_le("filetype") 42 | self.header.uint32_le("cmds") 43 | self.header.uint32_le("cmdsize") 44 | self.header.uint32_le("flags") 45 | if self.header.magic == 0xfeedfacf: 46 | self.header.uint32_le("reserved") 47 | self.bits = 64 48 | else: 49 | self.bits = 32 50 | self.big_endian = False 51 | elif (self.header.magic == 0xcefaedfe) or (self.header.magic == 0xcffaedfe): 52 | self.header.uint32_be("cputype") 53 | self.header.uint32_be("cpusubtype") 54 | self.header.uint32_be("filetype") 55 | self.header.uint32_be("cmds") 56 | self.header.uint32_be("cmdsize") 57 | self.header.uint32_be("flags") 58 | if self.header.magic == 0xcffaedfe: 59 | self.header.uint32_be("reserved") 60 | self.bits = 64 61 | else: 62 | self.bits = 32 63 | self.big_endian = True 64 | 65 | self.symbol_table = None 66 | self.dynamic_symbol_table = None 67 | 68 | # Parse loader commands 69 | self.commands = self.tree.array(self.header.cmds, "commands") 70 | self.segments = [] 71 | self.sections = [] 72 | offset = self.header.getSize() 73 | for i in xrange(0, self.header.cmds): 74 | cmd = self.commands[i] 75 | cmd.seek(offset) 76 | if self.big_endian: 77 | cmd.uint32_be("cmd") 78 | cmd.uint32_be("size") 79 | else: 80 | cmd.uint32_le("cmd") 81 | cmd.uint32_le("size") 82 | 83 | if cmd.cmd == 1: # SEGMENT 84 | cmd.bytes(16, "name") 85 | if self.big_endian: 86 | cmd.uint32_be("vmaddr") 87 | cmd.uint32_be("vmsize") 88 | cmd.uint32_be("fileoff") 89 | cmd.uint32_be("filesize") 90 | cmd.uint32_be("maxprot") 91 | cmd.uint32_be("initprot") 92 | cmd.uint32_be("nsects") 93 | cmd.uint32_be("flags") 94 | else: 95 | cmd.uint32_le("vmaddr") 96 | cmd.uint32_le("vmsize") 97 | cmd.uint32_le("fileoff") 98 | cmd.uint32_le("filesize") 99 | cmd.uint32_le("maxprot") 100 | cmd.uint32_le("initprot") 101 | cmd.uint32_le("nsects") 102 | cmd.uint32_le("flags") 103 | 104 | if cmd.initprot != 0: # Ignore __PAGE_ZERO or anything like it 105 | self.segments.append(cmd) 106 | 107 | cmd.array(cmd.nsects, "sections") 108 | for i in xrange(0, cmd.nsects): 109 | section = cmd.sections[i] 110 | section.bytes(16, "name") 111 | section.bytes(16, "segment") 112 | if self.big_endian: 113 | section.uint32_be("addr") 114 | section.uint32_be("size") 115 | section.uint32_be("offset") 116 | section.uint32_be("align") 117 | section.uint32_be("reloff") 118 | section.uint32_be("nreloc") 119 | section.uint32_be("flags") 120 | section.uint32_be("reserved1") 121 | section.uint32_be("reserved2") 122 | else: 123 | section.uint32_le("addr") 124 | section.uint32_le("size") 125 | section.uint32_le("offset") 126 | section.uint32_le("align") 127 | section.uint32_le("reloff") 128 | section.uint32_le("nreloc") 129 | section.uint32_le("flags") 130 | section.uint32_le("reserved1") 131 | section.uint32_le("reserved2") 132 | self.sections.append(section) 133 | 134 | for i in xrange(0, cmd.nsects): 135 | section = cmd.sections[i] 136 | section.array(section.nreloc, "relocs") 137 | for j in xrange(0, section.nreloc): 138 | reloc = section.relocs[j] 139 | reloc.seek(section.reloff + (j * 8)) 140 | if self.big_endian: 141 | reloc.uint32_be("addr") 142 | reloc.uint32_be("value") 143 | else: 144 | reloc.uint32_le("addr") 145 | reloc.uint32_le("value") 146 | elif cmd.cmd == 25: # SEGMENT_64 147 | cmd.bytes(16, "name") 148 | if self.big_endian: 149 | cmd.uint64_be("vmaddr") 150 | cmd.uint64_be("vmsize") 151 | cmd.uint64_be("fileoff") 152 | cmd.uint64_be("filesize") 153 | cmd.uint32_be("maxprot") 154 | cmd.uint32_be("initprot") 155 | cmd.uint32_be("nsects") 156 | cmd.uint32_be("flags") 157 | else: 158 | cmd.uint64_le("vmaddr") 159 | cmd.uint64_le("vmsize") 160 | cmd.uint64_le("fileoff") 161 | cmd.uint64_le("filesize") 162 | cmd.uint32_le("maxprot") 163 | cmd.uint32_le("initprot") 164 | cmd.uint32_le("nsects") 165 | cmd.uint32_le("flags") 166 | 167 | if cmd.initprot != 0: # Ignore __PAGE_ZERO or anything like it 168 | self.segments.append(cmd) 169 | 170 | cmd.array(cmd.nsects, "sections") 171 | for i in xrange(0, cmd.nsects): 172 | section = cmd.sections[i] 173 | section.bytes(16, "name") 174 | section.bytes(16, "segment") 175 | if self.big_endian: 176 | section.uint64_be("addr") 177 | section.uint64_be("size") 178 | section.uint32_be("offset") 179 | section.uint32_be("align") 180 | section.uint32_be("reloff") 181 | section.uint32_be("nreloc") 182 | section.uint32_be("flags") 183 | section.uint32_be("reserved1") 184 | section.uint32_be("reserved2") 185 | section.uint32_be("reserved3") 186 | else: 187 | section.uint64_le("addr") 188 | section.uint64_le("size") 189 | section.uint32_le("offset") 190 | section.uint32_le("align") 191 | section.uint32_le("reloff") 192 | section.uint32_le("nreloc") 193 | section.uint32_le("flags") 194 | section.uint32_le("reserved1") 195 | section.uint32_le("reserved2") 196 | section.uint32_le("reserved3") 197 | self.sections.append(section) 198 | 199 | for i in xrange(0, cmd.nsects): 200 | section = cmd.sections[i] 201 | section.array(section.nreloc, "relocs") 202 | for j in xrange(0, section.nreloc): 203 | reloc = section.relocs[j] 204 | reloc.seek(section.reloff + (j * 8)) 205 | if self.big_endian: 206 | reloc.uint32_be("addr") 207 | reloc.uint32_be("value") 208 | else: 209 | reloc.uint32_le("addr") 210 | reloc.uint32_le("value") 211 | elif cmd.cmd == 5: # UNIX_THREAD 212 | if self.header.cputype == 7: # x86 213 | cmd.uint32_le("flavor") 214 | cmd.uint32_le("count") 215 | for reg in ["eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp", "ss", "eflags", 216 | "eip", "cs", "ds", "es", "fs", "gs"]: 217 | cmd.uint32_le(reg) 218 | self.entry_addr = cmd.eip 219 | elif self.header.cputype == 0x01000007: # x86_64 220 | cmd.uint32_le("flavor") 221 | cmd.uint32_le("count") 222 | for reg in ["rax", "rbx", "rcx", "rdx", "rdi", "rsi", "rbp", "rsp", "r8", "r9", 223 | "r10", "r11", "r12", "r13", "r14", "r15", "rip", "rflags", "cs", "fs", "gs"]: 224 | cmd.uint64_le(reg) 225 | self.entry_addr = cmd.rip 226 | elif self.header.cputype == 18: # PPC32 227 | cmd.uint32_be("flavor") 228 | cmd.uint32_be("count") 229 | for reg in ["srr0", "srr1"] + ["r%d" % i for i in xrange(0, 32)] + ["cr", "xer", 230 | "lr", "ctr", "mq", "vrsave"]: 231 | cmd.uint32_be(reg) 232 | self.entry_addr = cmd.srr0 233 | elif self.header.cputype == 0x01000012: # PPC64 234 | cmd.uint32_be("flavor") 235 | cmd.uint32_be("count") 236 | for reg in ["srr0", "srr1"] + ["r%d" % i for i in xrange(0, 32)] + ["cr", "xer", 237 | "lr", "ctr", "mq", "vrsave"]: 238 | cmd.uint64_be(reg) 239 | self.entry_addr = cmd.srr0 240 | elif self.header.cputype == 12: # ARM 241 | cmd.uint32_le("flavor") 242 | cmd.uint32_le("count") 243 | for reg in ["r%d" % i for i in xrange(0, 13)] + ["sp", "lr", "pc", "cpsr"]: 244 | cmd.uint32_le(reg) 245 | self.entry_addr = cmd.pc 246 | elif cmd.cmd == 2: # SYMTAB 247 | if self.big_endian: 248 | cmd.uint32_be("symoff") 249 | cmd.uint32_be("nsyms") 250 | cmd.uint32_be("stroff") 251 | cmd.uint32_be("strsize") 252 | else: 253 | cmd.uint32_le("symoff") 254 | cmd.uint32_le("nsyms") 255 | cmd.uint32_le("stroff") 256 | cmd.uint32_le("strsize") 257 | 258 | self.symbol_table = self.tree.array(cmd.nsyms, "symtab") 259 | strings = self.data.read(cmd.stroff, cmd.strsize) 260 | 261 | sym_offset = cmd.symoff 262 | for j in xrange(0, cmd.nsyms): 263 | entry = self.symbol_table[j] 264 | entry.seek(sym_offset) 265 | 266 | if self.big_endian: 267 | entry.uint32_be("strx") 268 | entry.uint8("type") 269 | entry.uint8("sect") 270 | entry.uint16_be("desc") 271 | if self.bits == 32: 272 | entry.uint32_be("value") 273 | else: 274 | entry.uint64_be("value") 275 | else: 276 | entry.uint32_le("strx") 277 | entry.uint8("type") 278 | entry.uint8("sect") 279 | entry.uint16_le("desc") 280 | if self.bits == 32: 281 | entry.uint32_le("value") 282 | else: 283 | entry.uint64_le("value") 284 | 285 | str_end = strings.find("\x00", entry.strx) 286 | entry.name = strings[entry.strx:str_end] 287 | 288 | if self.bits == 32: 289 | sym_offset += 12 290 | else: 291 | sym_offset += 16 292 | elif cmd.cmd == 11: # DYSYMTAB 293 | if self.big_endian: 294 | cmd.uint32_be("ilocalsym") 295 | cmd.uint32_be("nlocalsym") 296 | cmd.uint32_be("iextdefsym") 297 | cmd.uint32_be("nextdefsym") 298 | cmd.uint32_be("iundefsym") 299 | cmd.uint32_be("nundefsym") 300 | cmd.uint32_be("tocoff") 301 | cmd.uint32_be("ntoc") 302 | cmd.uint32_be("modtaboff") 303 | cmd.uint32_be("nmodtab") 304 | cmd.uint32_be("extrefsymoff") 305 | cmd.uint32_be("nextrefsyms") 306 | cmd.uint32_be("indirectsymoff") 307 | cmd.uint32_be("nindirectsyms") 308 | cmd.uint32_be("extreloff") 309 | cmd.uint32_be("nextrel") 310 | cmd.uint32_be("locreloff") 311 | cmd.uint32_be("nlocrel") 312 | else: 313 | cmd.uint32_le("ilocalsym") 314 | cmd.uint32_le("nlocalsym") 315 | cmd.uint32_le("iextdefsym") 316 | cmd.uint32_le("nextdefsym") 317 | cmd.uint32_le("iundefsym") 318 | cmd.uint32_le("nundefsym") 319 | cmd.uint32_le("tocoff") 320 | cmd.uint32_le("ntoc") 321 | cmd.uint32_le("modtaboff") 322 | cmd.uint32_le("nmodtab") 323 | cmd.uint32_le("extrefsymoff") 324 | cmd.uint32_le("nextrefsyms") 325 | cmd.uint32_le("indirectsymoff") 326 | cmd.uint32_le("nindirectsyms") 327 | cmd.uint32_le("extreloff") 328 | cmd.uint32_le("nextrel") 329 | cmd.uint32_le("locreloff") 330 | cmd.uint32_le("nlocrel") 331 | elif (cmd.cmd & 0x7fffffff) == 0x22: # DYLD_INFO 332 | self.dynamic_symbol_table = cmd 333 | if self.big_endian: 334 | cmd.uint32_be("rebaseoff") 335 | cmd.uint32_be("rebasesize") 336 | cmd.uint32_be("bindoff") 337 | cmd.uint32_be("bindsize") 338 | cmd.uint32_be("weakbindoff") 339 | cmd.uint32_be("weakbindsize") 340 | cmd.uint32_be("lazybindoff") 341 | cmd.uint32_be("lazybindsize") 342 | cmd.uint32_be("exportoff") 343 | cmd.uint32_be("exportsize") 344 | else: 345 | cmd.uint32_le("rebaseoff") 346 | cmd.uint32_le("rebasesize") 347 | cmd.uint32_le("bindoff") 348 | cmd.uint32_le("bindsize") 349 | cmd.uint32_le("weakbindoff") 350 | cmd.uint32_le("weakbindsize") 351 | cmd.uint32_le("lazybindoff") 352 | cmd.uint32_le("lazybindsize") 353 | cmd.uint32_le("exportoff") 354 | cmd.uint32_le("exportsize") 355 | 356 | offset += cmd.size 357 | 358 | # Add symbols from symbol table 359 | if self.symbol_table: 360 | for i in xrange(0, len(self.symbol_table)): 361 | symbol = self.symbol_table[i] 362 | 363 | # Only use symbols that are within a section 364 | if ((symbol.type & 0xe) == 0xe) and (symbol.sect <= len(self.sections)): 365 | self.create_symbol(symbol.value, symbol.name) 366 | 367 | # If there is a DYLD_INFO section, parse it and add PLT entries 368 | if self.dynamic_symbol_table: 369 | self.parse_dynamic_tables([[self.dynamic_symbol_table.bindoff, self.dynamic_symbol_table.bindsize], 370 | [self.dynamic_symbol_table.lazybindoff, self.dynamic_symbol_table.lazybindsize]]) 371 | 372 | self.tree.complete() 373 | self.valid = True 374 | except: 375 | self.valid = False 376 | raise 377 | 378 | if self.valid: 379 | self.data.add_callback(self) 380 | 381 | def read_leb128(self, data, ofs): 382 | value = 0 383 | shift = 0 384 | while ofs < len(data): 385 | cur = ord(data[ofs]) 386 | ofs += 1 387 | value |= (cur & 0x7f) << shift 388 | shift += 7 389 | if (cur & 0x80) == 0: 390 | break 391 | return value, ofs 392 | 393 | def parse_dynamic_tables(self, tables): 394 | # Interpret DYLD_INFO instructions (not documented by Apple) 395 | # http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html 396 | ordinal = 0 397 | segment = 0 398 | offset = 0 399 | sym_type = 0 400 | name = "" 401 | 402 | for table in tables: 403 | offset = table[0] 404 | size = table[1] 405 | opcodes = self.data.read(offset, size) 406 | i = 0 407 | while i < len(opcodes): 408 | opcode = ord(opcodes[i]) 409 | i += 1 410 | if (opcode >> 4) == 0: 411 | continue 412 | elif (opcode >> 4) == 1: 413 | ordinal = opcode & 0xf 414 | elif (opcode >> 4) == 2: 415 | ordinal, i = self.read_leb128(opcodes, i) 416 | elif (opcode >> 4) == 3: 417 | ordinal = -(opcode & 0xf) 418 | elif (opcode >> 4) == 4: 419 | name = "" 420 | while i < len(opcodes): 421 | ch = opcodes[i] 422 | i += 1 423 | if ch == '\x00': 424 | break 425 | name += ch 426 | elif (opcode >> 4) == 5: 427 | sym_type = opcode & 0xf 428 | elif (opcode >> 4) == 6: 429 | addend, i = self.read_leb128(opcodes, i) 430 | elif (opcode >> 4) == 7: 431 | segment = opcode & 0xf 432 | offset, i = self.read_leb128(opcodes, i) 433 | elif (opcode >> 4) == 8: 434 | rel, i = self.read_leb128(opcodes, i) 435 | offset += rel 436 | elif (opcode >> 4) >= 9: 437 | if (sym_type == 1) and (segment <= len(self.segments)): 438 | # Add pointer type entries to the PLT 439 | addr = self.segments[segment - 1].vmaddr + offset 440 | self.plt[addr] = name 441 | self.create_symbol(addr, self.decorate_plt_name(name)) 442 | if self.bits == 32: 443 | offset += 4 444 | else: 445 | offset += 8 446 | if (opcode >> 4) == 10: 447 | rel, i = self.read_leb128(opcodes, i) 448 | offset += rel 449 | elif (opcode >> 4) == 11: 450 | offset += (opcode & 0xf) * 4 451 | elif (opcode >> 4) == 12: 452 | count, i = self.read_leb128(opcodes, i) 453 | skip, i = self.read_leb128(opcodes, i) 454 | 455 | def read(self, ofs, len): 456 | result = "" 457 | while len > 0: 458 | cur = None 459 | for i in self.segments: 460 | if ((ofs >= i.vmaddr) and (ofs < (i.vmaddr + i.vmsize))) and (i.vmsize != 0): 461 | cur = i 462 | if cur == None: 463 | break 464 | 465 | prog_ofs = ofs - cur.vmaddr 466 | mem_len = cur.vmsize - prog_ofs 467 | file_len = cur.filesize - prog_ofs 468 | if mem_len > len: 469 | mem_len = len 470 | if file_len > len: 471 | file_len = len 472 | 473 | if file_len <= 0: 474 | result += "\x00" * mem_len 475 | len -= mem_len 476 | ofs += mem_len 477 | continue 478 | 479 | result += self.data.read(cur.fileoff + prog_ofs, file_len) 480 | len -= file_len 481 | ofs += file_len 482 | 483 | return result 484 | 485 | def next_valid_addr(self, ofs): 486 | result = -1 487 | for i in self.segments: 488 | if (i.vmaddr >= ofs) and (i.vmsize != 0) and ((result == -1) or (i.vmaddr < result)): 489 | result = i.vmaddr 490 | return result 491 | 492 | def get_modification(self, ofs, len): 493 | result = [] 494 | while len > 0: 495 | cur = None 496 | for i in self.segments: 497 | if ((ofs >= i.vmaddr) and (ofs < (i.vmaddr + i.vmsize))) and (i.vmsize != 0): 498 | cur = i 499 | if cur == None: 500 | break 501 | 502 | prog_ofs = ofs - cur.vmaddr 503 | mem_len = cur.vmsize - prog_ofs 504 | file_len = cur.filesize - prog_ofs 505 | if mem_len > len: 506 | mem_len = len 507 | if file_len > len: 508 | file_len = len 509 | 510 | if file_len <= 0: 511 | result += [DATA_ORIGINAL] * mem_len 512 | len -= mem_len 513 | ofs += mem_len 514 | continue 515 | 516 | result += self.data.get_modification(cur.fileoff + prog_ofs, file_len) 517 | len -= file_len 518 | ofs += file_len 519 | 520 | return result 521 | 522 | def write(self, ofs, data): 523 | result = 0 524 | while len(data) > 0: 525 | cur = None 526 | for i in self.segments: 527 | if ((ofs >= i.vmaddr) and (ofs < (i.vmaddr + i.vmsize))) and (i.vmsize != 0): 528 | cur = i 529 | if cur == None: 530 | break 531 | 532 | prog_ofs = ofs - cur.vmaddr 533 | mem_len = cur.vmsize - prog_ofs 534 | file_len = cur.filesize - prog_ofs 535 | if mem_len > len: 536 | mem_len = len 537 | if file_len > len: 538 | file_len = len 539 | 540 | if file_len <= 0: 541 | break 542 | 543 | result += self.data.write(cur.fileoff + prog_ofs, data[0:file_len]) 544 | data = data[file_len:] 545 | ofs += file_len 546 | 547 | return result 548 | 549 | def insert(self, ofs, data): 550 | return 0 551 | 552 | def remove(self, ofs, size): 553 | return 0 554 | 555 | def notify_data_write(self, data, ofs, contents): 556 | # Find sections that hold data backed by updated regions of the file 557 | for i in self.segments: 558 | if ((ofs + len(contents)) > i.fileoff) and (ofs < (i.fileoff + i.filesize)) and (i.vmsize != 0): 559 | # This section has been updated, compute which region has been changed 560 | from_start = ofs - i.fileoff 561 | data_ofs = 0 562 | length = len(contents) 563 | if from_start < 0: 564 | length += from_start 565 | data_ofs -= from_start 566 | from_start = 0 567 | if (from_start + length) > i.filesize: 568 | length = i.filesize - from_start 569 | 570 | # Notify callbacks 571 | if length > 0: 572 | for cb in self.callbacks: 573 | if hasattr(cb, "notify_data_write"): 574 | cb.notify_data_write(self, i.vmaddr + from_start, 575 | contents[data_ofs:(data_ofs + length)]) 576 | 577 | def save(self, filename): 578 | self.data.save(filename) 579 | 580 | def start(self): 581 | result = None 582 | for i in self.segments: 583 | if ((result == None) or (i.vmaddr < result)) and (i.vmsize != 0): 584 | result = i.vmaddr 585 | return result 586 | 587 | def entry(self): 588 | if not hasattr(self, "entry_addr"): 589 | return self.start() 590 | return self.entry_addr 591 | 592 | def __len__(self): 593 | max = None 594 | for i in self.segments: 595 | if ((max == None) or ((i.vmaddr + i.vmsize) > max)) and (i.vmsize != 0): 596 | max = i.vmaddr + i.vmsize 597 | return max - self.start() 598 | 599 | def is_macho(self): 600 | if self.data.read(0, 4) == "\xfe\xed\xfa\xce": 601 | return True 602 | if self.data.read(0, 4) == "\xfe\xed\xfa\xcf": 603 | return True 604 | if self.data.read(0, 4) == "\xce\xfa\xed\xfe": 605 | return True 606 | if self.data.read(0, 4) == "\xcf\xfa\xed\xfe": 607 | return True 608 | return False 609 | 610 | def architecture(self): 611 | if self.header.cputype == 7: 612 | return "x86" 613 | if self.header.cputype == 0x01000007: 614 | return "x86_64" 615 | if self.header.cputype == 12: 616 | return "arm" 617 | if self.header.cputype == 18: 618 | return "ppc" 619 | if self.header.cputype == 0x01000012: 620 | return "ppc" 621 | return None 622 | 623 | def decorate_plt_name(self, name): 624 | return name + "@PLT" 625 | 626 | def create_symbol(self, addr, name): 627 | self.symbols_by_name[name] = addr 628 | self.symbols_by_addr[addr] = name 629 | 630 | def delete_symbol(self, addr, name): 631 | if name in self.symbols_by_name: 632 | del(self.symbols_by_name[name]) 633 | if addr in self.symbols_by_addr: 634 | del(self.symbols_by_addr[addr]) 635 | 636 | def add_callback(self, cb): 637 | self.callbacks.append(cb) 638 | 639 | def remove_callback(self, cb): 640 | self.callbacks.remove(cb) 641 | 642 | def is_modified(self): 643 | return self.data.is_modified() 644 | 645 | def find(self, regex, addr): 646 | while (addr < self.end()) and (addr != -1): 647 | data = self.read(addr, 0xfffffffff) 648 | match = regex.search(data) 649 | if match != None: 650 | return match.start() + addr 651 | 652 | addr += len(data) 653 | addr = self.next_valid_addr(addr) 654 | 655 | return -1 656 | 657 | def has_undo_actions(self): 658 | return self.data.has_undo_actions() 659 | 660 | def commit_undo(self, before_loc, after_loc): 661 | self.data.commit_undo(before_loc, after_loc) 662 | 663 | def undo(self): 664 | self.data.undo() 665 | 666 | def redo(self): 667 | self.data.redo() 668 | 669 | 670 | # a = open("test_prog.app").read() 671 | # # print len(a) 672 | # b = BinaryData(a[0:len(a)-100]) 673 | # c = MachOFile(b) 674 | # print c.symbols_by_name 675 | # print c.symbols_by_addr 676 | # print c.plt 677 | 678 | -------------------------------------------------------------------------------- /src/python/libs/parse/structure.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Rusty Wagner 2 | #All rights reserved. 3 | 4 | #Redistribution and use in source and binary forms are permitted 5 | #provided that the above copyright notice and this paragraph are 6 | #duplicated in all such forms and that any documentation, 7 | #advertising materials, and other materials related to such 8 | #distribution and use acknowledge that the software was developed 9 | #by the Rusty Wagner. The name of the 10 | # Rusty Wagner may not be used to endorse or promote products derived 11 | #from this software without specific prior written permission. 12 | #THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 13 | #IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 14 | #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | from BinaryData import * 17 | 18 | 19 | class _ParserState: 20 | def __init__(self, data, ofs): 21 | self.data = data 22 | self.offset = ofs 23 | 24 | 25 | class Array: 26 | def __init__(self, state, count): 27 | self._state = state 28 | self.elements = [] 29 | for i in range(0, count): 30 | self.elements += [Structure(state.data, state)] 31 | 32 | def append(self): 33 | self.elements.append(Structure(self._state.data, self._state)) 34 | 35 | def getStart(self): 36 | start = None 37 | for i in self.elements: 38 | if (start == None) or (i.getStart() < start): 39 | start = i.getStart() 40 | if start == None: 41 | return 0 42 | return start 43 | 44 | def getSize(self): 45 | start = self.getStart() 46 | end = None 47 | for i in self.elements: 48 | if (end == None) or ((i.getStart() + i.getSize()) > end): 49 | end = i.getStart() + i.getSize() 50 | if end == None: 51 | return 0 52 | return end - start 53 | 54 | def complete(self): 55 | for i in self.elements: 56 | i.complete() 57 | 58 | def __getitem__(self, index): 59 | return self.elements[index] 60 | 61 | def __len__(self): 62 | return len(self.elements) 63 | 64 | 65 | class Structure: 66 | def __init__(self, data, state = None): 67 | self._data = data 68 | self._state = state 69 | if state == None: 70 | self._state = _ParserState(data, 0) 71 | self._names = {} 72 | self._order = [] 73 | self._start = {} 74 | self._size = {} 75 | self._type = {} 76 | 77 | def seek(self, ofs): 78 | self._state.offset = ofs 79 | 80 | def struct(self, name, id = None): 81 | if id == None: 82 | id = name 83 | result = Structure(self._data, self._state) 84 | self.__dict__[id] = result 85 | self._names[id] = name 86 | self._type[id] = "struct" 87 | self._order += [id] 88 | return result 89 | 90 | def array(self, count, name, id = None): 91 | if id == None: 92 | id = name 93 | result = Array(self._state, count) 94 | self.__dict__[id] = result 95 | self._names[id] = name 96 | self._type[id] = "array" 97 | self._order += [id] 98 | return result 99 | 100 | def bytes(self, count, name, id = None): 101 | if id == None: 102 | id = name 103 | result = self._data.read(self._state.offset, count) 104 | self.__dict__[id] = result 105 | self._names[id] = name 106 | self._start[id] = self._state.offset 107 | self._size[id] = count 108 | self._type[id] = "bytes" 109 | self._order += [id] 110 | self._state.offset += count 111 | return result 112 | 113 | def uint8(self, name, id = None): 114 | if id == None: 115 | id = name 116 | result = self._data.read_uint8(self._state.offset) 117 | self.__dict__[id] = result 118 | self._names[id] = name 119 | self._start[id] = self._state.offset 120 | self._size[id] = 1 121 | self._type[id] = "uint8" 122 | self._order += [id] 123 | self._state.offset += 1 124 | return result 125 | 126 | def uint16(self, name, id = None): 127 | if id == None: 128 | id = name 129 | result = self._data.read_uint16(self._state.offset) 130 | self.__dict__[id] = result 131 | self._names[id] = name 132 | self._start[id] = self._state.offset 133 | self._size[id] = 2 134 | self._type[id] = "uint16" 135 | self._order += [id] 136 | self._state.offset += 2 137 | return result 138 | 139 | def uint32(self, name, id = None): 140 | if id == None: 141 | id = name 142 | result = self._data.read_uint32(self._state.offset) 143 | self.__dict__[id] = result 144 | self._names[id] = name 145 | self._start[id] = self._state.offset 146 | self._size[id] = 4 147 | self._type[id] = "uint32" 148 | self._order += [id] 149 | self._state.offset += 4 150 | return result 151 | 152 | def uint64(self, name, id = None): 153 | if id == None: 154 | id = name 155 | result = self._data.read_uint64(self._state.offset) 156 | self.__dict__[id] = result 157 | self._names[id] = name 158 | self._start[id] = self._state.offset 159 | self._size[id] = 8 160 | self._type[id] = "uint64" 161 | self._order += [id] 162 | self._state.offset += 8 163 | return result 164 | 165 | def uint16_le(self, name, id = None): 166 | if id == None: 167 | id = name 168 | result = self._data.read_uint16_le(self._state.offset) 169 | self.__dict__[id] = result 170 | self._names[id] = name 171 | self._start[id] = self._state.offset 172 | self._size[id] = 2 173 | self._type[id] = "uint16_le" 174 | self._order += [id] 175 | self._state.offset += 2 176 | return result 177 | 178 | def uint32_le(self, name, id = None): 179 | if id == None: 180 | id = name 181 | result = self._data.read_uint32_le(self._state.offset) 182 | self.__dict__[id] = result 183 | self._names[id] = name 184 | self._start[id] = self._state.offset 185 | self._size[id] = 4 186 | self._type[id] = "uint32_le" 187 | self._order += [id] 188 | self._state.offset += 4 189 | return result 190 | 191 | def uint64_le(self, name, id = None): 192 | if id == None: 193 | id = name 194 | result = self._data.read_uint64_le(self._state.offset) 195 | self.__dict__[id] = result 196 | self._names[id] = name 197 | self._start[id] = self._state.offset 198 | self._size[id] = 8 199 | self._type[id] = "uint64_le" 200 | self._order += [id] 201 | self._state.offset += 8 202 | return result 203 | 204 | def uint16_be(self, name, id = None): 205 | if id == None: 206 | id = name 207 | result = self._data.read_uint16_be(self._state.offset) 208 | self.__dict__[id] = result 209 | self._names[id] = name 210 | self._start[id] = self._state.offset 211 | self._size[id] = 2 212 | self._type[id] = "uint16_be" 213 | self._order += [id] 214 | self._state.offset += 2 215 | return result 216 | 217 | def uint32_be(self, name, id = None): 218 | if id == None: 219 | id = name 220 | result = self._data.read_uint32_be(self._state.offset) 221 | self.__dict__[id] = result 222 | self._names[id] = name 223 | self._start[id] = self._state.offset 224 | self._size[id] = 4 225 | self._type[id] = "uint32_be" 226 | self._order += [id] 227 | self._state.offset += 4 228 | return result 229 | 230 | def uint64_be(self, name, id = None): 231 | if id == None: 232 | id = name 233 | result = self._data.read_uint64_be(self._state.offset) 234 | self.__dict__[id] = result 235 | self._names[id] = name 236 | self._start[id] = self._state.offset 237 | self._size[id] = 8 238 | self._type[id] = "uint64_be" 239 | self._order += [id] 240 | self._state.offset += 8 241 | return result 242 | 243 | def getStart(self): 244 | self.complete() 245 | start = None 246 | for i in self._order: 247 | if (start == None) or (self._start[i] < start): 248 | start = self._start[i] 249 | return start 250 | 251 | def getSize(self): 252 | start = self.getStart() 253 | end = None 254 | for i in self._order: 255 | if (end == None) or ((self._start[i] + self._size[i]) > end): 256 | end = self._start[i] + self._size[i] 257 | if end == None: 258 | return None 259 | return end - start 260 | 261 | def complete(self): 262 | for i in self._order: 263 | if (not self._start.has_key(i)) or (not self._size.has_key(i)): 264 | self.__dict__[i].complete() 265 | self._start[i] = self.__dict__[i].getStart() 266 | self._size[i] = self.__dict__[i].getSize() 267 | -------------------------------------------------------------------------------- /src/python/libs/util.py: -------------------------------------------------------------------------------- 1 | from inspect import stack 2 | 3 | def err_noattach(func): 4 | '''decorator to check if instance of macdbg is attached''' 5 | def wrapper(*args, **kwargs): 6 | if (args[0].task == None): # args[0] is self 7 | fname = func.func_name 8 | ERR("No process attached: {0}".format(fname)) 9 | return 0 10 | return func(*args, **kwargs) 11 | return wrapper 12 | 13 | def LOG(msg): 14 | '''Prints log message in the format [*function_name] LOG: msg''' 15 | return "\n[*{0}] LOG: {1}".format(stack()[1][3], msg) 16 | 17 | def ERR(msg): 18 | '''Prints error message in the format [-function_name] ERR: msg''' 19 | print "\n[-{0}] ERR: {1}".format(stack()[1][3], msg) 20 | 21 | def err_cont(): 22 | '''Prompts user to continue or exit the program''' 23 | cont = "" 24 | while cont != 'n' or cont != 'y': 25 | cont = raw_input("\nContinue? [Y/n]: ") 26 | cont = cont.lower() 27 | if cont == 'n': 28 | exit(1) 29 | elif cont == 'y': 30 | break 31 | 32 | -------------------------------------------------------------------------------- /src/python/run_tests.sh: -------------------------------------------------------------------------------- 1 | sudo python -m unittest discover 2 | -------------------------------------------------------------------------------- /src/python/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/__init__.py -------------------------------------------------------------------------------- /src/python/tests/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/__init__.pyc -------------------------------------------------------------------------------- /src/python/tests/test_base.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os, signal 3 | from libs.macdbg import MacDbg 4 | from ctypes import * 5 | 6 | class MacDbgTestBase(unittest.TestCase): 7 | PROGRAM_NAME = "./test_prog.app" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | cls.dbg = MacDbg() 12 | arr = (c_char_p * 2)() 13 | arr[0] = cls.PROGRAM_NAME 14 | arr[1] = 0 15 | 16 | cls.dbg.run(cls.PROGRAM_NAME, arr) 17 | 18 | def test_task(self): 19 | self.assertNotEqual(self.dbg.task, 0) 20 | 21 | @classmethod 22 | def tearDownClass(cls): 23 | cls.dbg.terminate_() 24 | 25 | if __name__ == '__main__': 26 | unittest.main() 27 | -------------------------------------------------------------------------------- /src/python/tests/test_base.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/test_base.pyc -------------------------------------------------------------------------------- /src/python/tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | from libs.const import * 3 | from ctypes import c_byte 4 | 5 | def gen_callback(info_struct): 6 | return 1 7 | 8 | 9 | class MacDbgTestException(test_base.MacDbgTestBase): 10 | def test_add_breakpoint(self): 11 | x = self.dbg.add_breakpoint("abc", PERSISTENT, self.dbg.make_callback(gen_callback)) 12 | self.assertEqual(x, 1) 13 | x = self.dbg.add_breakpoint(0xe0e, PERSISTENT, self.dbg.make_callback(gen_callback)) 14 | self.assertEqual(x, 1) 15 | x = self.dbg.add_breakpoint("hello", PERSISTENT, self.dbg.make_callback(gen_callback)) 16 | self.assertEqual(x, 0) 17 | 18 | def test_get_breaks(self): 19 | x = self.dbg.get_breaks() 20 | self.assertEqual(len(x), 2) 21 | 22 | def test_add_exception(self): 23 | x = self.dbg.add_exception_callback(self.dbg.make_callback(gen_callback), NOTE_EXEC) 24 | self.assertEqual(x, 1) 25 | x = self.dbg.add_exception_callback(self.dbg.make_callback(gen_callback), NOTE_FORK) 26 | self.assertEqual(x, 1) 27 | 28 | @classmethod 29 | def tearDownClass(cls): 30 | cls.dbg.detach() 31 | super(MacDbgTestException, cls).tearDownClass() 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /src/python/tests/test_exceptions.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/test_exceptions.pyc -------------------------------------------------------------------------------- /src/python/tests/test_memory.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | from libs.const import * 3 | from ctypes import c_byte 4 | 5 | class MacDbgTestProccessInfo(test_base.MacDbgTestBase): 6 | 7 | allocation = 0 8 | 9 | def test_image_size(self): 10 | self.assertGreater(self.dbg.get_image_size(), 0) 11 | 12 | def test_allocate(self): 13 | self.__class__.allocation = self.dbg.allocate_space(4096, VM_FLAGS_ANYWHERE) 14 | self.assertGreater(self.__class__.allocation, self.dbg.base_address) 15 | 16 | def test_change_page(self): 17 | self.dbg.change_page_protection(self.dbg.base_address, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) 18 | prot = self.dbg.get_page_protection(self.dbg.base_address) 19 | self.assertEqual(prot,'rwx') 20 | 21 | def test_disass(self): 22 | dis = self.dbg.disass(self.dbg.base_address+0xd90, 3, 1) 23 | self.assertTrue("push\trbp" in dis[0]) 24 | 25 | def test_get_page(self): 26 | prot = self.dbg.get_page_protection(self.dbg.base_address) 27 | self.assertEqual(prot,'rwx') 28 | 29 | def test_hex_dump(self): 30 | HEX_STRING = 'TEST_HEX_DUMP' 31 | addr = self.dbg.inject_code(HEX_STRING) 32 | act = self.dbg.hex_dump(addr,len(HEX_STRING)) 33 | exp = ":".join("{:02x}".format(ord(c)) for c in HEX_STRING) 34 | self.assertEqual(act, exp) 35 | 36 | def test_inject_code(self): 37 | INJECT_STRING = 'TEST_INJECT_CODE' 38 | addr = self.dbg.inject_code(INJECT_STRING) 39 | self.assertEqual(INJECT_STRING, self.dbg.read_memory_string(addr, len(INJECT_STRING))) 40 | 41 | def test_region(self): 42 | region_info = self.dbg.get_region_info(self.dbg.base_address) 43 | prot = self.dbg.protection_to_string(region_info.protection) 44 | self.assertEqual(prot,'rwx') 45 | 46 | def test_search_mem(self): 47 | search_string = "blue" 48 | search_results = self.dbg.search_mem(search_string, self.dbg.base_address, 0x40000) 49 | self.assertEqual(len(search_results), 3) 50 | 51 | def test_write_bytes(self): 52 | WRITE_STRING = 'TEST_WRITE_BYTES' 53 | addr = self.dbg.write_bytes(self.__class__.allocation , WRITE_STRING) 54 | self.assertEqual(WRITE_STRING, self.dbg.read_memory_string(addr, len(WRITE_STRING))) 55 | 56 | def test_write_memory(self): 57 | WRITE_DATA = 0x41 58 | self.dbg.write_memory(self.__class__.allocation , WRITE_DATA, 1) 59 | str = self.dbg.read_memory(self.__class__.allocation, 1) 60 | res = format(ord(str), '#04x') 61 | self.assertEqual(res, hex(WRITE_DATA)) 62 | 63 | def test_free_memory(self): 64 | size = 10 65 | allocation2 = self.dbg.allocate(self.dbg.base_address, size, VM_FLAGS_ANYWHERE) 66 | self.assertGreater(allocation2, self.dbg.base_address) 67 | status = self.dbg.free_memory(allocation2, size) 68 | self.assertEqual(status, 1) 69 | 70 | 71 | if __name__ == '__main__': 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /src/python/tests/test_memory.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/test_memory.pyc -------------------------------------------------------------------------------- /src/python/tests/test_process_info.py: -------------------------------------------------------------------------------- 1 | import test_base 2 | from libs.const import * 3 | 4 | class MacDbgTestProccessInfo(test_base.MacDbgTestBase): 5 | def test_pid(self): 6 | self.pid = self.dbg.find_pid() 7 | self.assertNotEqual(self.pid, 0) 8 | 9 | def test_base_address(self): 10 | base_address = self.dbg.get_base_address() 11 | self.assertGreater(base_address, 0) 12 | 13 | 14 | if __name__ == '__main__': 15 | unittest.main() 16 | -------------------------------------------------------------------------------- /src/python/tests/test_process_info.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/test_process_info.pyc -------------------------------------------------------------------------------- /src/python/tests/test_thread_info.py: -------------------------------------------------------------------------------- 1 | import time 2 | import test_base 3 | 4 | class MacDbgTestThreadInfo(test_base.MacDbgTestBase): 5 | 6 | threads = [] 7 | thread_handles = [] 8 | thread_ids = [] 9 | 10 | def test_all_threads(self): 11 | self.__class__.threads = self.dbg.thread_list_info(); 12 | for i in self.__class__.threads: 13 | self.assertGreater(i,0) 14 | 15 | def test_basic_info(self): 16 | for i in self.__class__.threads: 17 | basic_info = self.dbg.get_thread_basic_info(i) 18 | self.assertGreater(basic_info.user_time.microseconds, 0) 19 | self.assertGreater(basic_info.system_time.microseconds, 0) 20 | 21 | def test_identifer(self): 22 | for i in self.__class__.threads: 23 | thread_ident_info = self.dbg.get_thread_identifier_info(i) 24 | thread_id = thread_ident_info.thread_id 25 | self.assertGreater(thread_id, 0) 26 | self.thread_ids.append(thread_id) 27 | thread_handle = thread_ident_info.thread_handle 28 | self.assertGreater(thread_handle, 0) 29 | self.__class__.thread_handles.append(thread_handle) 30 | 31 | def test_proc_thread_info(self): 32 | for i in self.__class__.thread_handles: 33 | proc_t_info = self.dbg.get_proc_threadinfo(i) 34 | self.assertGreater(proc_t_info.pth_user_time, 0) 35 | self.assertGreater(proc_t_info.pth_system_time, 0) 36 | self.assertGreater(proc_t_info.pth_priority, 0) 37 | self.assertGreater(proc_t_info.pth_maxpriority, 0) 38 | 39 | def test_thread_suspend_resume(self): 40 | for i in self.__class__.threads: 41 | status = self.dbg.thread_suspend_(i) 42 | self.assertEqual(status, 0) 43 | time.sleep(2) 44 | status = self.dbg.thread_resume_(i) 45 | self.assertEqual(status, 0) 46 | 47 | def test_thread_state(self): 48 | for i in self.__class__.threads: 49 | x = self.dbg.get_thread_state(i) 50 | self.assertEqual(len(x), 21) 51 | x["r13"] = 1 52 | self.dbg.set_thread_state(i, x) 53 | x = self.dbg.get_thread_state(i) 54 | self.assertEqual(x["r13"], 1) 55 | 56 | if __name__ == '__main__': 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /src/python/tests/test_thread_info.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/src/python/tests/test_thread_info.pyc -------------------------------------------------------------------------------- /src/thread.c: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | 3 | EXPORT 4 | kern_return_t set_thread_state(mach_port_t thread, x86_thread_state64_t *break_state) { 5 | kern_return_t kret; 6 | 7 | kret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)break_state, x86_THREAD_STATE64_COUNT); 8 | EXIT_ON_MACH_ERROR("[-set_thread_state] failed", kret); 9 | 10 | return kret; 11 | } 12 | 13 | /* 14 | Given a thread populates a state and returns it 15 | states very much like a context 16 | */ 17 | EXPORT 18 | x86_thread_state64_t* get_state(thread_act_port_t thread) { 19 | kern_return_t kret; 20 | x86_thread_state64_t *state; 21 | mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT; 22 | 23 | state = safe_malloc(sizeof(x86_thread_state64_t)); 24 | kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)state, &stateCount); 25 | RETURN_ON_MACH_ERROR("[-get_state] thread_get_state()", kret); 26 | 27 | return state; 28 | } 29 | 30 | //Get the state of thread 0 from a task 31 | EXPORT 32 | x86_thread_state64_t* thread_state(mach_port_t task, uint thread_count) { 33 | kern_return_t kret; 34 | thread_act_port_array_t threadList; 35 | mach_msg_type_number_t threadCount; 36 | x86_thread_state64_t *state; 37 | 38 | kret = task_threads(task, &threadList, &threadCount); 39 | RETURN_ON_MACH_ERROR("[-thread_stae] task_threads failed", kret); 40 | 41 | state = get_state(threadList[thread_count]); 42 | DEBUG_PRINT("STATE: %llx\n", state->__rip); 43 | 44 | return state; 45 | } 46 | 47 | 48 | EXPORT 49 | kern_return_t thread_list_info(mach_port_t task,thread_act_port_array_t* threadList, mach_msg_type_number_t* threadCount) { 50 | kern_return_t kret; 51 | // thread_act_port_array_t* threadList = safe_malloc(sizeof(thread_act_port_array_t)); 52 | // mach_msg_type_number_t threadCount; 53 | 54 | kret = task_threads(task, threadList, threadCount); 55 | RETURN_ON_MACH_ERROR("[-thread_list] task_threads failed", kret); 56 | 57 | return kret; 58 | } 59 | 60 | EXPORT 61 | mach_msg_type_number_t thread_count(mach_port_t task) { 62 | kern_return_t kret; 63 | mach_msg_type_number_t threadCount; 64 | thread_act_port_array_t *threadList = safe_malloc(sizeof(thread_act_port_array_t)); 65 | 66 | kret = task_threads(task, threadList, &threadCount); 67 | RETURN_ON_MACH_ERROR("[-thread_count] task_threads failed", kret); 68 | 69 | return threadCount; 70 | } 71 | 72 | 73 | /* NEED TO ADD HERE */ 74 | 75 | 76 | /* Get the basic information (thread_basic_info_t) about a given 77 | thread. 78 | Gives you the suspend count; thread state; user time; system time; sleep time; etc. 79 | The return value is a pointer to malloc'ed memory - it is the caller's 80 | responsibility to free it. */ 81 | 82 | EXPORT 83 | thread_basic_info_t get_thread_basic_info(thread_t thread) { 84 | kern_return_t kret; 85 | integer_t *thinfo = safe_malloc(sizeof(integer_t) * THREAD_INFO_MAX); 86 | mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX; 87 | kret = thread_info (thread, THREAD_BASIC_INFO,(thread_info_t) thinfo, &thread_info_count); 88 | RETURN_ON_MACH_ERROR("[-get_thread_basic_info] failed", kret); 89 | 90 | return (thread_basic_info_t) thinfo; 91 | } 92 | 93 | /* Get the thread identifier info (thread_identifier_info_data_t) 94 | about a given thread. 95 | Gives you the system-wide unique thread number; the pthread identifier number 96 | */ 97 | 98 | EXPORT 99 | thread_identifier_info_data_t* get_thread_identifier_info(thread_t thread) { 100 | kern_return_t kret; 101 | thread_identifier_info_data_t *tident = safe_malloc(sizeof(thread_identifier_info_data_t)); 102 | mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; 103 | kret = thread_info (thread, THREAD_IDENTIFIER_INFO, (thread_info_t)tident, &tident_count); 104 | RETURN_ON_MACH_ERROR("[-get_thread_identifier_info] failed", kret); 105 | 106 | return tident; 107 | } 108 | 109 | 110 | EXPORT 111 | kern_return_t thread_terminate_(mach_port_t thread) { 112 | thread_terminate(thread); 113 | 114 | return thread_terminate(thread); 115 | } 116 | 117 | EXPORT 118 | kern_return_t thread_suspend_(mach_port_t thread) { 119 | return thread_suspend(thread); 120 | } 121 | 122 | EXPORT 123 | kern_return_t thread_resume_(mach_port_t thread) { 124 | return thread_resume(thread);; 125 | } 126 | 127 | EXPORT 128 | proc_threadinfo_t* get_proc_threadinfo (pid_t pid, uint64_t thread_handle) { 129 | struct proc_threadinfo *p_th; 130 | p_th = safe_malloc(sizeof(struct proc_threadinfo)); 131 | p_th->pth_name[0] = '\0'; 132 | int ret = proc_pidinfo (pid, PROC_PIDTHREADINFO, thread_handle, p_th, sizeof(struct proc_threadinfo)); 133 | if (ret != 0) 134 | return p_th; 135 | else 136 | return NULL; 137 | } 138 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef T_H 2 | #define T_H 3 | #include "mcdb.h" 4 | 5 | typedef struct proc_threadinfo proc_threadinfo_t; 6 | 7 | x86_thread_state64_t* get_state(thread_act_port_t thread); 8 | kern_return_t set_thread_state(mach_port_t thread, x86_thread_state64_t *break_state); 9 | x86_thread_state64_t* thread_state(mach_port_t task, uint count); 10 | kern_return_t thread_list_info(mach_port_t task,thread_act_port_array_t* threadList, mach_msg_type_number_t* threadCount); 11 | kern_return_t thread_terminate_(mach_port_t thread); 12 | kern_return_t thread_suspend_(mach_port_t thread); 13 | kern_return_t thread_resume_(mach_port_t thread); 14 | thread_identifier_info_data_t* get_thread_identifier_info(thread_t thread); 15 | thread_basic_info_t get_thread_basic_info (thread_t thread); 16 | proc_threadinfo_t* get_proc_threadinfo (pid_t pid, uint64_t thread_handle); 17 | mach_msg_type_number_t thread_count(mach_port_t task); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "mcdb.h" 2 | 3 | EXPORT 4 | void* safe_malloc(size_t x) { 5 | void *mal = malloc(x); 6 | if(mal == NULL) { 7 | fprintf(stderr, "[-safe_malloc] Error Exiting\n"); 8 | exit(-1); 9 | } 10 | 11 | memset(mal, 0, x); 12 | return mal; 13 | } 14 | 15 | void* safe_realloc(void* ptr, size_t x) { 16 | void *mal = realloc(ptr, x); 17 | if(mal == NULL) { 18 | fprintf(stderr, "[-safe_malloc] Error Exiting\n"); 19 | exit(-1); 20 | } 21 | return mal; 22 | } 23 | 24 | EXPORT 25 | void print_byte(char *byte) { 26 | int i, len; 27 | len = strlen(byte); 28 | 29 | for(i = 0; i < len; ++i) { 30 | printf("%x ", byte[i] & 0xff); 31 | } 32 | 33 | puts(""); 34 | } 35 | 36 | EXPORT 37 | void print_bytes(char *byte, int len) { 38 | int i; 39 | 40 | for(i = 0; i < len; ++i) { 41 | printf("%x ", byte[i]&0xff); 42 | } 43 | 44 | puts(""); 45 | } 46 | 47 | EXPORT 48 | char* exception_to_string(exception_type_t exc) { 49 | switch(exc) { 50 | case EXC_BREAKPOINT : return "EXC_BREAKPOINT"; 51 | case EXC_BAD_ACCESS : return "EXC_BAD_ACCESS"; 52 | case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; 53 | case EXC_ARITHMETIC : return "EXC_ARITHMETIC"; 54 | case EXC_EMULATION : return "EXC_EMULATION"; 55 | case EXC_SOFTWARE : return "EXC_SOFTWARE"; 56 | case EXC_SYSCALL : return "EXC_SYSCALL"; 57 | case EXC_MACH_SYSCALL : return "EXC_MACH_SYSCALL"; 58 | case EXC_RPC_ALERT : return "EXC_RPC_ALERT"; 59 | case EXC_CRASH : return "EXC_CRASH"; 60 | case EXC_RESOURCE : return "EXC_RESOURCE"; 61 | case EXC_GUARD : return "EXC_GUARD"; 62 | case NOTE_EXEC : return "EXEC"; 63 | case NOTE_FORK : return "FORK"; 64 | case NOTE_SIGNAL : return "SIGNAL"; 65 | case NOTE_EXIT : return "EXIT"; 66 | 67 | default: 68 | return "[-exception_to_string] unknown exception type!"; 69 | } 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /talks_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankwall/MacDBG/9025446ee97a44d0e78b60cc29657a4d9491af1d/talks_slides.pdf --------------------------------------------------------------------------------