├── .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
--------------------------------------------------------------------------------