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