├── LICENSE
└── README.md
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ghidra Snippets
2 | *Ghidra Snippets* is a collection of Python examples showing how to work with [Ghidra](https://ghidra-sre.org/) APIs. There are three primary APIs covered here, the [Flat Program API][1], the [Flat Decompiler API][2], and everything else ("Complex API"). The Flat APIs are 'simple' versions of the full fledged Complex Ghidra API. They are a great starting point for anyone looking to develop Ghidra modules and/or scripts.
3 |
4 | At some point however, you need to reach outside of the Flat APIs to do really interesting things. Everything here is just a mashup to get the job done. There's more than one way to accomplish each task so make sure you ask yourself if these snippets are really what you need before copy/pasta.
5 |
6 | # Latest Release & API Docs
7 | * Loooking for the latest release of Ghidra? [Download it here][0].
8 | * Loooking for the latest API Docs? In Ghidra's UI, select **Help** -> **Ghidra API Help** to unpack the docs and view them.
9 | * Just want a quick online API reference? [Ghidra.re](https://ghidra.re/) hosts an online version of the [API docs](https://ghidra.re/ghidra_docs/api/index.html) (may not be up to date).
10 |
11 | # Contributing
12 |
13 | Feel free to submit pull requests to master on this repo with any modifications you see fit. Most of these snippets are meant to be verbose, but if you know of a better way to do something, please share it via pull request or submitting an issue. Thanks!
14 |
15 | # Table of Contents
16 |
17 |
18 | Working with the Flat APIs
19 |
20 | * [`Using the FlatProgramAPI`](#using-the-flatprogramapi)
21 | * [`Using the FlatDecompilerAPI`](#using-the-flatdecompilerapi)
22 | * [`Using the FlatDebuggerAPI`](#using-the-flatdebuggerapi)
23 |
24 |
25 |
26 |
27 | Working with Projects
28 |
29 | * [`Get the name and location on disk of the current project`](#get-the-name-and-location-on-disk-of-the-current-project)
30 | * [`List all programs in the current project`](#list-all-programs-in-the-current-project)
31 |
32 |
33 |
34 |
35 |
36 | Working with Programs
37 |
38 | * [`List the current program name and location on disk`](#list-the-current-program-name-and-location-on-disk)
39 | * [`List the name and size of program sections`](#list-the-name-and-size-of-program-sections)
40 |
41 |
42 |
43 |
44 | Working with Functions
45 |
46 | * [`Enumerate all functions printing their name and address`](#enumerate-all-functions-printing-their-name-and-address)
47 | * [`Get a function name by address`](#get-a-function-name-by-address)
48 | * [`Get a function address by name`](#get-a-function-address-by-name)
49 | * [`Get cross references to a function`](#get-cross-references-to-a-function)
50 | * [`Analyzing function call arguments`](#analyzing-function-call-arguments)
51 | * [`Analyzing function call arguments at cross references`](#analyzing-function-call-arguments-at-cross-references)
52 | * [`Rename functions based on strings`](#rename-functions-based-on-strings)
53 |
54 |
55 |
56 |
57 | Working with Basic Blocks
58 |
59 | * [`Print details about basic blocks in a select function`](#print-details-about-basic-blocks-in-a-select-function)
60 |
61 |
62 |
63 |
64 | Working with Instructions
65 |
66 | * [`Print all instructions in a select function`](#print-all-instructions-in-a-select-function)
67 | * [`Find all calls and jumps to a register`](#find-all-calls-and-jumps-to-a-register)
68 | * [`Count all mnemonics in a binary`](#count-all-mnemonics-in-a-binary)
69 |
70 |
71 |
72 |
73 | Working with Variables
74 |
75 | * [`Get a stack variable from a Varnode or VarnodeAST`](#get-a-stack-variable-from-a-varnode-or-varnodeast)
76 | * [`Get stack variables from a PcodeOpAST`](#get-a-stack-variable-from-a-pcodeopast)
77 |
78 |
79 |
80 |
81 | Working with the Decompiler
82 |
83 | * [`Print decompiled code for a specific function`](#print-decompiled-code-for-a-specific-function)
84 | * [`Getting variable information from the decompiler`](#getting-variable-information-from-the-decompiler)
85 |
86 |
87 |
88 |
89 | Working with Comments
90 |
91 | * [`Get all Automatic comments for a function`](#get-all-automatic-comments-for-a-function)
92 | * [`Get specific comment types for all functions`](#get-specific-comment-types-for-all-functions)
93 |
94 |
95 |
96 |
97 |
98 | Working with PCode
99 |
100 | * [`Emulating a function`](#emulating-a-function)
101 | * [`Dumping Raw PCode`](#dumping-raw-pcode)
102 | * [`Dumping Refined PCode`](#dumping-refined-pcode)
103 | * [`Plotting a Function AST`](#plotting-a-function-ast)
104 |
105 |
106 |
107 |
108 | Working with Graphs
109 |
110 | * [`Creating a Call Graph`](#creating-a-call-graph)
111 |
112 |
113 |
114 |
115 | Miscellaneous
116 |
117 | * [`Program Slices`](#program-slices)
118 |
119 |
120 |
121 | ## Working with the Flat APIs
122 | As stated in the introduction, the simplest method of using Ghidra's API is via the Flat APIs. As of Ghidra 10.2 this includes FlatProgramAPI, FlatDecompilerAPI, and the most recent FlatDebuggerAPI. The Flat APIs are designed to offer a stable API interface to some of Ghidra's high level functionality. The simplicity and convenience offered by the Flat APIs can be quickly eclipsed by their limited functionality. However, if what you need to do is offered via the Flat APIs, it's highly recommended you use them.
123 |
124 | ### Using the FlatProgramAPI
125 | The `FlatProgramAPI` is a simple interface to Ghidra's program related functionality. This includes functions, data, instructions, etc.
126 |
127 | ```python
128 | from ghidra.program.flatapi import FlatProgramAPI
129 |
130 | state = getState()
131 | program = state.getCurrentProgram()
132 | fpapi = FlatProgramAPI(program)
133 |
134 | for x in dir(fpapi): print(x)
135 | print(fpapi.currentProgram)
136 | print(fpapi.firstFunction)
137 | ```
138 |
139 |
140 | Output example
141 |
142 | ```
143 | MAX_REFERENCES_TO
144 | __class__
145 | <...snip...>
146 | addressFactory
147 | analyze
148 | analyzeAll
149 | <...snip...>
150 | currentProgram
151 | disassemble
152 | <...snip...>
153 | findStrings
154 | firstData
155 | firstFunction
156 | firstInstruction
157 | getAddressFactory
158 | getBookmarks
159 | <...snip...>
160 | bug - .ProgramDB
161 | _init
162 | ```
163 |
164 |
165 |
[⬆ Back to top](#table-of-contents)
166 |
167 | ### Using the FlatDecompilerAPI
168 | The `FlatDecompilerAPI` is a simple interface to Ghidra's decompiler and requires an instance of the `FlatProgramAPI` for initialization in order to do anything useful. In other words, we need a target program to use the decompiler.
169 |
170 | ```python
171 | from ghidra.app.decompiler.flatapi import FlatDecompilerAPI
172 | from ghidra.program.flatapi import FlatProgramAPI
173 |
174 | fpapi = FlatProgramAPI(getState().getCurrentProgram())
175 | fdapi = FlatDecompilerAPI(fpapi)
176 |
177 | for x in dir(fdapi): print(x)
178 |
179 | main_decomp = fdapi.decompile(fpapi.getFunction('main'))
180 | print(main_decomp)
181 |
182 | ```
183 |
184 |
185 | Output example
186 |
187 | ```
188 | <...snip...>
189 | decompile
190 | decompiler
191 | dispose
192 | equals
193 | getClass
194 | getDecompiler
195 | hashCode
196 | initialize
197 | notify
198 | notifyAll
199 | toString
200 | wait
201 |
202 | int main(void)
203 |
204 | {
205 | int retVal;
206 | long in_FS_OFFSET;
207 | int choice;
208 | char *local_38 [5];
209 | long stackCookie;
210 |
211 | stackCookie = *(long *)(in_FS_OFFSET + 0x28);
212 | choice = 0;
213 | local_38[0] = "My secret";
214 | local_38[1] = "Red";
215 | local_38[2] = "Green";
216 | local_38[3] = "Blue";
217 | printf("Choice: ");
218 | __isoc99_fscanf(stdin,"%d",&choice);
219 | if ((choice == 0) || (3 < choice)) {
220 | printf("Invalid choice: %d!\n",(ulong)(uint)choice);
221 | retVal = -1;
222 | }
223 | else {
224 | printf("data[%d]: %s\n",(ulong)(uint)choice,local_38[(int)(char)choice]);
225 | retVal = 0;
226 | }
227 | if (stackCookie != *(long *)(in_FS_OFFSET + 0x28)) {
228 | /* WARNING: Subroutine does not return */
229 | __stack_chk_fail();
230 | }
231 | return retVal;
232 | }
233 | ```
234 |
235 |
236 |
[⬆ Back to top](#table-of-contents)
237 |
238 | ### Using the FlatDebuggerAPI
239 | The `FlatDebuggerAPI` is a simple interface to Ghidra's debugger and trace functionality. This is a new feature as of Ghidra 10.2 and yet to be documented in the Ghidra 10.2 API docs. For some extra context about this API, see [DemoDebuggerScript.java](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_10.2_build/Ghidra/Debug/Debugger/ghidra_scripts/DemoDebuggerScript.java). As I learn more about this API I'll update this section.
240 |
241 | ```python
242 | from ghidra.debug.flatapi import FlatDebuggerAPI
243 |
244 | fdapi = FlatDebuggerAPI
245 |
246 | for x in dir(fdapi): print(x)
247 | ```
248 |
249 |
250 | Output example
251 |
252 | ```
253 | <...snip...>
254 | kill
255 | launch
256 | launchOffers
257 | <...snip...>
258 | writeMemory
259 | writeRegister
260 | ```
261 |
262 |
263 |
[⬆ Back to top](#table-of-contents)
264 |
265 | ## Working with Projects
266 | A Ghidra *Project* (class [GhidraProject][4]) contains a logical set of program binaries related to a reverse engineering effort. Projects can be shared (collaborative) or non-shared (private). The snippets in this section deal with bulk import and analysis, locating project files on disk, and more.
267 |
268 | ### Get the name and location on disk of the current project
269 | If you're looking to automate analysis using headless scripts you'll likley have to deal with project management. This includes adding binaries to existing projects, cleaning up old projects, or perhaps syncing analysis to shared projects.
270 |
271 | ```python
272 | state = getState()
273 | project = state.getProject()
274 | program = state.getCurrentProgram()
275 | locator = project.getProjectData().getProjectLocator()
276 | print("type(state): {}".format(type(state)))
277 | print("type(project): {}".format(type(project)))
278 | print("type(program): {}".format(type(program)))
279 | print("type(locator): {}".format(type(locator)))
280 | print("Project Name: {}".format(locator.getName()))
281 | print("Files in this project: {}".format(project.getProjectData().getFileCount()))
282 | print("Is a remote project: {}".format(locator.isTransient()))
283 | print("Project location: {}".format(locator.getLocation()))
284 | print("Project directory: {}".format(locator.getProjectDir()))
285 | print("Lock file: {}".format(locator.getProjectLockFile()))
286 | print("Marker file: {}".format(locator.getMarkerFile()))
287 | print("Project URL: {}".format(locator.getURL()))
288 | ```
289 |
290 |
291 | Output example
292 |
293 | ```
294 | type(state):
295 | type(project):
296 | type(program):
297 | type(locator):
298 | Project Name: pcodeproject
299 | Files in this project: 4
300 | Is a remote project: 0
301 | Project location: C:\Users\username\Desktop\pcode
302 | Project directory: C:\Users\username\Desktop\pcode\pcodeproject.rep
303 | Lock file: C:\Users\username\Desktop\pcode\pcodeproject.lock
304 | Marker file: C:\Users\username\Desktop\pcode\pcodeproject.gpr
305 | Project URL: ghidra:/C:/Users/username/Desktop/pcode/pcodeproject
306 | ```
307 |
308 |
309 |
[⬆ Back to top](#table-of-contents)
310 |
311 | ### List all programs in the current project
312 | A Ghidra project is a logical collection binaries that relate to a specific RE effort. This might be a single executable with multiple shared objects, or multiple executables with numerous third-party libraries, kernel modules, and drivers.
313 |
314 | ```python
315 | state = getState()
316 | project = state.getProject()
317 | locator = project.getProjectData().getProjectLocator()
318 | projectMgr = project.getProjectManager()
319 | activeProject = projectMgr.getActiveProject()
320 | projectData = activeProject.getProjectData()
321 | rootFolder = projectData.getRootFolder()
322 |
323 | print("type(state): {}".format(type(state)))
324 | print("type(project): {}".format(type(project)))
325 | print("type(projectMgr): {}".format(type(projectMgr)))
326 | print("type(activeProject): {}".format(type(activeProject)))
327 | print("type(projectData): {}".format(type(projectData)))
328 | print("type(rootFolder): {}".format(type(rootFolder)))
329 |
330 | projectName = locator.getName()
331 | fileCount = projectData.getFileCount()
332 | files = rootFolder.getFiles()
333 |
334 | print("The project '{}' has {} files in it:".format(projectName, fileCount))
335 | for file in files:
336 | print("\t{}".format(file))
337 | ```
338 |
339 |
340 | Output example
341 |
342 | ```
343 | type(state):
344 | type(project):
345 | type(projectMgr):
346 | type(activeProject):
347 | type(projectData):
348 | type(rootFolder):
349 | The project 'pcodeproject' has 4 files in it:
350 | pcodeproject:/WinSCP.exe
351 | pcodeproject:/deobExampleX86
352 | pcodeproject:/deobHookExampleX86
353 | pcodeproject:/server
354 | ```
355 |
356 |
357 |
[⬆ Back to top](#table-of-contents)
358 |
359 |
360 | ## Working with Programs
361 | A *Program* is a binary component within a Ghidra Project. The snippets in this section deal with gathering information about the Programs within a Project.
362 |
363 | ### List the current program name and location on disk
364 | ```python
365 | state = getState()
366 | currentProgram = state.getCurrentProgram()
367 | name = currentProgram.getName()
368 | location = currentProgram.getExecutablePath()
369 | print("The currently loaded program is: '{}'".format(name))
370 | print("Its location on disk is: '{}'".format(location))
371 | ```
372 |
373 |
374 | Output example
375 |
376 | ```
377 | The currently loaded program is: 'deobExampleX86'
378 | Its location on disk is: '/C:/Users/username/Desktop/pcode/emulation/deobExampleX86'
379 | ```
380 |
381 |
382 |
[⬆ Back to top](#table-of-contents)
383 |
384 |
385 | ### List the name and size of program sections
386 | ```python
387 | blocks = currentProgram.getMemory().getBlocks()
388 | for block in blocks:
389 | print("Name: {}, Size: {}".format(block.getName(), block.getSize()))
390 | ```
391 |
392 |
393 | Output example
394 |
395 | ```
396 | Name: segment_2.1, Size: 568
397 | Name: .interp, Size: 28
398 | Name: .note.ABI-tag, Size: 32
399 | Name: .note.gnu.build-id, Size: 36
400 | Name: .gnu.hash, Size: 36
401 | Name: .dynsym, Size: 360
402 | Name: .dynstr, Size: 192
403 | Name: .gnu.version, Size: 30
404 | Name: .gnu.version_r, Size: 48
405 | Name: .rela.dyn, Size: 216
406 | Name: .rela.plt, Size: 192
407 | Name: .init, Size: 23
408 | Name: .plt, Size: 144
409 | Name: .plt.got, Size: 8
410 | Name: .text, Size: 706
411 | Name: .fini, Size: 9
412 | Name: .rodata, Size: 134
413 | Name: .eh_frame_hdr, Size: 68
414 | Name: .eh_frame, Size: 296
415 | Name: .init_array, Size: 8
416 | Name: .fini_array, Size: 8
417 | Name: .dynamic, Size: 496
418 | Name: .got, Size: 128
419 | Name: .data, Size: 16
420 | Name: .bss, Size: 16
421 | Name: EXTERNAL, Size: 104
422 | Name: .comment, Size: 43
423 | Name: .shstrtab, Size: 254
424 | Name: .strtab, Size: 672
425 | Name: .symtab, Size: 1728
426 | Name: _elfSectionHeaders, Size: 1856
427 | ```
428 |
429 |
430 |
[⬆ Back to top](#table-of-contents)
431 |
432 |
433 | ## Working with Functions
434 | A *Function* is a subroutine within an Program. The snippets in this section deal with gathering information about Functions and modifying them within an Program.
435 |
436 | ### Enumerate all functions printing their name and address
437 | There are at least two ways to do this. The output is the same for each method.
438 | ```python
439 | # Method 1:
440 | func = getFirstFunction()
441 | while func is not None:
442 | print("Function: {} @ 0x{}".format(func.getName(), func.getEntryPoint()))
443 | func = getFunctionAfter(func)
444 |
445 | # Method 2:
446 | fm = currentProgram.getFunctionManager()
447 | funcs = fm.getFunctions(True) # True means 'forward'
448 | for func in funcs:
449 | print("Function: {} @ 0x{}".format(func.getName(), func.getEntryPoint()))
450 | ```
451 |
452 |
453 | Output example
454 |
455 | ```
456 | Function: _alloca_probe @ 0x1400a8e00
457 | Function: FUN_1400a8e70 @ 0x1400a8e70
458 | ... snip ...
459 | ```
460 |
461 |
462 |
[⬆ Back to top](#table-of-contents)
463 |
464 | ### Get a function name by address
465 | This snippet will help you correlate addresses with associated function names.
466 |
467 | ```python
468 | # helper function to get a Ghidra Address type
469 | def getAddress(offset):
470 | return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)
471 |
472 | # get a FunctionManager reference for the current program
473 | functionManager = currentProgram.getFunctionManager()
474 |
475 | # getFunctionAt() only works with function entryPoint addresses!
476 | # returns `None` if address is not the address of the first
477 | # instruction in a defined function. Consider using
478 | # getFunctionContaining() method instead.
479 | addr = getAddress(0x00100690)
480 | funcName = functionManager.getFunctionAt(addr).getName()
481 | print(funcName)
482 |
483 | # check if a specific address resides in a function
484 | addr = getAddress(0x00100691)
485 | print(functionManager.isInFunction(addr))
486 |
487 | # get the function an address belongs to, returns `None` if the address
488 | # is not part of a defined function.
489 | addr = getAddress(0x00100691)
490 | print(functionManager.getFunctionContaining(addr))
491 | ```
492 |
493 |
494 | Output example
495 |
496 | ```
497 | main
498 | True
499 | main
500 | ```
501 |
502 |
503 |
[⬆ Back to top](#table-of-contents)
504 |
505 | ### Get a function address by name
506 | Have a name for a function but want the entry point address for it? This will help you do that. Just remember that two or more functions can share the same name (due to function overloading), so Ghidra will return an array (Python list) you have to consider iterating over.
507 |
508 | ```python
509 | # Note that multiple functions can share the same name, so Ghidra's API
510 | # returns a list of `Function` types. Just keep this in mind.
511 | name = "main"
512 | funcs = getGlobalFunctions(name)
513 | print("Found {} function(s) with the name '{}'".format(len(funcs), name))
514 | for func in funcs:
515 | print("{} is located at 0x{}".format(func.getName(), func.getEntryPoint()))
516 | ```
517 |
518 |
519 | Output example
520 |
521 | ```
522 | Found 1 function(s) with the name 'main'
523 | main is located at 0x00100690
524 | ```
525 |
526 |
527 |
[⬆ Back to top](#table-of-contents)
528 |
529 | ### Get cross references to a function
530 | Ghidra makes it easy to find all cross references to a function using `getReferencesTo`. To use this, you'll just need the function's entry address which can be acquired using the `getEntryPoint` method on a function object. Let's take a look at an example where we find all cross references to functions named "system".
531 |
532 | ```python
533 | fm = currentProgram.getFunctionManager()
534 | funcs = fm.getFunctions(True)
535 | for func in funcs:
536 | if func.getName() == "system":
537 | print("\nFound 'system' @ 0x{}".format(func.getEntryPoint()))
538 | entry_point = func.getEntryPoint()
539 | references = getReferencesTo(entry_point)
540 | for xref in references:
541 | print(xref)
542 | ```
543 |
544 |
545 | Output example
546 |
547 | ```
548 | Found 'system' @ 0x004024a0
549 | From: 0040bd34 To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
550 | From: 0040a66c To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
551 | From: 00411dd8 To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
552 | From: 004155d8 To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
553 | From: 00415b20 To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
554 | From: 00415d4c To: 004024a0 Type: UNCONDITIONAL_CALL Op: 0 DEFAULT
555 |
556 | Found 'system' @ 0x004300fc
557 | From: 0042f2ec To: 004300fc Type: DATA Op: 0 DEFAULT
558 | From: 004024a8 To: 004300fc Type: UNCONDITIONAL_CALL Op: 0 ANALYSIS
559 | ```
560 |
561 |
562 |
[⬆ Back to top](#table-of-contents)
563 |
564 |
565 | ### Analyzing function call arguments
566 | This snippet uses a `TARGET_ADDR` which should be the address of a call to return the call arguments at that address. Thanks to [gipi](https://github.com/gipi) for [suggesting](https://github.com/HackOvert/GhidraSnippets/issues/4) this much cleaner way to obtain function call arguments than previously listed!
567 |
568 | ```python
569 | from ghidra.app.decompiler import DecompileOptions
570 | from ghidra.app.decompiler import DecompInterface
571 | from ghidra.util.task import ConsoleTaskMonitor
572 |
573 | # # Disassembly shows: 00434f6c CALL FUN_00433ff0
574 | # # Decompiler shows: uVar1 = FUN_00433ff0(param_1,param_2,param_3);
575 | TARGET_ADDR = toAddr(0x00434f6c)
576 |
577 | options = DecompileOptions()
578 | monitor = ConsoleTaskMonitor()
579 | ifc = DecompInterface()
580 | ifc.setOptions(options)
581 | ifc.openProgram(currentProgram)
582 |
583 | func = getFunctionContaining(TARGET_ADDR)
584 | res = ifc.decompileFunction(func, 60, monitor)
585 | high_func = res.getHighFunction()
586 | pcodeops = high_func.getPcodeOps(TARGET_ADDR)
587 | op = pcodeops.next()
588 | print(op.getInputs())
589 | ```
590 |
591 |
592 | Output example
593 |
594 | ```
595 | array(ghidra.program.model.pcode.Varnode, [(ram, 0x433ff0, 8), (stack, 0x4, 4), (stack, 0x8, 4), (stack, 0xc, 4)])
596 | ```
597 |
598 |
599 |
[⬆ Back to top](#table-of-contents)
600 |
601 |
602 | ### Analyzing function call arguments at cross references
603 | In this snippet we locate cross references to a target function (`TARGET_FUNC`) and show how we can analyze the arguments passed to each call. This can be helpful in analyzing malware, or potentially vulnerable functions. For malware analysis, this may help "decrypt" strings, or in vulnerability research this may help locate functions that may be vulnerable if called with an incorrect value. The specific analysis performed on the arguments of a called target function are up to you. This snippet will allow you to add your own analysis as you see fit.
604 |
605 | ```python
606 | from ghidra.app.decompiler import DecompileOptions
607 | from ghidra.app.decompiler import DecompInterface
608 | from ghidra.util.task import ConsoleTaskMonitor
609 |
610 | TARGET_FUNC = "FUN_62de9540"
611 |
612 | # Step 1. Get functions that call the target function ('callers')
613 | target_addr = 0
614 | callers = []
615 | funcs = getGlobalFunctions(TARGET_FUNC)
616 | for func in funcs:
617 | if func.getName() == TARGET_FUNC:
618 | print("\nFound {} @ 0x{}".format(TARGET_FUNC, func.getEntryPoint()))
619 | target_addr = func.getEntryPoint()
620 | references = getReferencesTo(target_addr)
621 | for xref in references:
622 | call_addr = xref.getFromAddress()
623 | caller = getFunctionContaining(call_addr)
624 | callers.append(caller)
625 | break
626 |
627 | # deduplicate callers
628 | callers = list(set(callers))
629 |
630 | # Step 2. Decompile all callers and find PCODE CALL operations leading to `target_add`
631 | options = DecompileOptions()
632 | monitor = ConsoleTaskMonitor()
633 | ifc = DecompInterface()
634 | ifc.setOptions(options)
635 | ifc.openProgram(currentProgram)
636 |
637 | for caller in callers:
638 | res = ifc.decompileFunction(caller, 60, monitor)
639 | high_func = res.getHighFunction()
640 | lsm = high_func.getLocalSymbolMap()
641 | symbols = lsm.getSymbols()
642 | if high_func:
643 | opiter = high_func.getPcodeOps()
644 | while opiter.hasNext():
645 | op = opiter.next()
646 | mnemonic = str(op.getMnemonic())
647 | if mnemonic == "CALL":
648 | inputs = op.getInputs()
649 | addr = inputs[0].getAddress()
650 | args = inputs[1:] # List of VarnodeAST types
651 | if addr == target_addr:
652 | print("Call to {} at {} has {} arguments: {}".format(addr, op.getSeqnum().getTarget(), len(args), args))
653 | for arg in args:
654 | # Do stuff with each `arg` here...
655 | # Not sure what to do? Check out this great article by Lars A. Wallenborn for some ideas:
656 | # https://blag.nullteilerfrei.de/2020/02/02/defeating-sodinokibi-revil-string-obfuscation-in-ghidra/
657 | # Specifically, search for the function implementation of "traceVarnodeValue"
658 | pass
659 | ```
660 |
661 |
662 | Output example
663 |
664 | ```
665 | Found FUN_62de9540 @ 0x62de9540
666 | Call to 62de9540 at 62dede09 has 0 arguments: array(ghidra.program.model.pcode.Varnode)
667 | Call to 62de9540 at 62dee3c1 has 0 arguments: array(ghidra.program.model.pcode.Varnode)
668 | Call to 62de9540 at 62def2b2 has 0 arguments: array(ghidra.program.model.pcode.Varnode)
669 | Call to 62de9540 at 62dea894 has 3 arguments: array(ghidra.program.model.pcode.Varnode, [(stack, 0x4, 4), (const, 0x0, 4), (const, 0x0, 4)])
670 | Call to 62de9540 at 62ded7ec has 3 arguments: array(ghidra.program.model.pcode.Varnode, [(stack, 0x8, 4), (unique, 0x10000168, 4), (const, 0x0, 4)])
671 | ```
672 |
673 |
674 |
[⬆ Back to top](#table-of-contents)
675 |
676 |
677 |
678 | ### Rename functions based on strings
679 | In some cases strings will either hint at or give the name of a function when symbols aren't available. In these cases, we can rename functions based on these strings to help perform reverse engineering. This becomes a daunting task when there are hundreds or thousands of these cases, making the process of copy, rename, paste, a soul crushing task. The power of tools like Ghidra is that you can script this. Take for example this decompiled function found in a binary:
680 |
681 | ```
682 | undefined4 FUN_00056b58(void)
683 | {
684 | undefined4 in_r3;
685 |
686 | register_function(1, "core_Init_Database", FUN_00058294);
687 | register_function(1, "core_Clear_Database", FUN_00058374);
688 | register_function(1, "core_Auth_Database", FUN_00058584);
689 | register_function(1, "core_Add_User_Database", FUN_00058650);
690 |
691 | // ... hundreds more in this function and in others ...
692 |
693 | return in_r3;
694 | }
695 | ```
696 |
697 | In this function, we have calls to a `register_function` which correlates an event string with a handler function. We want to rename these functions so that `FUN_00058294` becomes `core_Init_Database` and so on. Below is code that performs this task for every `register_function` in the target binary.
698 |
699 | ```python
700 | from ghidra.app.decompiler import DecompileOptions
701 | from ghidra.app.decompiler import DecompInterface
702 | from ghidra.util.task import ConsoleTaskMonitor
703 |
704 | def getString(addr):
705 | mem = currentProgram.getMemory()
706 | core_name_str = ""
707 | while True:
708 | byte = mem.getByte(addr.add(len(core_name_str)))
709 | if byte == 0:
710 | return core_name_str
711 | core_name_str += chr(byte)
712 |
713 | # Get decompiler interface
714 | options = DecompileOptions()
715 | monitor = ConsoleTaskMonitor()
716 | ifc = DecompInterface()
717 | ifc.setOptions(options)
718 | ifc.openProgram(currentProgram)
719 |
720 | # Get reference to `register_function`
721 | fm = currentProgram.getFunctionManager()
722 | funcs = fm.getFunctions(True)
723 | register_function = None
724 | for func in funcs:
725 | if func.getName() == "register_function":
726 | register_function = func
727 | break
728 |
729 | # Get xrefs to "register_function"
730 | entry_point = register_function.getEntryPoint()
731 | xrefs = getReferencesTo(entry_point)
732 | callers = []
733 | for xref in xrefs:
734 | from_addr = xref.getFromAddress()
735 | caller = fm.getFunctionContaining(from_addr)
736 | if caller not in callers:
737 | callers.append(caller)
738 |
739 | # Process callers (functions calling `register_function`)
740 | for caller in callers:
741 | if not caller:
742 | continue
743 | res = ifc.decompileFunction(caller, 60, monitor)
744 | hf = res.getHighFunction()
745 | opiter = hf.getPcodeOps()
746 | while opiter.hasNext():
747 | op = opiter.next()
748 | mnemonic = op.getMnemonic()
749 | if mnemonic == "CALL":
750 | call_target = op.getInput(0)
751 | if call_target.getAddress() == entry_point:
752 | core_name = op.getInput(2)
753 | core_func = op.getInput(3)
754 | core_name_def = core_name.getDef()
755 | core_name_addr = toAddr(core_name_def.getInput(0).getOffset())
756 | core_string = getString(core_name_addr)
757 | core_func_addr = toAddr(core_func.getDef().getInput(1).getOffset())
758 | core_func_obj = fm.getFunctionAt(core_func_addr)
759 | core_func_obj.setName(core_string, ghidra.program.model.symbol.SourceType.DEFAULT)
760 | ```
761 |
762 |
763 | Output example
764 |
765 | ```
766 | undefined4 FUN_00056b58(void)
767 | {
768 | undefined4 in_r3;
769 |
770 | register_function(1, "core_Init_Database", core_Init_Database);
771 | register_function(1, "core_Clear_Database", core_Clear_Database);
772 | register_function(1, "core_Auth_Database", core_Auth_Database);
773 | register_function(1, "core_Add_User_Database", core_Add_User_Database);
774 |
775 | // ... hundreds more in this function and in others ...
776 |
777 | return in_r3;
778 | }
779 | ```
780 |
781 |
782 |
[⬆ Back to top](#table-of-contents)
783 |
784 |
785 |
786 |
787 | ## Working with Instructions
788 |
789 | ### Print all instructions in a select function
790 | Just like `objdump` or a `disas` command in GDB, Ghidra provides a way to dump instructions if you need. You might do this to generate input for another application, or for documenting issues found during analysis. Whatever you use case might be, you can easily acquire the address, opcodes, and instruction text for a target function, or specific addresses.
791 |
792 | ```python
793 | from binascii import hexlify
794 |
795 | listing = currentProgram.getListing()
796 | main_func = getGlobalFunctions("main")[0] # assume there's only 1 function named 'main'
797 | addrSet = main_func.getBody()
798 | codeUnits = listing.getCodeUnits(addrSet, True) # true means 'forward'
799 |
800 | for codeUnit in codeUnits:
801 | print("0x{} : {:16} {}".format(codeUnit.getAddress(), hexlify(codeUnit.getBytes()), codeUnit.toString()))
802 | ```
803 |
804 |
805 | Output example
806 |
807 | ```
808 | 0x00100690 : 55 PUSH RBP
809 | 0x00100691 : 4889e5 MOV RBP,RSP
810 | 0x00100694 : 4883ec30 SUB RSP,0x30
811 | 0x00100698 : 897ddc MOV dword ptr [RBP + -0x24],EDI
812 | 0x0010069b : 488975d0 MOV qword ptr [RBP + -0x30],RSI
813 | 0x0010069f : 488d051a010000 LEA RAX,[0x1007c0]
814 | 0x001006a6 : 488945f0 MOV qword ptr [RBP + -0x10],RAX
815 | 0x001006aa : c745e800000000 MOV dword ptr [RBP + -0x18],0x0
816 | 0x001006b1 : eb4d JMP 0x00100700
817 | 0x001006b3 : 488b45f0 MOV RAX,qword ptr [RBP + -0x10]
818 | 0x001006b7 : 4889c7 MOV RDI,RAX
819 | 0x001006ba : e83bffffff CALL 0x001005fa
820 | 0x001006bf : 8945ec MOV dword ptr [RBP + -0x14],EAX
821 | 0x001006c2 : 8b55ec MOV EDX,dword ptr [RBP + -0x14]
822 | 0x001006c5 : 488b45f0 MOV RAX,qword ptr [RBP + -0x10]
823 | 0x001006c9 : 488d3570092000 LEA RSI,[0x301040]
824 | 0x001006d0 : 4889c7 MOV RDI,RAX
825 | 0x001006d3 : e84fffffff CALL 0x00100627
826 | 0x001006d8 : 488945f8 MOV qword ptr [RBP + -0x8],RAX
827 | 0x001006dc : 8b45e8 MOV EAX,dword ptr [RBP + -0x18]
828 | 0x001006df : 8d5001 LEA EDX,[RAX + 0x1]
829 | 0x001006e2 : 8955e8 MOV dword ptr [RBP + -0x18],EDX
830 | 0x001006e5 : 488b55f8 MOV RDX,qword ptr [RBP + -0x8]
831 | 0x001006e9 : 89c6 MOV ESI,EAX
832 | 0x001006eb : 4889d7 MOV RDI,RDX
833 | 0x001006ee : e88fffffff CALL 0x00100682
834 | 0x001006f3 : 8b45ec MOV EAX,dword ptr [RBP + -0x14]
835 | 0x001006f6 : 4898 CDQE
836 | 0x001006f8 : 4883c001 ADD RAX,0x1
837 | 0x001006fc : 480145f0 ADD qword ptr [RBP + -0x10],RAX
838 | 0x00100700 : 488b45f0 MOV RAX,qword ptr [RBP + -0x10]
839 | 0x00100704 : 0fb600 MOVZX EAX,byte ptr [RAX]
840 | 0x00100707 : 84c0 TEST AL,AL
841 | 0x00100709 : 75a8 JNZ 0x001006b3
842 | 0x0010070b : b800000000 MOV EAX,0x0
843 | 0x00100710 : c9 LEAVE
844 | 0x00100711 : c3 RET
845 | ```
846 |
847 |
848 |
[⬆ Back to top](#table-of-contents)
849 |
850 |
851 | ### Find all calls and jumps to a register
852 | This snippet shows how to find all instructions in an x86 program that are calls or jumps to a regitser. This information can be useful when attempting to track down a crash by researching code flow in hard-to-debug targets. `getRegister` returns `None` when the specified index is not a regitser. We use `startswith('J')` to account for all jump variants. This is not architecture agnostic and a little goofy but it gets the job done.
853 |
854 | ```python
855 | listing = currentProgram.getListing()
856 | func = getFirstFunction()
857 | entryPoint = func.getEntryPoint()
858 | instructions = listing.getInstructions(entryPoint, True)
859 |
860 | for instruction in instructions:
861 | addr = instruction.getAddress()
862 | oper = instruction.getMnemonicString()
863 | if (oper.startswith('CALL') or oper.startswith('J') and instruction.getRegister(0)):
864 | print("0x{} : {}".format(addr, instruction))
865 | ```
866 |
867 |
868 | Output example
869 |
870 | ```
871 | 0x001004c8 : CALL RAX
872 | 0x00100544 : JMP RAX
873 | 0x00100595 : JMP RDX
874 | 0x00100771 : CALL R15
875 | ```
876 |
877 |
878 |
[⬆ Back to top](#table-of-contents)
879 |
880 |
881 | ### Count all mnemonics in a binary
882 | While recently preparing to teach some introductary x86, I wanted to know the most used mnemonics appearing in a given application to make sure I covered them. This is insanely easy to do in Binary Ninja, but a bit more involved in Ghidra. Essentially, we track mnemonics in a dictionary and increment the count as we process all instructions in a binary.
883 |
884 | This requires getting a `InstructionDB` and using the `getMnemonicString` method to determine the mnemonic of the native assembly instruction. At the end of this snippet, we copy/pasta code from StackOverflow to sort our collected data without really thinking about how it works and we call it a day. All joking aside, this is a pretty neat way to prioritize which instructions you should focus on learning if you're learning a new architecture and don't know where to begin.
885 |
886 | ```python
887 | instructions = {}
888 | af = currentProgram.getAddressFactory()
889 |
890 | func = getFirstFunction()
891 | addr = af.getAddress(str(func.getEntryPoint()))
892 | ins = getInstructionAt(addr)
893 |
894 | while ins is not None:
895 | mnemonic = ins.getMnemonicString()
896 | if mnemonic in instructions:
897 | instructions[mnemonic] += 1
898 | else:
899 | instructions[mnemonic] = 1
900 | ins = ins.getNext()
901 |
902 | ins_sorted = [ (i,mnem) for mnem,i in instructions.iteritems() ]
903 | ins_sorted.sort(reverse=True)
904 | for i,mnem in ins_sorted:
905 | print("{}: {}".format(i, mnem))
906 | ```
907 |
908 |
909 | Output example
910 |
911 | ```
912 | 178636: MOV
913 | 43836: CALL
914 | 35709: LEA
915 | 25479: CMP
916 | 19316: ADD
917 | 18943: JZ
918 | 15751: SUB
919 | 14636: TEST
920 | 14236: POP
921 | 13384: PUSH
922 | <...snip...>
923 | ```
924 |
925 |
926 |
[⬆ Back to top](#table-of-contents)
927 |
928 | ## Working with Variables
929 |
930 | ### Get a stack variable from a Varnode or VarnodeAST
931 | When working with refined PCode you'll almost exclusively be dealing with `VarnodeAST` or `PCodeOpAST` objects. Correlating these objects to stack variables is not something exposed by the Ghidra API (as far as I can tell in v9.2.2). This leads to a complex mess of taking a varnode and comparing it to the decompiler's stack variable symbols for a given function. It's not intutitive, and quite frankly, it's been the most confusing and complex thing I've done with the Ghidra API to date.
932 |
933 | This function works when you're passing a Varnode/AST of a simple variable, say something like this:
934 |
935 | ```c
936 | memset(local_88,0,0x10);
937 | ```
938 |
939 | If you want to know what that the first argument (`local_88`) is named "local_88" and get its size, you can use this function. If that first parameter include nested `PCodeOpAST`s however, say something like `(char *)local_a8`, this function will not work because the variable is "wrapped" inside of a `CAST` operation. In order to work, this needs to be paired with `get_vars_from_varnode` (ctrl-f to find its definition) to unwrap this onion, isolate the variable VarnodeAST, and then pass it here. There's an example of doing that in this document under the heading "Get stack variables from a PcodeOpAST".
940 |
941 | ```python
942 | def get_stack_var_from_varnode(func, varnode):
943 | if type(varnode) not in [Varnode, VarnodeAST]:
944 | raise Exception("Invalid value. Expected `Varnode` or `VarnodeAST`, got {}.".format(type(varnode)))
945 |
946 | bitness_masks = {
947 | '16': 0xffff,
948 | '32': 0xffffffff,
949 | '64': 0xffffffffffffffff,
950 | }
951 |
952 | try:
953 | addr_size = currentProgram.getMetadata()['Address Size']
954 | bitmask = bitness_masks[addr_size]
955 | except KeyError:
956 | raise Exception("Unsupported bitness: {}. Add a bit mask for this target.".format(addr_size))
957 |
958 | local_variables = func.getAllVariables()
959 | vndef = varnode.getDef()
960 | if vndef:
961 | vndef_inputs = vndef.getInputs()
962 | for defop_input in vndef_inputs:
963 | defop_input_offset = defop_input.getAddress().getOffset() & bitmask
964 | for lv in local_variables:
965 | unsigned_lv_offset = lv.getMinAddress().getUnsignedOffset() & bitmask
966 | if unsigned_lv_offset == defop_input_offset:
967 | return lv
968 |
969 | # If we get here, varnode is likely a "acStack##" variable.
970 | hf = get_high_function(func)
971 | lsm = hf.getLocalSymbolMap()
972 | for vndef_input in vndef_inputs:
973 | defop_input_offset = vndef_input.getAddress().getOffset() & bitmask
974 | for symbol in lsm.getSymbols():
975 | if symbol.isParameter():
976 | continue
977 | if defop_input_offset == symbol.getStorage().getFirstVarnode().getOffset() & bitmask:
978 | return symbol
979 |
980 | # unable to resolve stack variable for given varnode
981 | return None
982 | ```
983 |
984 |
985 | Output example
986 |
987 | ```
988 | # This output corresponds to this line of code:
989 | # memset(local_88,0,0x10);
990 | # In PCode this line looks like this:
991 | # --- CALL (ram, 0x102370, 8) , (unique, 0x620, 8) , (const, 0x0, 4) , (const, 0x10, 8)
992 | # The address of `memset` is: (ram, 0x102370, 8)
993 | # The `local_88` variable is: (unique, 0x620, 8)
994 | # We'll pass `(unique, 0x620, 8)` to `get_stack_var_from_varnode` to get the following output
995 | # which will be a list of `ghidra.program.database.function.LocalVariableDB`.
996 |
997 | [undefined2 local_88@Stack[-0x88]:2]
998 | ```
999 |
1000 |
1001 |
[⬆ Back to top](#table-of-contents)
1002 |
1003 |
1004 | ### Get stack variables from a PcodeOpAST
1005 | If you took a look at the code under the section "Get a stack variable from a Varnode or VarnodeAST", you'll probably be asking why that code works for something like: `memset(local_88,0,0x10);` but it fails for `strchr((char *)local_a8,10);`. The reason is that `local_88` is a `VarnodeAST` while `(char *)local_a8` is a `PcodeOpAST`. In other words, the `local_a8` varnode is "wrapped" inside of a `PcodeOpAST` and you can't associate it to any kind of meaningful value without first "unwrapping" it. Of course, a wrapped `VarnodeAST` could be wrapped in numerous `CAST` operations and `INT_ADD` operations, etc. So how do we handle this? Recursion. * shudder *.
1006 |
1007 | Fair warning, recursion is my computer science nemisis. If you look at this code and think "this is odd" - you're probably right!
1008 |
1009 | That being said, let's slap something like `(char *)local_a8` into this function and see if we can get the associated variable name(s) out of it. It's perfectly fine to pass a `PcodeOpAST` with multiple variables (e.g. `(long)iVar2 + local_a0`) into this function. It will just return a list of all variables contained in that `VarnodeAST`.
1010 |
1011 | ```python
1012 | def get_vars_from_varnode(func, node, variables=None):
1013 | if type(node) not in [PcodeOpAST, VarnodeAST]:
1014 | raise Exception("Invalid value passed. Got {}.".format(type(node)))
1015 |
1016 | # create `variables` list on first call. Do not make `variables` default to [].
1017 | if variables == None:
1018 | variables = []
1019 |
1020 | # We must use `getDef()` on VarnodeASTs
1021 | if type(node) == VarnodeAST:
1022 | # For `get_stack_var_from_varnode` see:
1023 | # https://github.com/HackOvert/GhidraSnippets
1024 | # Ctrl-F for "get_stack_var_from_varnode"
1025 | var = get_stack_var_from_varnode(func, node)
1026 | if var and type(var) != HighSymbol:
1027 | variables.append(var.getName())
1028 | node = node.getDef()
1029 | if node:
1030 | variables = get_vars_from_varnode(func, node, variables)
1031 |
1032 | # We must call `getInputs()` on PcodeOpASTs
1033 | elif type(node) == PcodeOpAST:
1034 | nodes = list(node.getInputs())
1035 | for node in nodes:
1036 | if type(node.getHigh()) == HighLocal:
1037 | variables.append(node.getHigh().getName())
1038 | else:
1039 | variables = get_vars_from_varnode(func, node, variables)
1040 |
1041 | return variables
1042 | ```
1043 |
1044 |
1045 | Output example
1046 |
1047 | ```
1048 | Python output goes here...
1049 | ```
1050 |
1051 |
1052 |
[⬆ Back to top](#table-of-contents)
1053 |
1054 | ## Working with Basic Blocks
1055 | Basic Blocks are collections of continuous non-branching instructions within Functions. They are joined by conditional and non-conditional branches, revealing valuable information about a program and function's code flow. This section deals with examples working with Basic Block models.
1056 |
1057 | ### Print details about basic blocks in a select function
1058 | ```python
1059 | from ghidra.program.model.block import BasicBlockModel
1060 | from ghidra.util.task import ConsoleTaskMonitor
1061 |
1062 | funcName = 'main'
1063 | blockModel = BasicBlockModel(currentProgram)
1064 | monitor = ConsoleTaskMonitor()
1065 | func = getGlobalFunctions(funcName)[0]
1066 |
1067 | print("Basic block details for function '{}':".format(funcName))
1068 | blocks = blockModel.getCodeBlocksContaining(func.getBody(), monitor)
1069 |
1070 | # print first block
1071 | print("\t[*] {} ".format(func.getEntryPoint()))
1072 |
1073 | # print any remaining blocks
1074 | while(blocks.hasNext()):
1075 | bb = blocks.next()
1076 | dest = bb.getDestinations(monitor)
1077 | while(dest.hasNext()):
1078 | dbb = dest.next()
1079 | # For some odd reason `getCodeBlocksContaining()` and `.next()`
1080 | # return the root basic block after CALL instructions (x86). To filter
1081 | # these out, we use `getFunctionAt()` which returns `None` if the address
1082 | # is not the entry point of a function. See:
1083 | # https://github.com/NationalSecurityAgency/ghidra/issues/855
1084 | if not getFunctionAt(dbb.getDestinationAddress()):
1085 | print("\t[*] {} ".format(dbb))
1086 | ```
1087 |
1088 |
1089 | Output example
1090 |
1091 | ```
1092 | Basic block details for function 'main':
1093 | [*] 00100690
1094 | [*] 001006b1 -> 00100700
1095 | [*] 001006fc -> 00100700
1096 | [*] 00100709 -> 001006b3
1097 | [*] 00100709 -> 0010070b
1098 | ```
1099 |
1100 |
1101 |
[⬆ Back to top](#table-of-contents)
1102 |
1103 |
1104 | ## Working with the Decompiler
1105 |
1106 | ### Print decompiled code for a specific function
1107 | Ghidra's decompiler is an exceptional resource. In certain cases you might want to extract the decompiler output for a list of functions. Here's an easy way to gather that information. Just add your own file I/O code to dump everything to individual files for analysis.
1108 |
1109 | ```python
1110 | from ghidra.app.decompiler import DecompInterface
1111 | from ghidra.util.task import ConsoleTaskMonitor
1112 |
1113 | program = getCurrentProgram()
1114 | ifc = DecompInterface()
1115 | ifc.openProgram(program)
1116 |
1117 | # here we assume there is only one function named `main`
1118 | function = getGlobalFunctions('main')[0]
1119 |
1120 | # decompile the function and print the pseudo C
1121 | results = ifc.decompileFunction(function, 0, ConsoleTaskMonitor())
1122 | print(results.getDecompiledFunction().getC())
1123 | ```
1124 |
1125 |
1126 | Output example
1127 |
1128 | ```
1129 | undefined8 main(void)
1130 |
1131 | {
1132 | uint uVar1;
1133 | undefined8 uVar2;
1134 | uint local_20;
1135 | undefined1 *local_18;
1136 |
1137 | local_18 = data;
1138 | local_20 = 0;
1139 | while (*local_18 != '\0') {
1140 | uVar1 = length(local_18);
1141 | uVar2 = deobfuscate(local_18,buffer,(ulong)uVar1);
1142 | use_string(uVar2,(ulong)local_20,uVar2);
1143 | local_18 = local_18 + (long)(int)uVar1 + 1;
1144 | local_20 = local_20 + 1;
1145 | }
1146 | return 0;
1147 | }
1148 |
1149 | ```
1150 |
1151 |
1152 |
[⬆ Back to top](#table-of-contents)
1153 |
1154 |
1155 | ### Getting variable information from the decompiler
1156 | Ghidra's decompiler performs a lot of analysis in order to recover variable information. If you're interested in getting this information you'll need to use the decompiler interface to get a high function and its symbols. Once you have this data, you can enumerate the symbols and retrieve information about variables in the target function. Let's take a look at an example decompiled function:
1157 |
1158 | ```c++
1159 | undefined8 func(int param_1,int param_2)
1160 | {
1161 | long in_FS_OFFSET;
1162 | uint auStack88 [8];
1163 | undefined4 auStack56 [10];
1164 | long local_10;
1165 |
1166 | local_10 = *(long *)(in_FS_OFFSET + 0x28);
1167 | auStack56[param_1] = 1;
1168 | printf("%d\n",(ulong)auStack88[param_2]);
1169 | if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
1170 | __stack_chk_fail();
1171 | }
1172 | return 0;
1173 | }
1174 | ```
1175 |
1176 | The decompiled function above shows two stack variables that seem interesting to us; auStack88 and auStack56. Let's get that information programmatically.
1177 |
1178 | ```python
1179 | from ghidra.app.decompiler import DecompileOptions
1180 | from ghidra.app.decompiler import DecompInterface
1181 | from ghidra.util.task import ConsoleTaskMonitor
1182 |
1183 | func_name = "func"
1184 | func = getGlobalFunctions(func_name)[0]
1185 | options = DecompileOptions()
1186 | monitor = ConsoleTaskMonitor()
1187 | ifc = DecompInterface()
1188 | ifc.setOptions(options)
1189 | ifc.openProgram(func.getProgram())
1190 | res = ifc.decompileFunction(func, 60, monitor)
1191 | high_func = res.getHighFunction()
1192 | lsm = high_func.getLocalSymbolMap()
1193 | symbols = lsm.getSymbols()
1194 |
1195 | for i, symbol in enumerate(symbols):
1196 | print("\nSymbol {}:".format(i+1))
1197 | print(" name: {}".format(symbol.name))
1198 | print(" dataType: {}".format(symbol.dataType))
1199 | print(" getPCAddress: 0x{}".format(symbol.getPCAddress()))
1200 | print(" size: {}".format(symbol.size))
1201 | print(" storage: {}".format(symbol.storage))
1202 | print(" parameter: {}".format(symbol.parameter))
1203 | print(" readOnly: {}".format(symbol.readOnly))
1204 | print(" typeLocked: {}".format(symbol.typeLocked))
1205 | print(" nameLocked: {}".format(symbol.nameLocked))
1206 | print(" slot: {}".format(symbol.slot))
1207 | ```
1208 |
1209 |
1210 | Output example
1211 |
1212 | ```
1213 | Symbol 1:
1214 | name: auStack56
1215 | dataType: undefined4[10]
1216 | getPCAddress: 0xNone
1217 | size: 40
1218 | parameter: 0
1219 | readOnly: 0
1220 | typeLocked: 0
1221 | nameLocked: 0
1222 | slot: -1
1223 | storage: Stack[-0x38]:40
1224 |
1225 | Symbol 2:
1226 | name: auStack88
1227 | dataType: uint[8]
1228 | getPCAddress: 0xNone
1229 | size: 32
1230 | parameter: 0
1231 | readOnly: 0
1232 | typeLocked: 0
1233 | nameLocked: 0
1234 | slot: -1
1235 | storage: Stack[-0x58]:32
1236 |
1237 | Symbol 3:
1238 | name: in_FS_OFFSET
1239 | dataType: long
1240 | getPCAddress: 0x001006a9
1241 | size: 8
1242 | parameter: 0
1243 | readOnly: 0
1244 | typeLocked: 0
1245 | nameLocked: 0
1246 | slot: -1
1247 | storage: FS_OFFSET:8
1248 |
1249 | Symbol 4:
1250 | name: local_10
1251 | dataType: long
1252 | getPCAddress: 0xNone
1253 | size: 8
1254 | parameter: 0
1255 | readOnly: 0
1256 | typeLocked: 0
1257 | nameLocked: 0
1258 | slot: -1
1259 | storage: Stack[-0x10]:8
1260 |
1261 | Symbol 5:
1262 | name: param_1
1263 | dataType: int
1264 | getPCAddress: 0x001006a9
1265 | size: 4
1266 | parameter: 1
1267 | readOnly: 0
1268 | typeLocked: 0
1269 | nameLocked: 0
1270 | slot: 0
1271 | storage: EDI:4
1272 |
1273 | Symbol 6:
1274 | name: param_2
1275 | dataType: int
1276 | getPCAddress: 0x001006a9
1277 | size: 4
1278 | parameter: 1
1279 | readOnly: 0
1280 | typeLocked: 0
1281 | nameLocked: 0
1282 | slot: 1
1283 | storage: ESI:4
1284 | ```
1285 |
1286 |
1287 |
[⬆ Back to top](#table-of-contents)
1288 |
1289 |
1290 | ## Working with Comments
1291 |
1292 | ### Get all Automatic comments for a function
1293 |
1294 | Ghidra adds "automatic comments" (light gray in color) in the EOL field. Here's how you can access those comments.
1295 |
1296 | ```python
1297 | from ghidra.app.util import DisplayableEol
1298 |
1299 | listing = currentProgram.getListing()
1300 | func = getGlobalFunctions("frame_dummy")[0]
1301 | addrSet = func.getBody()
1302 | codeUnits = listing.getCodeUnits(addrSet, True)
1303 |
1304 | for codeUnit in codeUnits:
1305 | deol = DisplayableEol(codeUnit, True, True, True, True, 5, True)
1306 | if deol.hasAutomatic():
1307 | ac = deol.getAutomaticComment()
1308 | print(type(ac))
1309 | print(ac)
1310 | print(ac[0])
1311 | ```
1312 |
1313 |
1314 | Output example
1315 |
1316 | ```
1317 |
1318 | array(java.lang.String, [u'undefined register_tm_clones()'])
1319 | undefined register_tm_clones()
1320 | ... snip ...
1321 | ```
1322 |
1323 |
1324 |
[⬆ Back to top](#table-of-contents)
1325 |
1326 |
1327 | ### Get specific comment types for all functions
1328 |
1329 | Ghidra supports 5 unique comment types users can add to their projects. This snippet shows you show to print all comments by type. This snippet is a slightly modified version of what user `u/securisec` posted in the Ghidra subreddit, `r/ghidra`. Thanks!
1330 |
1331 | ```python
1332 | fm = currentProgram.getFunctionManager()
1333 | listing = currentProgram.getListing()
1334 | funcs = fm.getFunctions(True) # True means iterate forward
1335 |
1336 | comment_types = {
1337 | 0: 'EOL',
1338 | 1: 'PRE',
1339 | 2: 'POST',
1340 | 3: 'PLATE',
1341 | 4: 'REPEATABLE',
1342 | }
1343 |
1344 | for func in funcs:
1345 | addrSet = func.getBody()
1346 | codeUnits = listing.getCodeUnits(addrSet, True)
1347 | for codeUnit in codeUnits:
1348 | for i, comment_type in comment_types.items():
1349 | comment = codeUnit.getComment(i)
1350 | if comment is not None:
1351 | comment = comment.decode("utf-8")
1352 | print("[{} : {}] {}: {}".format(func.name, codeUnit.address, comment_type, comment))
1353 | ```
1354 |
1355 |
1356 | Output example
1357 |
1358 | ```
1359 | [TclCompileSwitchCmd : 00058f3c] EOL: EOL COMMENT 000
1360 | [TclCompileSwitchCmd : 00058f3c] PRE: PRE-COMMENT 111
1361 | [TclCompileSwitchCmd : 00058f3c] POST: POST COMMENT 222
1362 | [TclCompileSwitchCmd : 00058f3c] PLATE: PLATE COMMENT 333
1363 | [TclCompileSwitchCmd : 00058f3c] REPEATABLE: REPEATABLE COMMENT 444
1364 | [getpagesize : 00110000] EOL: Grocery list: milk, cookies, santa trap
1365 | [getpagesize : 00110000] PRE: getpagesize@@GLIBC_2.0
1366 | ```
1367 |
1368 |
1369 |
[⬆ Back to top](#table-of-contents)
1370 |
1371 |
1372 | ## Working with PCode
1373 |
1374 | ### Emulating a function
1375 | Instruction emulation is an extremely powerful technique to asist in static code analysis. Rarely however, do we have the full memory context in which dynamic code executes. So while emulation can bring an element of 'dynamic' analysis to static RE, it's typically plagued with problems of unknown memory state. For simple code this might be no problem. For object oriented code this can be a major difficulty. Either way, some element of emulation can help tremendously in speeding up analysis. Ghidra uses its internal intermediate representation (PCode) to define what instructions do. Emulation is the process of tracking these changes in a cumulative state. Unfortunely Ghidra doesn't provide fancy GUI controls around the emulator (yet), but it's fully scriptable. Ghidra v9.1 added emprovements to the `EmulatorHelper` class, which is really quite amazing. Here's a simple example of what you can do with it.
1376 |
1377 | ```python
1378 | from ghidra.app.emulator import EmulatorHelper
1379 | from ghidra.program.model.symbol import SymbolUtilities
1380 |
1381 | # == Helper functions ======================================================
1382 | def getAddress(offset):
1383 | return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)
1384 |
1385 | def getSymbolAddress(symbolName):
1386 | symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName, None)
1387 | if (symbol != None):
1388 | return symbol.getAddress()
1389 | else:
1390 | raise("Failed to locate label: {}".format(symbolName))
1391 |
1392 | def getProgramRegisterList(currentProgram):
1393 | pc = currentProgram.getProgramContext()
1394 | return pc.registers
1395 |
1396 | # == Main function =========================================================
1397 | def main():
1398 | CONTROLLED_RETURN_OFFSET = 0
1399 |
1400 | # Identify function to be emulated
1401 | mainFunctionEntry = getSymbolAddress("main")
1402 |
1403 | # Establish emulation helper, please check out the API docs
1404 | # for `EmulatorHelper` - there's a lot of helpful things
1405 | # to help make architecture agnostic emulator tools.
1406 | emuHelper = EmulatorHelper(currentProgram)
1407 |
1408 | # Set controlled return location so we can identify return from emulated function
1409 | controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET)
1410 |
1411 | # Set initial RIP
1412 | mainFunctionEntryLong = int("0x{}".format(mainFunctionEntry), 16)
1413 | emuHelper.writeRegister(emuHelper.getPCRegister(), mainFunctionEntryLong)
1414 |
1415 | # For x86_64 `registers` contains 872 registers! You probably don't
1416 | # want to print all of these. Just be aware, and print what you need.
1417 | # To see all supported registers. just print `registers`.
1418 | # We won't use this, it's just here to show you how to query
1419 | # valid registers for your target architecture.
1420 | registers = getProgramRegisterList(currentProgram)
1421 |
1422 | # Here's a list of all the registers we want printed after each
1423 | # instruction. Modify this as you see fit, based on your architecture.
1424 | reg_filter = [
1425 | "RIP", "RAX", "RBX", "RCX", "RDX", "RSI", "RDI",
1426 | "RSP", "RBP", "rflags"
1427 | ]
1428 |
1429 | # Setup your desired starting state. By default, all registers
1430 | # and memory will be 0. This may or may not be acceptable for
1431 | # you. So please be aware.
1432 | emuHelper.writeRegister("RAX", 0x20)
1433 | emuHelper.writeRegister("RSP", 0x000000002FFF0000)
1434 | emuHelper.writeRegister("RBP", 0x000000002FFF0000)
1435 |
1436 | # There are a couple of ways to write memory, use `writeMemoryValue` if you want
1437 | # to set a small typed value (e.g. uint64). Use `writeMemory` if you're mapping in
1438 | # a lot of memory (e.g. from a debugger memory dump). Note that each of these
1439 | # methods write with different endianess, see the example output.
1440 | emuHelper.writeMemoryValue(getAddress(0x000000000008C000), 4, 0x99AABBCC) # writes big endian
1441 | emuHelper.writeMemory(getAddress(0x00000000000CF000), b'\x99\xAA\xBB\xCC') # writes little endian
1442 |
1443 | # You can verify writes worked, or just read memory at select points
1444 | # during emulation. Here's a couple of examples:
1445 | mem1 = emuHelper.readMemory(getAddress(0x000000000008C000), 4)
1446 | mem2 = emuHelper.readMemory(getAddress(0x00000000000CF000), 4)
1447 | print("Memory at 0x000000000008C000: {}".format(mem1))
1448 | print("Memory at 0x00000000000CF000: {}".format(mem2))
1449 |
1450 | print("Emulation starting at 0x{}".format(mainFunctionEntry))
1451 | while monitor.isCancelled() is False:
1452 |
1453 | # Check the current address in the program counter, if it's
1454 | # zero (our `CONTROLLED_RETURN_OFFSET` value) stop emulation.
1455 | # Set this to whatever end target you want.
1456 | executionAddress = emuHelper.getExecutionAddress()
1457 | if (executionAddress == controlledReturnAddr):
1458 | print("Emulation complete.")
1459 | return
1460 |
1461 | # Print current instruction and the registers we care about
1462 | print("Address: 0x{} ({})".format(executionAddress, getInstructionAt(executionAddress)))
1463 | for reg in reg_filter:
1464 | reg_value = emuHelper.readRegister(reg)
1465 | print(" {} = {:#018x}".format(reg, reg_value))
1466 |
1467 | # single step emulation
1468 | success = emuHelper.step(monitor)
1469 | if (success == False):
1470 | lastError = emuHelper.getLastError()
1471 | printerr("Emulation Error: '{}'".format(lastError))
1472 | return
1473 |
1474 | # Cleanup resources and release hold on currentProgram
1475 | emuHelper.dispose()
1476 |
1477 | # == Invoke main ===========================================================
1478 | main()
1479 | ```
1480 |
1481 |
1482 | Output example
1483 |
1484 | ```
1485 | Memory at 0x000000000008C000: array('b', [-52, -69, -86, -103])
1486 | Memory at 0x00000000000CF000: array('b', [-103, -86, -69, -52])
1487 | Emulation starting at 0x00100690
1488 | Address: 0x00100690 (PUSH RBP)
1489 | RIP = 0x0000000000100690
1490 | RAX = 0x0000000000000020
1491 | RBX = 0x0000000000000000
1492 | RCX = 0x0000000000000000
1493 | RDX = 0x0000000000000000
1494 | RSI = 0x0000000000000000
1495 | RDI = 0x0000000000000000
1496 | RSP = 0x000000002fff0000
1497 | RBP = 0x000000002fff0000
1498 | rflags = 0x0000000000000000
1499 | Address: 0x00100691 (MOV RBP,RSP)
1500 | RIP = 0x0000000000100691
1501 | RAX = 0x0000000000000020
1502 | RBX = 0x0000000000000000
1503 | RCX = 0x0000000000000000
1504 | RDX = 0x0000000000000000
1505 | RSI = 0x0000000000000000
1506 | RDI = 0x0000000000000000
1507 | RSP = 0x000000002ffefff8
1508 | RBP = 0x000000002fff0000
1509 | rflags = 0x0000000000000000
1510 | Address: 0x00100694 (SUB RSP,0x30)
1511 | RIP = 0x0000000000100694
1512 | RAX = 0x0000000000000020
1513 | RBX = 0x0000000000000000
1514 | RCX = 0x0000000000000000
1515 | RDX = 0x0000000000000000
1516 | RSI = 0x0000000000000000
1517 | RDI = 0x0000000000000000
1518 | RSP = 0x000000002ffefff8
1519 | RBP = 0x000000002ffefff8
1520 | rflags = 0x0000000000000000
1521 | Address: 0x00100698 (MOV dword ptr [RBP + -0x24],EDI)
1522 | RIP = 0x0000000000100698
1523 | RAX = 0x0000000000000020
1524 | RBX = 0x0000000000000000
1525 | RCX = 0x0000000000000000
1526 | RDX = 0x0000000000000000
1527 | RSI = 0x0000000000000000
1528 | RDI = 0x0000000000000000
1529 | RSP = 0x000000002ffeffc8
1530 | RBP = 0x000000002ffefff8
1531 | rflags = 0x0000000000000000
1532 | ... snip ...
1533 | ```
1534 |
1535 |
1536 |
[⬆ Back to top](#table-of-contents)
1537 |
1538 |
1539 | ### Dumping Raw PCode
1540 | PCode exists in two primary forms you as a user should consider, "raw" and "refined". In documentation both forms are simply referred to as "PCode" making it confusing to talk about - so I distinguish between the forms using raw and refined. Just know theses are not universally accepted terms.
1541 |
1542 | So raw PCode is the first pass, and the form that's displayed in the "Listing" pane inside the Ghidra UI. It's extremely verbose and explicit. This is the form you want to use when emulating, if you're writing a symbolic executor, or anything of the sort. If you want details from the decompiler passes, you want to analyze refined PCode, not this stuff! So what does it look like and how do you access it? Let's take a look.
1543 |
1544 | ```python
1545 | def dump_raw_pcode(func):
1546 | func_body = func.getBody()
1547 | listing = currentProgram.getListing()
1548 | opiter = listing.getInstructions(func_body, True)
1549 | while opiter.hasNext():
1550 | op = opiter.next()
1551 | raw_pcode = op.getPcode()
1552 | print("{}".format(op))
1553 | for entry in raw_pcode:
1554 | print(" {}".format(entry))
1555 |
1556 | func = getGlobalFunctions("main")[0] # assumes only one function named `main`
1557 | dump_raw_pcode(func) # dump raw pcode as strings
1558 | ```
1559 |
1560 |
1561 | Output example
1562 |
1563 | Note that this looks different from the raw pcode you'll see in the UI (if you enable the PCode field) but it is exactly the same. It's just not formatted to print the same. Please run this against your own target function and you'll see what I mean.
1564 |
1565 | ```
1566 | PUSH RBP
1567 | (unique, 0x2510, 8) COPY (register, 0x28, 8)
1568 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1569 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (unique, 0x2510, 8)
1570 | MOV RBP,RSP
1571 | (register, 0x28, 8) COPY (register, 0x20, 8)
1572 | SUB RSP,0x50
1573 | (register, 0x200, 1) INT_LESS (register, 0x20, 8) , (const, 0x50, 8)
1574 | (register, 0x20b, 1) INT_SBORROW (register, 0x20, 8) , (const, 0x50, 8)
1575 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x50, 8)
1576 | (register, 0x207, 1) INT_SLESS (register, 0x20, 8) , (const, 0x0, 8)
1577 | (register, 0x206, 1) INT_EQUAL (register, 0x20, 8) , (const, 0x0, 8)
1578 | MOV dword ptr [RBP + -0x44],EDI
1579 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffbc, 8)
1580 | (unique, 0x1fd0, 4) COPY (register, 0x38, 4)
1581 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1fd0, 4)
1582 | MOV qword ptr [RBP + -0x50],RSI
1583 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffb0, 8)
1584 | (unique, 0x1ff0, 8) COPY (register, 0x30, 8)
1585 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1ff0, 8)
1586 | MOV RAX,qword ptr FS:[0x28]
1587 | (unique, 0x9e0, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8)
1588 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8)
1589 | (register, 0x0, 8) COPY (unique, 0x1ff0, 8)
1590 | MOV qword ptr [RBP + -0x8],RAX
1591 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xfffffffffffffff8, 8)
1592 | (unique, 0x1ff0, 8) COPY (register, 0x0, 8)
1593 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1ff0, 8)
1594 | XOR EAX,EAX
1595 | (register, 0x200, 1) COPY (const, 0x0, 1)
1596 | (register, 0x20b, 1) COPY (const, 0x0, 1)
1597 | (register, 0x0, 4) INT_XOR (register, 0x0, 4) , (register, 0x0, 4)
1598 | (register, 0x0, 8) INT_ZEXT (register, 0x0, 4)
1599 | (register, 0x207, 1) INT_SLESS (register, 0x0, 4) , (const, 0x0, 4)
1600 | (register, 0x206, 1) INT_EQUAL (register, 0x0, 4) , (const, 0x0, 4)
1601 | CMP dword ptr [RBP + -0x44],0x1
1602 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffbc, 8)
1603 | (unique, 0x1fe0, 4) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1604 | (register, 0x200, 1) INT_LESS (unique, 0x1fe0, 4) , (const, 0x1, 4)
1605 | (unique, 0x1fe0, 4) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1606 | (register, 0x20b, 1) INT_SBORROW (unique, 0x1fe0, 4) , (const, 0x1, 4)
1607 | (unique, 0x1fe0, 4) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1608 | (unique, 0x5950, 4) INT_SUB (unique, 0x1fe0, 4) , (const, 0x1, 4)
1609 | (register, 0x207, 1) INT_SLESS (unique, 0x5950, 4) , (const, 0x0, 4)
1610 | (register, 0x206, 1) INT_EQUAL (unique, 0x5950, 4) , (const, 0x0, 4)
1611 | JG 0x00100743
1612 | (unique, 0x2220, 1) BOOL_NEGATE (register, 0x206, 1)
1613 | (unique, 0x2230, 1) INT_EQUAL (register, 0x20b, 1) , (register, 0x207, 1)
1614 | (unique, 0x2250, 1) BOOL_AND (unique, 0x2220, 1) , (unique, 0x2230, 1)
1615 | --- CBRANCH (ram, 0x100743, 8) , (unique, 0x2250, 1)
1616 | MOV RAX,qword ptr [RBP + -0x50]
1617 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffb0, 8)
1618 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1619 | (register, 0x0, 8) COPY (unique, 0x1ff0, 8)
1620 | MOV RAX,qword ptr [RAX]
1621 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (register, 0x0, 8)
1622 | (register, 0x0, 8) COPY (unique, 0x1ff0, 8)
1623 | MOV RSI,RAX
1624 | (register, 0x30, 8) COPY (register, 0x0, 8)
1625 | LEA RDI,[0x1008a4]
1626 | (register, 0x38, 8) COPY (const, 0x1008a4, 8)
1627 | MOV EAX,0x0
1628 | (register, 0x0, 8) COPY (const, 0x0, 8)
1629 | CALL 0x001005d0
1630 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1631 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x100739, 8)
1632 | --- CALL (ram, 0x1005d0, 8)
1633 | MOV EAX,0x1
1634 | (register, 0x0, 8) COPY (const, 0x1, 8)
1635 | JMP 0x001007ff
1636 | --- BRANCH (ram, 0x1007ff, 8)
1637 | MOV RAX,0x4242424241414141
1638 | (register, 0x0, 8) COPY (const, 0x4242424241414141, 8)
1639 | MOV qword ptr [RBP + -0x1c],RAX
1640 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffe4, 8)
1641 | (unique, 0x1ff0, 8) COPY (register, 0x0, 8)
1642 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1ff0, 8)
1643 | MOV word ptr [RBP + -0x14],0x43
1644 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffec, 8)
1645 | (unique, 0x1fc0, 2) COPY (const, 0x43, 2)
1646 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1fc0, 2)
1647 | MOV qword ptr [RBP + -0x2c],0x0
1648 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffd4, 8)
1649 | (unique, 0x2000, 8) COPY (const, 0x0, 8)
1650 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x2000, 8)
1651 | LEA RDX,[RBP + -0x1c]
1652 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffe4, 8)
1653 | (register, 0x10, 8) COPY (unique, 0x620, 8)
1654 | LEA RAX,[RBP + -0x2c]
1655 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffd4, 8)
1656 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1657 | MOV RSI,RDX
1658 | (register, 0x30, 8) COPY (register, 0x10, 8)
1659 | MOV RDI,RAX
1660 | (register, 0x38, 8) COPY (register, 0x0, 8)
1661 | CALL 0x001005b0
1662 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1663 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x100772, 8)
1664 | --- CALL (ram, 0x1005b0, 8)
1665 | LEA RAX,[RBP + -0x2c]
1666 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffd4, 8)
1667 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1668 | MOV RSI,RAX
1669 | (register, 0x30, 8) COPY (register, 0x0, 8)
1670 | LEA RDI,[0x1008b6]
1671 | (register, 0x38, 8) COPY (const, 0x1008b6, 8)
1672 | MOV EAX,0x0
1673 | (register, 0x0, 8) COPY (const, 0x0, 8)
1674 | CALL 0x001005d0
1675 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1676 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x10078a, 8)
1677 | --- CALL (ram, 0x1005d0, 8)
1678 | MOV qword ptr [RBP + -0x24],0x0
1679 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffdc, 8)
1680 | (unique, 0x2000, 8) COPY (const, 0x0, 8)
1681 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x2000, 8)
1682 | MOV RAX,qword ptr [RBP + -0x50]
1683 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffb0, 8)
1684 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1685 | (register, 0x0, 8) COPY (unique, 0x1ff0, 8)
1686 | ADD RAX,0x8
1687 | (register, 0x200, 1) INT_CARRY (register, 0x0, 8) , (const, 0x8, 8)
1688 | (register, 0x20b, 1) INT_SCARRY (register, 0x0, 8) , (const, 0x8, 8)
1689 | (register, 0x0, 8) INT_ADD (register, 0x0, 8) , (const, 0x8, 8)
1690 | (register, 0x207, 1) INT_SLESS (register, 0x0, 8) , (const, 0x0, 8)
1691 | (register, 0x206, 1) INT_EQUAL (register, 0x0, 8) , (const, 0x0, 8)
1692 | MOV RDX,qword ptr [RAX]
1693 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (register, 0x0, 8)
1694 | (register, 0x10, 8) COPY (unique, 0x1ff0, 8)
1695 | LEA RAX,[RBP + -0x24]
1696 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffdc, 8)
1697 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1698 | MOV RSI,RDX
1699 | (register, 0x30, 8) COPY (register, 0x10, 8)
1700 | MOV RDI,RAX
1701 | (register, 0x38, 8) COPY (register, 0x0, 8)
1702 | CALL 0x001005b0
1703 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1704 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x1007ac, 8)
1705 | --- CALL (ram, 0x1005b0, 8)
1706 | LEA RAX,[RBP + -0x24]
1707 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffdc, 8)
1708 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1709 | MOV RSI,RAX
1710 | (register, 0x30, 8) COPY (register, 0x0, 8)
1711 | LEA RDI,[0x1008c2]
1712 | (register, 0x38, 8) COPY (const, 0x1008c2, 8)
1713 | MOV EAX,0x0
1714 | (register, 0x0, 8) COPY (const, 0x0, 8)
1715 | CALL 0x001005d0
1716 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1717 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x1007c4, 8)
1718 | --- CALL (ram, 0x1005d0, 8)
1719 | MOV dword ptr [RBP + -0x31],0x39393939
1720 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffcf, 8)
1721 | (unique, 0x1fe0, 4) COPY (const, 0x39393939, 4)
1722 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1fe0, 4)
1723 | MOV byte ptr [RBP + -0x2d],0x0
1724 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffd3, 8)
1725 | (unique, 0x1fa0, 1) COPY (const, 0x0, 1)
1726 | --- STORE (const, 0x1b1, 4) , (unique, 0x620, 8) , (unique, 0x1fa0, 1)
1727 | LEA RDX,[RBP + -0x31]
1728 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffcf, 8)
1729 | (register, 0x10, 8) COPY (unique, 0x620, 8)
1730 | LEA RAX,[RBP + -0x12]
1731 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffee, 8)
1732 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1733 | MOV RSI,RDX
1734 | (register, 0x30, 8) COPY (register, 0x10, 8)
1735 | MOV RDI,RAX
1736 | (register, 0x38, 8) COPY (register, 0x0, 8)
1737 | CALL 0x001005b0
1738 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1739 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x1007e2, 8)
1740 | --- CALL (ram, 0x1005b0, 8)
1741 | LEA RAX,[RBP + -0x12]
1742 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xffffffffffffffee, 8)
1743 | (register, 0x0, 8) COPY (unique, 0x620, 8)
1744 | MOV RSI,RAX
1745 | (register, 0x30, 8) COPY (register, 0x0, 8)
1746 | LEA RDI,[0x1008cf]
1747 | (register, 0x38, 8) COPY (const, 0x1008cf, 8)
1748 | MOV EAX,0x0
1749 | (register, 0x0, 8) COPY (const, 0x0, 8)
1750 | CALL 0x001005d0
1751 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1752 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x1007fa, 8)
1753 | --- CALL (ram, 0x1005d0, 8)
1754 | MOV EAX,0x0
1755 | (register, 0x0, 8) COPY (const, 0x0, 8)
1756 | MOV RCX,qword ptr [RBP + -0x8]
1757 | (unique, 0x620, 8) INT_ADD (register, 0x28, 8) , (const, 0xfffffffffffffff8, 8)
1758 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x620, 8)
1759 | (register, 0x8, 8) COPY (unique, 0x1ff0, 8)
1760 | XOR RCX,qword ptr FS:[0x28]
1761 | (unique, 0x9e0, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8)
1762 | (register, 0x200, 1) COPY (const, 0x0, 1)
1763 | (register, 0x20b, 1) COPY (const, 0x0, 1)
1764 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8)
1765 | (register, 0x8, 8) INT_XOR (register, 0x8, 8) , (unique, 0x1ff0, 8)
1766 | (register, 0x207, 1) INT_SLESS (register, 0x8, 8) , (const, 0x0, 8)
1767 | (register, 0x206, 1) INT_EQUAL (register, 0x8, 8) , (const, 0x0, 8)
1768 | JZ 0x00100813
1769 | --- CBRANCH (ram, 0x100813, 8) , (register, 0x206, 1)
1770 | CALL 0x001005c0
1771 | (register, 0x20, 8) INT_SUB (register, 0x20, 8) , (const, 0x8, 8)
1772 | --- STORE (const, 0x1b1, 8) , (register, 0x20, 8) , (const, 0x100813, 8)
1773 | --- CALL (ram, 0x1005c0, 8)
1774 | LEAVE
1775 | (register, 0x20, 8) COPY (register, 0x28, 8)
1776 | (register, 0x28, 8) LOAD (const, 0x1b1, 8) , (register, 0x20, 8)
1777 | (register, 0x20, 8) INT_ADD (register, 0x20, 8) , (const, 0x8, 8)
1778 | RET
1779 | (register, 0x288, 8) LOAD (const, 0x1b1, 8) , (register, 0x20, 8)
1780 | (register, 0x20, 8) INT_ADD (register, 0x20, 8) , (const, 0x8, 8)
1781 | --- RETURN (register, 0x288, 8)
1782 | ```
1783 |
1784 |
1785 |
[⬆ Back to top](#table-of-contents)
1786 |
1787 |
1788 | ### Dumping Refined PCode
1789 | PCode exists in two primary forms you as a user should consider, "raw" and "refined". In documentation both forms are simply referred to as "PCode" making it confusing to talk about - so I distinguish between the forms using raw and refined. Just know theses are not universally accepted terms.
1790 |
1791 | So refined PCode is heavily processed. It highly relates to the output you see in the decompiler, and if you're interested in making use of the Ghidra decompiler passes, this is the form of PCode you'll want to analyze. There are many interesting aspects of refined PCode we do not cover here, including `unique` values and name spaces. Just know that what might appear to be simple has a lot of analysis backing it and digging into these refined PCode elements are worth your time.
1792 |
1793 | ```python
1794 | from ghidra.util.task import ConsoleTaskMonitor
1795 | from ghidra.app.decompiler import DecompileOptions, DecompInterface
1796 |
1797 | # == helper functions =============================================================================
1798 | def get_high_function(func):
1799 | options = DecompileOptions()
1800 | monitor = ConsoleTaskMonitor()
1801 | ifc = DecompInterface()
1802 | ifc.setOptions(options)
1803 | ifc.openProgram(getCurrentProgram())
1804 | # Setting a simplification style will strip useful `indirect` information.
1805 | # Please don't use this unless you know why you're using it.
1806 | #ifc.setSimplificationStyle("normalize")
1807 | res = ifc.decompileFunction(func, 60, monitor)
1808 | high = res.getHighFunction()
1809 | return high
1810 |
1811 | def dump_refined_pcode(func, high_func):
1812 | opiter = high_func.getPcodeOps()
1813 | while opiter.hasNext():
1814 | op = opiter.next()
1815 | print("{}".format(op.toString()))
1816 |
1817 | # == run examples =================================================================================
1818 | func = getGlobalFunctions("main")[0] # assumes only one function named `main`
1819 | hf = get_high_function(func) # we need a high function from the decompiler
1820 | dump_refined_pcode(func, hf) # dump straight refined pcode as strings
1821 | ```
1822 |
1823 |
1824 | Output example
1825 |
1826 | Notice how this looks quite different than the raw PCode.
1827 |
1828 | ```
1829 | (unique, 0x10000110, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8)
1830 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8)
1831 | (unique, 0x9e0, 8) CAST (unique, 0x10000110, 8)
1832 | (unique, 0x2250, 1) INT_SLESS (register, 0x38, 4) , (const, 0x2, 4)
1833 | --- CBRANCH (ram, 0x100743, 1) , (unique, 0x2250, 1)
1834 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (register, 0x30, 8)
1835 | --- CALL (ram, 0x1005d0, 8) , (unique, 0x100000a8, 8) , (unique, 0x1ff0, 8)
1836 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x32, 4)
1837 | (stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x32, 4)
1838 | (stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x32, 4)
1839 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x32, 4)
1840 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x32, 4)
1841 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x32, 4)
1842 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x32, 4)
1843 | (stack, 0xfffffffffffffff0, 8) INDIRECT (unique, 0x1ff0, 8) , (const, 0x32, 4)
1844 | (unique, 0x100000a8, 8) COPY (const, 0x1008a4, 8)
1845 | --- BRANCH (ram, 0x1007ff, 1)
1846 | (stack, 0xffffffffffffffdc, 8) COPY (const, 0x4242424241414141, 8)
1847 | (stack, 0xffffffffffffffe4, 2) COPY (const, 0x43, 2)
1848 | (stack, 0xffffffffffffffcc, 8) COPY (const, 0x0, 8)
1849 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffdc, 8)
1850 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffcc, 8)
1851 | --- CALL (ram, 0x1005b0, 8) , (unique, 0x10000118, 8) , (unique, 0x10000120, 8)
1852 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x5d, 4)
1853 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x5d, 4)
1854 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x5d, 4)
1855 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x5d, 4)
1856 | (stack, 0xfffffffffffffff0, 8) INDIRECT (unique, 0x1ff0, 8) , (const, 0x5d, 4)
1857 | (unique, 0x10000118, 8) CAST (unique, 0x620, 8)
1858 | (unique, 0x10000120, 8) CAST (unique, 0x620, 8)
1859 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffcc, 8)
1860 | --- CALL (ram, 0x1005d0, 8) , (unique, 0x100000b0, 8) , (unique, 0x620, 8)
1861 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x65, 4)
1862 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x65, 4)
1863 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x65, 4)
1864 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x65, 4)
1865 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x65, 4)
1866 | (unique, 0x100000b0, 8) COPY (const, 0x1008b6, 8)
1867 | (stack, 0xffffffffffffffd4, 8) COPY (const, 0x0, 8)
1868 | (register, 0x0, 8) PTRADD (register, 0x30, 8) , (const, 0x1, 8) , (const, 0x8, 8)
1869 | (unique, 0x10000128, 8) LOAD (const, 0x1b1, 4) , (register, 0x0, 8)
1870 | (unique, 0x1ff0, 8) CAST (unique, 0x10000128, 8)
1871 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffd4, 8)
1872 | --- CALL (ram, 0x1005b0, 8) , (unique, 0x10000130, 8) , (unique, 0x1ff0, 8)
1873 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x79, 4)
1874 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x79, 4)
1875 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x79, 4)
1876 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x79, 4)
1877 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x79, 4)
1878 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x79, 4)
1879 | (unique, 0x10000130, 8) CAST (unique, 0x620, 8)
1880 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffd4, 8)
1881 | --- CALL (ram, 0x1005d0, 8) , (unique, 0x100000b8, 8) , (unique, 0x620, 8)
1882 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x81, 4)
1883 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x81, 4)
1884 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x81, 4)
1885 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x81, 4)
1886 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x81, 4)
1887 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x81, 4)
1888 | (unique, 0x100000b8, 8) COPY (const, 0x1008c2, 8)
1889 | (stack, 0xffffffffffffffc7, 4) COPY (const, 0x39393939, 4)
1890 | (stack, 0xffffffffffffffcb, 1) COPY (const, 0x0, 1)
1891 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffc7, 8)
1892 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffe6, 8)
1893 | --- CALL (ram, 0x1005b0, 8) , (unique, 0x620, 8) , (unique, 0x10000138, 8)
1894 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x90, 4)
1895 | (stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x90, 4)
1896 | (stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x90, 4)
1897 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x90, 4)
1898 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x90, 4)
1899 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x90, 4)
1900 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x90, 4)
1901 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x90, 4)
1902 | (unique, 0x10000138, 8) CAST (unique, 0x620, 8)
1903 | (unique, 0x620, 8) PTRSUB (register, 0x20, 8) , (const, 0xffffffffffffffe6, 8)
1904 | --- CALL (ram, 0x1005d0, 8) , (unique, 0x100000c0, 8) , (unique, 0x620, 8)
1905 | (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x98, 4)
1906 | (stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x98, 4)
1907 | (stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x98, 4)
1908 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x98, 4)
1909 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x98, 4)
1910 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x98, 4)
1911 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x98, 4)
1912 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x98, 4)
1913 | (unique, 0x100000c0, 8) COPY (const, 0x1008cf, 8)
1914 | (register, 0x0, 8) INT_ZEXT (unique, 0x1000009c, 1)
1915 | (register, 0x110, 8) MULTIEQUAL (register, 0x110, 8) , (register, 0x110, 8)
1916 | (unique, 0x1000009c, 1) INT_SLESS (register, 0x38, 4) , (const, 0x2, 4)
1917 | (stack, 0xffffffffffffffc7, 4) MULTIEQUAL (stack, 0xffffffffffffffc7, 4) , (stack, 0xffffffffffffffc7, 4)
1918 | (stack, 0xffffffffffffffcb, 1) MULTIEQUAL (stack, 0xffffffffffffffcb, 1) , (stack, 0xffffffffffffffcb, 1)
1919 | (stack, 0xffffffffffffffcc, 8) MULTIEQUAL (stack, 0xffffffffffffffcc, 8) , (stack, 0xffffffffffffffcc, 8)
1920 | (stack, 0xffffffffffffffd4, 8) MULTIEQUAL (stack, 0xffffffffffffffd4, 8) , (stack, 0xffffffffffffffd4, 8)
1921 | (stack, 0xffffffffffffffdc, 8) MULTIEQUAL (stack, 0xffffffffffffffdc, 8) , (stack, 0xffffffffffffffdc, 8)
1922 | (stack, 0xffffffffffffffe4, 2) MULTIEQUAL (stack, 0xffffffffffffffe4, 2) , (stack, 0xffffffffffffffe4, 2)
1923 | (stack, 0xfffffffffffffff0, 8) MULTIEQUAL (stack, 0xfffffffffffffff0, 8) , (stack, 0xfffffffffffffff0, 8)
1924 | (unique, 0x10000140, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8)
1925 | (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8)
1926 | (register, 0x206, 1) INT_NOTEQUAL (stack, 0xfffffffffffffff0, 8) , (unique, 0x1ff0, 8)
1927 | (unique, 0x9e0, 8) CAST (unique, 0x10000140, 8)
1928 | --- CBRANCH (ram, 0x100813, 1) , (register, 0x206, 1)
1929 | --- CALL (ram, 0x1005c0, 8)
1930 | --- RETURN (const, 0x1, 4)
1931 | (stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x42, 4)
1932 | (stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x42, 4)
1933 | (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x42, 4)
1934 | (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x42, 4)
1935 | (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x42, 4)
1936 | (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x42, 4)
1937 | (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x42, 4)
1938 | --- RETURN (const, 0x0, 8) , (register, 0x0, 8)
1939 | ```
1940 |
1941 |
1942 |
[⬆ Back to top](#table-of-contents)
1943 |
1944 |
1945 | ### Plotting a Function AST
1946 | Ghidra does not define a default graph provider, so you cannot graph abstract synatax trees out of the box. Here's a snippet that takes elements from Ghidra's Graph Java snippets and hacks them together to get an SVG version of a function's AST. This requires you to have `GraphViz` installed with the `dot` binary in your `PATH`, and `networkx` and `pydot` accessible from the Ghidra Jython console. Basically, I just copy my Python2 site-packages over to my ghidra_scripts directory and everything works. Keep in mind that if your Python module uses a natively compiled element (like Matplot lib does), you won't be able to use it in Jython. If you know of a way, please let me know.
1947 |
1948 | ```python
1949 | import networkx as nx
1950 | import pydot
1951 |
1952 | from ghidra.app.script import GhidraScript
1953 | from ghidra.util.task import ConsoleTaskMonitor
1954 | from ghidra.app.decompiler import DecompileOptions, DecompInterface
1955 | from ghidra.program.model.pcode import PcodeOp
1956 |
1957 | def buildAST(func):
1958 | options = DecompileOptions()
1959 | monitor = ConsoleTaskMonitor()
1960 | ifc = DecompInterface()
1961 | ifc.setOptions(options)
1962 | ifc.openProgram(getCurrentProgram())
1963 | ifc.setSimplificationStyle("normalize")
1964 | res = ifc.decompileFunction(func, 60, monitor)
1965 | high = res.getHighFunction()
1966 | return high
1967 |
1968 | def buildGraph(graph, func, high):
1969 | vertices = {}
1970 | opiter = getPcodeOpIterator(high)
1971 | while opiter.hasNext():
1972 | op = opiter.next()
1973 | vert = createOpVertex(func, op)
1974 | graph.add_node(vert)
1975 | for i in range(0, op.getNumInputs()):
1976 | opcode = op.getOpcode()
1977 | if (i == 0 and (opcode == PcodeOp.LOAD or opcode == PcodeOp.STORE)):
1978 | continue
1979 | if (i == 1 and opcode == PcodeOp.INDIRECT):
1980 | continue
1981 | vn = op.getInput(i)
1982 | if (vn != None):
1983 | v = getVarnodeVertex(graph, vertices, vn)
1984 | graph.add_edge(v, vert)
1985 |
1986 | outvn = op.getOutput()
1987 | if (outvn != None):
1988 | outv = getVarnodeVertex(graph, vertices, outvn)
1989 | if (outv != None):
1990 | graph.add_edge(vert, outv)
1991 |
1992 | def createOpVertex(func, op):
1993 | name = op.getMnemonic()
1994 | id = getOpKey(op)
1995 | opcode = op.getOpcode()
1996 | if ((opcode == PcodeOp.LOAD) or (opcode == PcodeOp.STORE)):
1997 | vn = op.getInput(0)
1998 | addrspace = currentProgram.getAddressFactory().getAddressSpace(vn.getOffset())
1999 | name += ' ' + addrspace.getName()
2000 | elif (opcode == PcodeOp.INDIRECT):
2001 | vn = op.getInput(1)
2002 | if (vn != None):
2003 | indOp = high.getOpRef(vn.getOffset())
2004 | if (indOp != None):
2005 | name += " (" + indOp.getMnemonic() + ")"
2006 | return "{}_{}".format(name, id)
2007 |
2008 | def createVarnodeVertex(graph, vn):
2009 | name = str(vn.getAddress())
2010 | id = getVarnodeKey(vn)
2011 | if (vn.isRegister()):
2012 | reg = currentProgram.getRegister(vn.getAddress(), vn.getSize())
2013 | if (reg != None):
2014 | name = reg.getName()
2015 | return "{}_{}".format(name, id)
2016 |
2017 | def getVarnodeVertex(graph, vertices, vn):
2018 | res = None
2019 | try:
2020 | res = vertices[str(vn.getUniqueId())]
2021 | except KeyError:
2022 | res = None
2023 | if (res == None):
2024 | res = createVarnodeVertex(graph, vn)
2025 | vertices[str(vn.getUniqueId())] = res
2026 | return res
2027 |
2028 | def getAddress(offset):
2029 | return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)
2030 |
2031 | def getOpKey(op):
2032 | sq = op.getSeqnum()
2033 | id = str(sq.getTarget()) + " o " + str(op.getSeqnum().getTime())
2034 | return id
2035 |
2036 | def getPcodeOpIterator(high):
2037 | return high.getPcodeOps()
2038 |
2039 | def getVarnodeKey(vn):
2040 | op = vn.getDef()
2041 | id = ""
2042 | if (op != None):
2043 | id = str(op.getSeqnum().getTarget()) + " v " + str(vn.getUniqueId())
2044 | else:
2045 | id = "i v " + str(vn.getUniqueId())
2046 | return id
2047 |
2048 | def main():
2049 | graph = nx.DiGraph()
2050 | listing = currentProgram.getListing()
2051 | func = getFunctionContaining(getAddress(0x00100690))
2052 | high = buildAST(func)
2053 | buildGraph(graph, func, high)
2054 |
2055 | dot_data = nx.nx_pydot.to_pydot(graph)
2056 | svg = pydot.graph_from_dot_data(dot_data.to_string())[0].create_svg()
2057 | svg_path = "C:\\Users\\username\\Desktop\\test.svg"
2058 | f = open(svg_path, 'w')
2059 | f.write(svg)
2060 | f.close()
2061 |
2062 | print("Wrote pydot SVG of graph to: {}\nNodes: {}, Edges: {}".format(svg_path, len(graph.nodes), len(graph.edges)))
2063 |
2064 | main()
2065 | ```
2066 |
2067 |
2068 | Output example
2069 |
2070 | ```
2071 | # also writes a file called 'test.svg' to the Windows Desktop for user 'username'.
2072 | Wrote pydot SVG of graph to: C:\Users\username\Desktop\test.svg
2073 | Nodes: 47, Edges: 48
2074 | ```
2075 |
2076 |
2077 |
2078 | ## Working with Graphs
2079 |
2080 | ### Creating a Call Graph
2081 | Ghidra's complex API allows for the creation of various graph structures including directional graphs (digraphs). This example shows how to create a DiGraph of vertices (functions) and edges (calls from/to).
2082 |
2083 | Note that adding a vertex or an edge between two vertex entries does not reuse or override them! This is because, while many nodes share the same name, they contain unique hash codes (keys). If you were looking to trim this graph to include only unqiue nodes, you would need to consider both the name of the symbol and its address to account for overridden functions.
2084 |
2085 | In its current form, this DiGraph is unlikely to be of any use to you. But the building blocks of creating interesting control flow graphs (CFG), program dependence graphs (PDG), data dependency graphs (DDG), and other graphs are all here.
2086 |
2087 | ```python
2088 | from ghidra.util.graph import DirectedGraph
2089 | from ghidra.util.graph import Edge
2090 | from ghidra.util.graph import Vertex
2091 |
2092 | def getAddress(offset):
2093 | return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)
2094 |
2095 | digraph = DirectedGraph()
2096 | listing = currentProgram.getListing()
2097 | fm = currentProgram.getFunctionManager()
2098 |
2099 | funcs = fm.getFunctions(True) # True mean iterate forward
2100 | for func in funcs:
2101 | # Add function vertices
2102 | print("Function: {} @ 0x{}".format(func.getName(), func.getEntryPoint())) # FunctionDB
2103 | digraph.add(Vertex(func))
2104 |
2105 | # Add edges for static calls
2106 | entryPoint = func.getEntryPoint()
2107 | instructions = listing.getInstructions(entryPoint, True)
2108 | for instruction in instructions:
2109 | addr = instruction.getAddress()
2110 | oper = instruction.getMnemonicString()
2111 | if oper == "CALL":
2112 | print(" 0x{} : {}".format(addr, instruction))
2113 | flows = instruction.getFlows()
2114 | if len(flows) == 1:
2115 | target_addr = "0x{}".format(flows[0])
2116 | digraph.add(Edge(Vertex(func), Vertex(fm.getFunctionAt(getAddress(target_addr)))))
2117 |
2118 | print("DiGraph info:")
2119 | edges = digraph.edgeIterator()
2120 | while edges.hasNext():
2121 | edge = edges.next()
2122 | from_vertex = edge.from()
2123 | to_vertex = edge.to()
2124 | print(" Edge from {} to {}".format(from_vertex, to_vertex))
2125 |
2126 | vertices = digraph.vertexIterator()
2127 | while vertices.hasNext():
2128 | vertex = vertices.next()
2129 | print(" Vertex: {} (key: {})".format(vertex, vertex.key()))
2130 | # some extra stuff you might want to see
2131 | #print(" type(vertex): {}".format(type(vertex)))
2132 | #print(" vertex.hashCode(): {}".format(vertex.hashCode()))
2133 | #print(" vertex.referent(): {}".format(vertex.referent()))
2134 | #print(" type(referent): {}".format(type(vertex.referent())))
2135 | ```
2136 |
2137 |
2138 | Output example
2139 |
2140 | ```
2141 | Function: main @ 0x0010064a
2142 | 0x00100691 : CALL 0x00100520
2143 | 0x001006cc : CALL 0x001004f0
2144 | 0x001006e9 : CALL qword ptr [R12 + RBX*0x8]
2145 | <...snip...>
2146 |
2147 | DiGraph info:
2148 | Edge from _init to __gmon_start__
2149 | Edge from _init to __libc_start_main
2150 | Edge from _init to __cxa_finalize
2151 | Edge from _init to deregister_tm_clones
2152 | Edge from _init to printf
2153 | Edge from _init to _init
2154 | Edge from printf to __libc_start_main
2155 | Edge from printf to __cxa_finalize
2156 | <...snip...>
2157 | Vertex: _init (key: 3690)
2158 | Vertex: main (key: 3803)
2159 | Vertex: main (key: 3804)
2160 | Vertex: printf (key: 3805)
2161 | Vertex: main (key: 3807)
2162 | Vertex: _init (key: 3808)
2163 | Vertex: __libc_csu_init (key: 3810)
2164 | Vertex: _init (key: 3812)
2165 | Vertex: __libc_csu_fini (key: 3814)
2166 | <...snip...>
2167 | ```
2168 |
2169 |
2170 |
[⬆ Back to top](#table-of-contents)
2171 |
2172 |
2173 | ## Miscellaneous
2174 |
2175 | ### Program Slices
2176 | Given a specific varnode, Ghidra is able to generate backward and forward program slices. This functionality is contained in the `DecompilerUtils` class as four functions; `getBackwardSlice`, `getForwardSlice`, `getForwardSliceToPCodeOps`, and `getBackwardSliceToPCodeOps`.
2177 |
2178 | You can read more about program slicing online, but the idea is to focus on a specific varnode (say a variable) and slice away anything unrelated to it so you're left with a slice of program related to the element you're interested in. In the Ghidra UI you can right-click a variable and select a program slice option and Ghidra will highlight the slice. If you right-click and don't see an option, you're not clicking on a compatable varnode / element, try something else.
2179 |
2180 | ```python
2181 | from ghidra.util.task import ConsoleTaskMonitor
2182 | from ghidra.app.decompiler import DecompileOptions, DecompInterface
2183 | from ghidra.app.decompiler.component import DecompilerUtils
2184 |
2185 | # == helper functions =============================================================================
2186 | def get_high_function(func):
2187 | options = DecompileOptions()
2188 | monitor = ConsoleTaskMonitor()
2189 | ifc = DecompInterface()
2190 | ifc.setOptions(options)
2191 | ifc.openProgram(getCurrentProgram())
2192 | res = ifc.decompileFunction(func, 60, monitor)
2193 | high = res.getHighFunction()
2194 | return high
2195 |
2196 | # == run examples =================================================================================
2197 | func = getGlobalFunctions("main")[0]
2198 | hf = get_high_function(func)
2199 | lsm = hf.getLocalSymbolMap()
2200 | symbols = lsm.getSymbols()
2201 |
2202 | for symbol in symbols:
2203 | print("\nSymbol name: {}".format(symbol.getName()))
2204 | hv = symbol.getHighVariable()
2205 | # Try this snippet with `hv.getInstances()` to enumerate slices for all instances!
2206 | varnode = hv.getRepresentative()
2207 | print("Varnode: {}".format(varnode))
2208 | fs = DecompilerUtils.getForwardSlice(varnode)
2209 | print("Forward Slice: {}".format(fs))
2210 | bs = DecompilerUtils.getBackwardSlice(varnode)
2211 | print("Backward Slice: {}".format(bs))
2212 | bswo = DecompilerUtils.getBackwardSliceToPCodeOps(varnode)
2213 | print("Backward Slice w/ PCode Ops: {}".format(bswo))
2214 | fswo = DecompilerUtils.getForwardSliceToPCodeOps(varnode)
2215 | print("Forward Slice w/ PCode Ops: {}".format(fswo))
2216 | ```
2217 |
2218 |
2219 | Output examples
2220 |
2221 | You might think these slices are returning arrays (Python lists) with the name varnodes duplacted over and over - but that's not the case. Each use has a unique ID that ties it to other operations and uses. The printed varnodes may look the same, but these are all unique instances with more data under the hood. Experiment a bit and you'll see what I mean.
2222 |
2223 | ```
2224 | Symbol name: local_1c
2225 | Varnode: (stack, 0xffffffffffffffe4, 2)
2226 | Forward Slice: [(stack, 0xffffffffffffffe4, 2), (stack, 0xffffffffffffffe4, 2), (stack, 0xffffffffffffffe4, 2), (stack, 0xffffffffffffffe4, 2)]
2227 | Backward Slice: [(stack, 0xffffffffffffffe4, 2)]
2228 | Backward Slice w/ PCode Ops: []
2229 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x32, 4), (stack, 0xffffffffffffffe4, 2) MULTIEQUAL (stack, 0xffffffffffffffe4, 2) , (stack, 0xffffffffffffffe4, 2), (stack, 0xffffffffffffffe4, 2) INDIRECT (stack, 0xffffffffffffffe4, 2) , (const, 0x42, 4)]
2230 |
2231 | Symbol name: local_24
2232 | Varnode: (stack, 0xffffffffffffffdc, 8)
2233 | Forward Slice: [(stack, 0xffffffffffffffdc, 8), (stack, 0xffffffffffffffdc, 8), (stack, 0xffffffffffffffdc, 8), (stack, 0xffffffffffffffdc, 8)]
2234 | Backward Slice: [(stack, 0xffffffffffffffdc, 8)]
2235 | Backward Slice w/ PCode Ops: []
2236 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x32, 4), (stack, 0xffffffffffffffdc, 8) MULTIEQUAL (stack, 0xffffffffffffffdc, 8) , (stack, 0xffffffffffffffdc, 8), (stack, 0xffffffffffffffdc, 8) INDIRECT (stack, 0xffffffffffffffdc, 8) , (const, 0x42, 4)]
2237 |
2238 | Symbol name: local_35
2239 | Varnode: (stack, 0xffffffffffffffcb, 1)
2240 | Forward Slice: [(stack, 0xffffffffffffffcb, 1), (stack, 0xffffffffffffffcb, 1), (stack, 0xffffffffffffffcb, 1), (stack, 0xffffffffffffffcb, 1)]
2241 | Backward Slice: [(stack, 0xffffffffffffffcb, 1)]
2242 | Backward Slice w/ PCode Ops: []
2243 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x42, 4), (stack, 0xffffffffffffffcb, 1) INDIRECT (stack, 0xffffffffffffffcb, 1) , (const, 0x32, 4), (stack, 0xffffffffffffffcb, 1) MULTIEQUAL (stack, 0xffffffffffffffcb, 1) , (stack, 0xffffffffffffffcb, 1)]
2244 |
2245 | Symbol name: param_1
2246 | Varnode: (register, 0x38, 4)
2247 | Forward Slice: [(register, 0x38, 4), (unique, 0x2250, 1), (register, 0x0, 8), (unique, 0x1000009c, 1)]
2248 | Backward Slice: [(register, 0x38, 4)]
2249 | Backward Slice w/ PCode Ops: []
2250 | Forward Slice w/ PCode Ops: [(unique, 0x2250, 1) INT_SLESS (register, 0x38, 4) , (const, 0x2, 4), (register, 0x0, 8) INT_ZEXT (unique, 0x1000009c, 1), (unique, 0x1000009c, 1) INT_SLESS (register, 0x38, 4) , (const, 0x2, 4), --- CBRANCH (ram, 0x100743, 1) , (unique, 0x2250, 1), --- RETURN (const, 0x0, 8) , (register, 0x0, 8)]
2251 |
2252 | Symbol name: param_2
2253 | Varnode: (register, 0x30, 8)
2254 | Forward Slice: [(register, 0x30, 8), (unique, 0x1ff0, 8), (unique, 0x1ff0, 8), (unique, 0x10000128, 8), (register, 0x0, 8)]
2255 | Backward Slice: [(register, 0x30, 8)]
2256 | Backward Slice w/ PCode Ops: []
2257 | Forward Slice w/ PCode Ops: [(unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (register, 0x30, 8), (unique, 0x10000128, 8) LOAD (const, 0x1b1, 4) , (register, 0x0, 8), --- CALL (ram, 0x1005d0, 8) , (unique, 0x100000a8, 8) , (unique, 0x1ff0, 8), (register, 0x0, 8) PTRADD (register, 0x30, 8) , (const, 0x1, 8) , (const, 0x8, 8), (unique, 0x1ff0, 8) CAST (unique, 0x10000128, 8), --- CALL (ram, 0x1005b0, 8) , (unique, 0x10000130, 8) , (unique, 0x1ff0, 8)]
2258 |
2259 | Symbol name: in_FS_OFFSET
2260 | Varnode: (register, 0x110, 8)
2261 | Forward Slice: [(stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (unique, 0x9e0, 8), (stack, 0xfffffffffffffff0, 8), (unique, 0x1ff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (register, 0x110, 8), (register, 0x110, 8), (register, 0x206, 1), (register, 0x110, 8), (register, 0x110, 8), (unique, 0x9e0, 8), (register, 0x110, 8), (unique, 0x1ff0, 8), (register, 0x110, 8), (register, 0x110, 8), (register, 0x110, 8), (unique, 0x10000110, 8), (register, 0x110, 8), (unique, 0x10000140, 8)]
2262 | Backward Slice: [(register, 0x110, 8)]
2263 | Backward Slice w/ PCode Ops: []
2264 | Forward Slice w/ PCode Ops: [(unique, 0x10000140, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8), (register, 0x206, 1) INT_NOTEQUAL (stack, 0xfffffffffffffff0, 8) , (unique, 0x1ff0, 8), (stack, 0xfffffffffffffff0, 8) INDIRECT (unique, 0x1ff0, 8) , (const, 0x5d, 4), (unique, 0x9e0, 8) CAST (unique, 0x10000110, 8), (unique, 0x9e0, 8) CAST (unique, 0x10000140, 8), (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x79, 4), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x5d, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x65, 4), (stack, 0xfffffffffffffff0, 8) MULTIEQUAL (stack, 0xfffffffffffffff0, 8) , (stack, 0xfffffffffffffff0, 8), (unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x79, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x81, 4), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x90, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (unique, 0x1ff0, 8) , (const, 0x32, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x98, 4), --- CBRANCH (ram, 0x100813, 1) , (register, 0x206, 1), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x42, 4), (unique, 0x10000110, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x32, 4), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x98, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x90, 4), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x65, 4), (register, 0x110, 8) MULTIEQUAL (register, 0x110, 8) , (register, 0x110, 8), (register, 0x110, 8) INDIRECT (register, 0x110, 8) , (const, 0x81, 4)]
2265 |
2266 | Symbol name: local_10
2267 | Varnode: (stack, 0xfffffffffffffff0, 8)
2268 | Forward Slice: [(stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (register, 0x206, 1), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8), (stack, 0xfffffffffffffff0, 8)]
2269 | Backward Slice: [(stack, 0xfffffffffffffff0, 8), (const, 0x5d, 4), (register, 0x110, 8), (unique, 0x10000110, 8), (unique, 0x9e0, 8), (const, 0x1b1, 4), (const, 0x28, 8), (unique, 0x1ff0, 8)]
2270 | Backward Slice w/ PCode Ops: [(unique, 0x1ff0, 8) LOAD (const, 0x1b1, 4) , (unique, 0x9e0, 8), (unique, 0x10000110, 8) INT_ADD (register, 0x110, 8) , (const, 0x28, 8), (stack, 0xfffffffffffffff0, 8) INDIRECT (unique, 0x1ff0, 8) , (const, 0x5d, 4), (unique, 0x9e0, 8) CAST (unique, 0x10000110, 8)]
2271 | Forward Slice w/ PCode Ops: [(stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x65, 4), (stack, 0xfffffffffffffff0, 8) MULTIEQUAL (stack, 0xfffffffffffffff0, 8) , (stack, 0xfffffffffffffff0, 8), (register, 0x206, 1) INT_NOTEQUAL (stack, 0xfffffffffffffff0, 8) , (unique, 0x1ff0, 8), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x42, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x81, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x90, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x98, 4), (stack, 0xfffffffffffffff0, 8) INDIRECT (stack, 0xfffffffffffffff0, 8) , (const, 0x79, 4), --- CBRANCH (ram, 0x100813, 1) , (register, 0x206, 1)]
2272 |
2273 | Symbol name: local_1a
2274 | Varnode: (stack, 0xffffffffffffffe6, 10)
2275 | Forward Slice: [(stack, 0xffffffffffffffe6, 10)]
2276 | Backward Slice: [(stack, 0xffffffffffffffe6, 10)]
2277 | Backward Slice w/ PCode Ops: []
2278 | Forward Slice w/ PCode Ops: []
2279 |
2280 | Symbol name: local_34
2281 | Varnode: (stack, 0xffffffffffffffcc, 8)
2282 | Forward Slice: [(stack, 0xffffffffffffffcc, 8), (stack, 0xffffffffffffffcc, 8), (stack, 0xffffffffffffffcc, 8), (stack, 0xffffffffffffffcc, 8)]
2283 | Backward Slice: [(stack, 0xffffffffffffffcc, 8)]
2284 | Backward Slice w/ PCode Ops: []
2285 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x32, 4), (stack, 0xffffffffffffffcc, 8) MULTIEQUAL (stack, 0xffffffffffffffcc, 8) , (stack, 0xffffffffffffffcc, 8), (stack, 0xffffffffffffffcc, 8) INDIRECT (stack, 0xffffffffffffffcc, 8) , (const, 0x42, 4)]
2286 |
2287 | Symbol name: local_2c
2288 | Varnode: (stack, 0xffffffffffffffd4, 8)
2289 | Forward Slice: [(stack, 0xffffffffffffffd4, 8), (stack, 0xffffffffffffffd4, 8), (stack, 0xffffffffffffffd4, 8), (stack, 0xffffffffffffffd4, 8)]
2290 | Backward Slice: [(stack, 0xffffffffffffffd4, 8)]
2291 | Backward Slice w/ PCode Ops: []
2292 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x32, 4), (stack, 0xffffffffffffffd4, 8) MULTIEQUAL (stack, 0xffffffffffffffd4, 8) , (stack, 0xffffffffffffffd4, 8), (stack, 0xffffffffffffffd4, 8) INDIRECT (stack, 0xffffffffffffffd4, 8) , (const, 0x42, 4)]
2293 |
2294 | Symbol name: local_39
2295 | Varnode: (stack, 0xffffffffffffffc7, 4)
2296 | Forward Slice: [(stack, 0xffffffffffffffc7, 4), (stack, 0xffffffffffffffc7, 4), (stack, 0xffffffffffffffc7, 4), (stack, 0xffffffffffffffc7, 4)]
2297 | Backward Slice: [(stack, 0xffffffffffffffc7, 4)]
2298 | Backward Slice w/ PCode Ops: []
2299 | Forward Slice w/ PCode Ops: [(stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x32, 4), (stack, 0xffffffffffffffc7, 4) MULTIEQUAL (stack, 0xffffffffffffffc7, 4) , (stack, 0xffffffffffffffc7, 4), (stack, 0xffffffffffffffc7, 4) INDIRECT (stack, 0xffffffffffffffc7, 4) , (const, 0x42, 4)]
2300 | ```
2301 |
2302 |
2303 |
[⬆ Back to top](#table-of-contents)
2304 |
2305 |
2306 | [0]: https://ghidra-sre.org/
2307 | [1]: https://ghidra.re/ghidra_docs/api/ghidra/program/flatapi/FlatProgramAPI.html
2308 | [2]: https://ghidra.re/ghidra_docs/api/ghidra/app/decompiler/flatapi/FlatDecompilerAPI.html
2309 | [3]: https://ghidra.re/ghidra_docs/api/ghidra/framework/model/Project.html
2310 | [4]: https://ghidra.re/ghidra_docs/api/ghidra/base/project/GhidraProject.html
2311 | [5]: https://ghidra.re/ghidra_docs/api/ghidra/program/model/listing/Program.html
2312 | [6]: https://ghidra.re/ghidra_docs/api/ghidra/program/database/ProgramDB.html
2313 |
--------------------------------------------------------------------------------