├── Readme.txt
├── docs
├── css
│ └── style.css
├── idasim
│ ├── architecture.html
│ ├── handler.html
│ ├── idasim.html
│ ├── index.html
│ └── mmu.html
├── idasimulator
│ ├── images
│ │ ├── activate_handler.png
│ │ ├── activate_plugin.png
│ │ ├── idasimulator_command_line.png
│ │ ├── membase.png
│ │ ├── new_handler.png
│ │ ├── python_expression.png
│ │ ├── register_config_defaults.png
│ │ ├── register_config_expression.png
│ │ ├── register_config_main.png
│ │ ├── registers_configured.png
│ │ ├── right_click_menu.png
│ │ └── start_idasimulator.png
│ └── index.html
└── index.html
├── install.py
└── src
├── plugins
├── idasimlib
│ ├── __init__.py
│ ├── fuzz.py
│ ├── libc.py
│ ├── libcsman.py
│ ├── libnvram.py
│ ├── pthread.py
│ └── stdio.py
└── idasimulator.py
└── python
└── idasim
├── __init__.py
├── application.py
├── architecture.py
├── exceptions.py
├── handler.py
├── idasim.py
└── mmu.py
/Readme.txt:
--------------------------------------------------------------------------------
1 | IDASimulator is a plugin that extends IDA's conditional breakpoint support, making it easy to augment / replace complex executable code inside a debugged process with Python code.
2 | Specifically, IDASimulator makes use of conditional breakpoints in the IDA debugger to hijack the execution flow of a process and invoke Python handler functions whenever particular code blocks are executed. With support for multiple target architectures, it handles details such as register initialization, memory allocation, pointers, function arguments and return values seamlessly and transparently, making it easy to replace, modify and subvert existing functionality (or lack thereof) in the target process.
3 | IDASimulator also includes the IDASim python module, on which IDASimulator is based. This allows for all of the features of IDASimulator to be integrated into more complex IDAPython scripts.
4 | IDASimulator currently supports the x86, x86_64, ARM and MIPS32 architectures. Porting to other architectures is very easy.
5 |
6 | Why?
7 |
8 | ** The ability to dynamically intercept, replace or modify process logic is useful in a variety of situations, such as:
9 | ** Monitoring function calls
10 | ** Non-intrusive function hooking
11 | ** Simulating library functions when emulating code snippets in IDA
12 | ** Simulating hardware-dependant functionality (embedded firmware, custom hardware, unsupported ioctl's, etc)
13 | ** Any situation where the application code does one thing and you want it to do something else
14 |
15 | Introduction
16 |
17 | IDASimulator is a plugin that allows IDA users to easily augment / replace executable code inside a debugged process with Python code.
18 | Specifically, IDASimulator makes use of conditional breakpoints in the IDA debugger to hijack the execution flow of a process and invoke Python handler functions whenever particular code blocks are executed. With support for multiple target architectures, it handles details such as register initialization, memory allocation, pointers, function arguments and return values seamlessly and transparently, making it easy to replace, modify and subvert existing functionality (or lack thereof) in the target process.
19 | IDASimulator currently supports the x86, x86_64, ARM and MIPS32 architectures. Porting to other architectures is very easy.
20 | Installation
21 |
22 | The IDASimulator plugin and its associated IDASim python module can be installed with the install.py script:
23 | $ python install.py /path/to/ida/install/directory
24 |
25 | Using IDASimulator
26 |
27 | See DOCS
28 |
29 | Thanks to Storm shadow keeping a copy of this!
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | .purple { color: purple; }
2 | .green { color: green; }
3 | .red { color: red; }
4 | .blue { color: DarkBlue; }
5 | .aqua { color: DarkCyan; }
6 | .gold { color: DarkOrange; }
7 |
8 | body { width: 60%; margin-left: auto; margin-right: auto; }
9 | blockquote { font-weight: bold; }
10 | h1 { font-size: 20pt; }
11 | h2 { font-size: 14pt; }
12 | img { display: block; margin-left: auto; margin-right: auto; margin-top: 20px; margin-bottom: 25px; border: 1px solid black; }
13 |
14 |
15 | /* #usage p { margin-top: 20px; } */
16 |
--------------------------------------------------------------------------------
/docs/idasim/architecture.html:
--------------------------------------------------------------------------------
1 |
2 |
28 | Converts an integer value of size bytes into a raw string of bytes.
29 |
30 | @value - Integer value to be represented as a raw string.
31 | @size - Size of the integer value, in bytes.
32 |
33 | Returns a raw string containing the integer value in string form, and in the appropriate endianess.
34 |
35 |
36 |
37 |
38 |
def Argument(self, n, value=None)
39 |
40 |
41 |
42 | Read/write function arguments.
43 |
44 | @n - Argument index number, 0-indexed.
45 | @value - If specified, the argument will be set to this value.
46 |
47 | Returns the current argument value.
48 |
49 |
50 |
51 |
52 |
def StackPointer(self, value=None)
53 |
54 |
55 |
56 | Read/write the stack pointer register.
57 |
58 | @value - If specified, the stack pointer register will be set to this value.
59 |
60 | Returns the current stack pointer register value.
61 |
62 |
63 |
64 |
65 |
def ReturnValue(self, value=None, n=0)
66 |
67 |
68 |
69 | Read/write the function return register value.
70 |
71 | @value - If specified, the return register will be set to this value.
72 | @n - Return register index number, for those architectures with multiple return registers.
73 |
74 | Returns the current return register value.
75 |
76 |
77 |
78 |
79 |
def ProgramCounter(self, value=None)
80 |
81 |
82 |
83 | Read/write the program counter register.
84 |
85 | @value - If specified, the program counter register will be set to this value.
86 |
87 | Returns the current value of the program counter register.
88 |
89 |
90 |
91 |
92 |
def ReturnAddress(self, value=None)
93 |
94 |
95 |
96 | Read/write the return address.
97 |
98 | @value - If specified, the return address will be set to this value.
99 |
100 | Returns the current return address value.
101 |
16 | Class constructor.
17 |
18 | @idbm - Instance of IDASimMMU.
19 | @name - Name that will be assigned to the class instance.
20 | @verbose - Enable verbose mode.
21 |
22 | Returns None.
23 |
31 | Registers a given function handler for a given function name.
32 |
33 | @name - Name of the function.
34 | @handler - The function handler to call.
35 | @stubs - If True, handle calls to both extern and stub addresses.
36 |
37 | Returns True on success, False on failure.
38 |
39 |
40 |
41 |
42 |
def RegisterHandlers(self, handlers, stubs=True)
43 |
44 |
45 |
46 | Registers a set of function handlers.
47 |
48 | @handlers - A dictionary consisting of 'name':handler pairs.
49 | @stubs - If True, handle calls to both extern and stub addresses.
50 |
51 | Returns the number of handlers successfully registered.
52 |
53 |
54 |
55 |
56 |
def UnregisterHandler(self, name, stubs=True)
57 |
58 |
59 |
60 | Removes a function handler by name.
61 |
62 | @name - The name of the function handler to be removed.
63 | @stubs - If True, corresponding function stub handlers that were automatically created by RegisterHandler will also be removed.
64 |
65 | Returns None.
66 |
67 |
68 |
69 |
70 |
def UnregisterHandlers(self, purge=False)
71 |
72 |
73 |
74 | Deletes breakpoints for all registered handlers.
75 |
76 | @purge - If True, removes all handlers for all instances of IDASimFunctionHandler.
77 | If False, only handlers for this instance of IDASimFunctionHandler will be removed.
78 |
79 | Returns None.
80 |
16 | Class constructor.
17 |
18 | @handlers - A dict of function names/addresses to simulate and their corresponding handlers.
19 | @debug - Set to True to automatically start debugging.
20 | @attach - Set to True to attach to a process, rather than directly running the debugger.
21 | @membase - Specify the base address to start at when allocating memory.
22 |
23 | Returns None.
24 |
25 |
26 |
27 |
28 |
def WaitForDebugger(self)
29 |
30 |
31 |
32 | Waits for the debugger event (WFNE_CONT | WFNE_SUSP).
33 | Called internally by StartDebugger and AttachDebugger.
34 |
35 | Returns None.
36 |
37 |
38 |
39 |
40 |
def StartDebugger(self)
41 |
42 |
43 |
44 | Starts the debugger (equivalent of pressing F9).
45 | Called internally by __init__ if debug=True.
46 |
47 | Returns None.
48 |
49 |
50 |
51 |
52 |
def AttachDebugger(self, pid=-1)
53 |
54 |
55 |
56 | Attaches the debugger to a running process.
57 | Called internally by __init__ if attach=True
58 |
59 | @pid - The PID of the process to attach to (user will be prompted if not specified).
60 |
61 | Returns None.
62 |
63 |
64 |
65 |
66 |
def vsprintf(self, fmt, index)
67 |
68 |
69 |
70 | Formats format strings.
71 |
72 | @fmt - The format string.
73 | @index - The index of the function argument containing the first format string argument (0-based).
74 |
75 | Returns a formatted string.
76 |
77 |
78 |
79 |
80 |
def GetArguments(self, index, n)
81 |
82 |
83 |
84 | Get a list of function arguments. Any valid string pointers will be converted to strings.
85 |
86 | @index - First argument index, 0-based.
87 | @n - The number of arguments to retrieve.
88 |
89 | Returns a list of n arguments.
90 |
91 |
92 |
93 |
94 |
def Malloc(self, data=None, size=0)
95 |
96 |
97 |
98 | Allocates space in the debugger's memory.
99 |
100 | @data - Fill the allocated space with this data.
101 | @size - If data is None, allocate and zero out size bytes of memory.
102 |
103 | Returns the address of the allocated memory.
104 |
105 |
106 |
107 |
108 |
def ARGV(self, argv)
109 |
110 |
111 |
112 | Allocates space for an argv data structure.
113 |
114 | @argv - A list of argv strings.
115 |
116 | Returns the address of the argv array of pointers.
117 |
118 |
119 |
120 |
121 |
def String(self, string, raw=False)
122 |
123 |
124 |
125 | Creates a NULL-terminated string in the debugger's memory.
126 |
127 | @string - The string, or list of strings, to place into memory.
128 | @raw - If set to True, the string will not be NULL terminated.
129 |
130 | Returns the address, or list of addresses, of the string(s) in memory.
131 |
132 |
133 |
134 |
135 |
def Int(self, value, size)
136 |
137 |
138 |
139 | Creates an integer value of size bytes in the debugger's memory.
140 |
141 | @value - The integer value, or list of values, to place into memory.
142 | @size - The size of the interger value(s), in bytes.
143 |
144 | Returns the address, or a list of addresses, of the integer(s) in memory.
145 |
146 |
147 |
148 |
149 |
def DoubleWord(self, dword)
150 |
151 |
152 |
153 | Places an 8 byte integer into the debugger's memory.
154 |
155 | @dword - The 8 byte register value, or list of values, to place into memory.
156 |
157 | Returns the address, or a list of addresses, of the dword(s) in memory.
158 |
159 |
160 |
161 |
def Word(self, word)
162 |
163 |
164 |
165 | Places a four byte integer into the debugger's memory.
166 |
167 | @word - The four byte integer value, or list of values, to place into memory.
168 |
169 | Returns the address, or a list of addresses, of the word(s) in memory.
170 |
171 |
172 |
173 |
174 |
def HalfWord(self, hword)
175 |
176 |
177 |
178 | Places two bytes of data into the debugger's memory.
179 |
180 | @hword - The two byte value, or list of values, to place into memory.
181 |
182 | Returns the address, or a list of addresses, of the half word(s) in memory.
183 |
184 |
185 |
186 |
187 |
def Byte(self, byte)
188 |
189 |
190 |
191 | Places one byte of data into the debugger's memory.
192 |
193 | @byte - The byte value, or list of values, to place into memory.
194 |
195 | Returns the address, or a list of addresses, of the byte(s) in memory.
196 |
197 |
198 |
199 |
200 |
def Cleanup(self)
201 |
202 |
203 |
204 | Removes all registered function simulation hooks.
205 |
206 | Returns None.
207 |
208 |
209 |
210 |
211 |
self.mmu
212 |
213 |
214 |
215 | Instance of the IDASimMMU class.
216 | This instance must be used for all direct IDASimMMU method calls.
217 |
12 | IDASim is an IDAPython module that supports simple simulation / replacement of executable code
13 | with Python code.
14 | Pesky details such as allocating memory space for strings and data, accessing function arguments,
15 | and setting return values are all handled by IDASim.
16 |
17 |
18 |
19 | IDASim is especially useful for:
20 |
21 |
22 |
Simulating library function calls when emlating code snippets in IDA
23 |
Simulating function calls that fail on your machine due to missing/incorrect hardware or variations in environments
24 |
Easily changing the functionality of a code block by dynamically modifying registers / memory values
25 |
26 |
27 |
28 |
29 |
30 |
31 |
Using IDASim
32 |
33 |
34 | To use IDASim, you must:
35 |
36 |
37 |
Import the idasim Python module
38 |
Create function handlers to replace existing functions in the running process
53 |
54 | This function is used to display dynamically generated debug information, and is called repeatedly throughout
55 | the code. However, the contents of this function were #defined out during the production build of our target,
56 | so none of this information ever gets displayed. We can change that:
57 |
58 |
69 |
70 | Here, we've told IDASim that we want all calls to the function named 'trace' redirected to our handler function, mytrace().
71 | Note that the class instace must be assigned a global name, in this case 'sim', even if it is not
72 | subsequently referenced by your script.
73 |
74 |
75 | The mytrace handler defines the first two arguments for the function (the rest are obviously variable arguments): trace_level
76 | and format_string. Since we know that the second argument, format_string, will be a pointer to a NULL-terminated string, we've
77 | defined the default value for format_string as a string type. This means that instead of being passed the raw value for the
78 | format_string argument (a pointer to the NULL-terminated format string), the actual format string will be passed to mytrace.
79 |
80 |
81 | Format string parsing can typically be handled by the built in IDASim.vsprintf() method. We pass the format_string as the first argument to
82 | IDASim.vsprintf(), and also tell it that the format string arguments start at function argument index 2 (i.e., the third argument
83 | to the trace function will be the first format string argument).
84 |
85 |
86 | IDASim.vsprintf() will return the formatted string, so we simply print that out to the IDA console and return 0. The
87 | return register will be automatically populated with our return value, and execution of the process will jump back
88 | to the return address specified by the caller.
89 |
90 |
91 |
92 |
Returning String/Data Pointers
93 |
94 | What about handlers that need to return string/data pointers? Let's see what would be needed to simulate libc's strdup():
95 |
96 |
102 |
103 | When a handler returns a string type, that string data gets placed into the running process's memory, the return
104 | register is populated with a pointer to the data, and process execution is transferred back to the caller's return address.
105 |
106 |
107 | Note that since strdup() needs to return a NULL terminated string, the handler function needs to ensure that a NULL byte
108 | is placed at the end of the string that it returns.
109 |
110 |
111 |
112 |
Controlling Execution Flow
113 |
114 | OK, but what if we only want to simulate a function under specific circumstances? Each function handler can control if the
115 | original function is executed or not. If a handler returns None, nothing further is done and the process continues execution normally:
116 |
117 |
140 |
141 | In the above example, gotohandler() causes process execution to continue from the address 0x0040BAAD. By contrast, jumptohandler()
142 | causes the process execution to continue from the current program counter address plus 8 bytes.
143 |
144 |
145 |
146 |
Starting the Debugger
147 |
148 | If the debug or attach arguments to IDASim() are set to true, the IDASim class constructor will respectively attempt to automatically
149 | start the debugger or attach to a remote debugger before returning:
150 |
151 |
157 |
158 | This is especially useful if you wish to automate the initialization of memory / registers before debugging the target process.
159 | The IDASim class provides various methods for allocating strings, integers, or raw data in process memory. As an example, let's
160 | use IDASim to configure the argc and argv arguments when running the main() function of a target binary as a code snippet:
161 |
162 |
169 |
170 | This uses the Architecture.Argument() method for setting the values of the main function's first argument (argc) and second argument (argv).
171 | Note that the special IDASim.ARGV() method is used for allocating space for the argv strings and generating an array of pointers to
172 | those strings.
173 |
174 |
175 |
176 |
Memory Management
177 |
178 | The IDASim class provides various methods for allocating data in process memory. Strings, integers and raw data can be placed directly
179 | into process memory using the following methods (where applicable, endianess is handled automatically):
180 |
181 |
182 |
183 | sim.String("Put this string in memory and ensure it is NULL terminated.")
184 | sim.Word(0x0040BAAD)
185 | sim.HalfWord(0xBAAD)
186 | sim.Byte(0xAD)
187 | sim.Malloc("\x00\x01\x02\x03\x04\x05")
188 |
189 |
190 |
191 | When allocating space for strings / data, IDASim needs to have some memory available where it can place data without interfering with
192 | the memory used by the running process. By default, IDASim looks for a segment of memory named 'MMU' to use. If this does not exist
193 | it looks to see if the last segment of memory is named 'MEMORY' or 'RAM', and if so, uses that. Else, it will begin allocating data
194 | from the bottom of the stack segment. If this also fails, it will default to using a base address of 0x100000.
195 |
196 | While this usually works fine, you may need to explicitly specify a memory segment to use in order to ensure data integrity. This can be
197 | done by defining a memory segment named 'MMU', or by passing the start address to use for data storage to the IDASim class constructor:
198 |
199 |
200 |
201 | sim = IDASim(membase=0x0040BAAD)
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
Porting
210 |
211 |
212 | To add support for a new architecture, add a new entry to the Architecture.ARCH dictionary:
213 |
214 |
215 |
216 | ARCH = {
217 |
218 | ...
219 |
220 | # Dictionary key is the architecture name
221 | 'mips' : {
222 | # Offset from the stack pointer where function arguments start
223 | 'spoffset' : 0x10,
224 | # Registers used to pass function arguments
225 | 'argreg' : ['a0', 'a1', 'a2', 'a3'],
226 | # Register(s) used to store function return values
227 | 'retval' : ['v0', 'v1'],
228 | # Name of the stack pointer register
229 | 'sp' : 'sp',
230 | # Name of the return address register
231 | 'ra' : 'ra',
232 | # Name of the program counter register
233 | 'pc' : 'pc',
234 | },
235 |
236 | ...
237 | }
238 |
239 |
240 |
241 | Note that any of the required values can be specified as pointer values if necessary. For example, in x86, the return
242 | address is not stored in a register, but rather pushed onto the stack such that the stack pointer register points
243 | to the return address on the stack at the beginning of any function. Thus, the value for the 'ra' key can be specified
244 | as '*ESP', indicating that the ESP register contains a pointer to the memory location where the return address is located.
245 |
246 |
247 | If your target architecture is supported but your specific target processor is not supported, all
248 | that is required is to add a new entry to Architecture.PROCESSORS dictionary:
249 |
250 |
251 |
252 | PROCESSORS = {
253 |
254 | ...
255 |
256 | # Dictionary key is the name of the IDA processory module
257 | 'mipsl' : [{
258 | # The architecture key value, as specified in Architecture.ARCH
259 | 'architecture' : 'mips',
260 | # The endianess of this processor module, one of: 'little', 'big'
261 | 'endianess' : 'little',
262 | # The default bus width of this processory module
263 | 'bits' : 32
264 | }],
265 |
266 | ...
267 | }
268 |
16 | Class constructor.
17 |
18 | @base - Specify the base address to start at when allocating memory. Will be auto-detected if not specified.
19 |
20 | Returns None.
21 |
22 |
23 |
24 |
25 |
def reset(self)
26 |
27 |
28 |
29 | Resets the current memory allocation pointer to the base memory allocation address.
30 | Called automatically when the debugger is started/stopped.
31 |
32 | Returns None.
33 |
34 |
35 |
36 |
37 |
def base(self, base=None)
38 |
39 |
40 |
41 | Sets the base memory allocation address.
42 |
43 | @base - Specify the base address to start at when allocating memory.
44 |
45 | Returns None.
46 |
47 |
48 |
49 |
50 |
def malloc(self, data=None, size=0)
51 |
52 |
53 |
54 | Allocates space for data in the debugger's memory and populates it.
55 |
56 | @data - Data to place into memory. If None, NULL bytes will be used.
57 | @size - Size of memory to allocate. If 0, len(data) bytes will be allocated.
58 |
59 | Returns the address of the allocated memory.
60 |
12 | IDASimulator is a plugin that allows IDA users to easily augment / replace executable code inside a debugged process with Python code.
13 |
14 |
15 |
16 | Specifically, IDASimulator makes use of conditional breakpoints in the IDA debugger to hijack the execution flow of a process
17 | and invoke Python handler functions whenever particular code blocks are executed. With support for multiple target architectures,
18 | it handles details such as register initialization, memory allocation, pointers, function arguments and return values seamlessly
19 | and transparently, making it easy to replace, modify and subvert existing functionality (or lack thereof) in the target process.
20 |
21 |
22 |
23 | IDASimulator currently supports the x86, x86_64, ARM and MIPS32 architectures. Porting to other architectures is very easy.
24 |
25 |
26 |
27 |
28 |
Installation
29 |
30 |
31 | The IDASimulator plugin and its associated IDASim python module can be installed with the install.py script:
32 |
33 |
45 | The IDASimulator plugin can be activated through the plugins menu, or by pressing the Alt+0 hotkey:
46 |
47 |
48 |
49 |
50 |
51 |
52 | The IDASimulator window displays all of the named locations in the current IDB that it has handlers for:
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Double-clicking on an entry will enable/disable the selected handler:
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | The right-click menu in the IDASimulator window provides a variety of additional options:
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | The Enable/disable selected handlers option is synonymous with double-clicking on a handler, as described above.
77 |
78 |
79 |
80 | The Enable all handlers and Disable all handlers options will respecitvely enable or disable all displayed handlers.
81 |
82 |
83 |
84 | The Reset to defaults option will remove all changes you have made and disable all handlers.
85 |
86 |
87 |
88 | The Show / hide unsupported imports option displays or hides all imports that do not have associated IDASimulator handlers.
89 |
90 |
91 |
92 | The Jump to selected name option will take you to the location of the selected name in IDA's disassembly window.
93 |
94 |
95 |
96 | The Save settings option saves all of the current IDASimulator settings for that IDB. This is also done automatically whenever the IDASimulator
97 | window is closed.
98 |
99 |
100 |
101 | The Quit option will save the current settings and disable the IDASimulator plugin (just closing the IDASimulator window does not disable the plugin).
102 |
103 |
104 |
The Set MMU base address option allows you to manually control the start address of where IDASimulator handler data (such as strings)
105 | is allocated in memory. This is normally auto-detected, but in some cases may need to be explicitly controlled:
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | The Set initialization values option allows you to define the initialization values of registers and define python statements,
114 | as well as control when those initializations are exercised. By default, they will be invoked when the debugger is started/attached:
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | When process execution reaches the specified named location, the specified settings will be applied:
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | Note that if the location named in the Initialization Configuration conflicts with a handler name, the handler for that
131 | named location will be removed and replaced with the Initialization Configuration handler.
132 |
133 |
134 |
135 | Up to ten registers may be specified. Their values may be assigned an integer value (decimal or hexadecimal), a quoted string value (the string will
136 | be allocated in memory and the register will be populated with the address of the string), or as the result of an
137 | IDASim or Achitecture method call:
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Valid Python expressions may also be included in the register assignment:
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | And if no register is specified, arbitrary Python statements may be entered into the value field:
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
Command Line
164 |
165 |
166 | When the IDASimulator plugin is activated, it creates a global instance of the IDASim class named 'IDASIM'.
167 | This global class instance is available for use in any IDAPython statement, and can be invoked manually from the command line during a debugging session:
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | IDAPython scripts invoked while the IDASimulator plugin is active should use the IDASIM class instance rather than creating a new class instance.
176 | Multiple class instances in use at the same time can result in corruption of data allocated by IDASim, due to multiple active instances of
177 | IDASimMMU.
178 | If multiple instances of IDASim are necessary, care should be taken to ensure that the base addresses used for memory allocation by the various
179 | instances of IDASimMMU are sufficiently different.
180 |
181 |
182 |
183 |
184 |
185 |
Writing IDASimulator Handlers
186 |
187 |
188 | Although IDASimulator comes with a set of handlers for common library and API functions, its real power is the ability to easily
189 | craft custom handlers for your target process.
190 |
191 |
192 |
193 | IDASimulator handler files are installed to the plugins/idasimlib subdirectory of the IDA install path. Each handler file must have a .py
194 | file extension, and must contain at least one uniquely named class. The constructor for each class must accept one argument, which will be
195 | an instance of the IDASim class:
196 |
197 |
208 | Every member of the class that does not start with an underscore will be loaded as a handler function:
209 |
210 |
211 |
212 | classMyHandlerClass:
213 |
214 | def__init__(self, idasim):
215 | self.idasim = idasim
216 |
217 | # Handler for the function named 'strcpy'
218 | # Writes a NULL-terminated string from src to the dst address.
219 | defstrcpy(self, dst, srcstr=''):
220 | '''
221 | A test to simulate strcpy functionality.
222 | '''
223 | idc.DbgWrite(dst, srcstr + "\x00")
224 | return dst
225 |
226 | # Handler for the function named 'strdup'
227 | # Returns a NULL-terminated string, which will be automatically allocated in memory
228 | defstrdup(self, src=''):
229 | return src + "\x00"
230 |
231 |
232 |
233 |
234 |
235 | If a handler accepts arguments, the appropriate arguments will be passed to the handler, just as they were passed to the
236 | original function. For arguments that accept pointers to NULL-terminated strings, the default value may be defined as a string type.
237 | In this case, instead of being passed the raw argument value (the pointer to the string), the actual string value will be supplied.
238 |
239 |
240 |
241 | Handlers can also access function arguments via the Architecture.Arguments method.
242 | An instance of the Architecture class is available through the cpu member of the IDASim class instance.
243 |
244 |
245 |
246 | A handler can return an integer, string, None, or raise a JumpTo or GoTo exception.
247 | See the IDASim documentation for more details.
248 |
249 |
250 |
251 | With the handlers written and the file placed in the idasimlib directory, refresh the IDASimulator window to load the new handlers (Right Click -> Refresh, or Ctl+U):
252 |
12 | IDASimulator is a plugin that extends IDA's conditional breakpoint support, making it easy to augment / replace complex executable code inside a debugged process with Python code.
13 |
14 |
15 |
16 | Specifically, IDASimulator makes use of conditional breakpoints in the IDA debugger to hijack the execution flow of a process
17 | and invoke Python handler functions whenever particular code blocks are executed. With support for multiple target architectures,
18 | it handles details such as register initialization, memory allocation, pointers, function arguments and return values seamlessly
19 | and transparently, making it easy to replace, modify and subvert existing functionality (or lack thereof) in the target process.
20 |
21 |
22 |
23 | IDASimulator also includes the IDASim python module, on which IDASimulator is based. This allows for all of the features of IDASimulator
24 | to be integrated into more complex IDAPython scripts.
25 |
26 |
27 |
28 | IDASimulator currently supports the x86, x86_64, ARM and MIPS32 architectures. Porting to other architectures is very easy.
29 |
30 |
31 |
32 |
33 |
Why?
34 |
35 |
36 | The ability to dynamically intercept, replace or modify process logic is useful in a variety of situations, such as:
37 |
38 |
39 |
Monitoring function calls
40 |
Non-intrusive function hooking
41 |
Simulating library functions when emulating code snippets in IDA