NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
49.06k stars 5.65k forks source link

Programmatic way of doing BSim search result application #6622

Closed harmansmithandsons closed 2 weeks ago

harmansmithandsons commented 3 weeks ago

I am interested in performing the "apply function signature and types of search result" functionality of BSim through programmatic means as part of a script I am working on.

I've reviewed the available example scripts that ship with Ghidra 11, namely QueryFunction.java and ExampleOverviewQueryScript,java, and while those show how to perform a search against BSim databases they don't show how the application is performed. I then tracked down the code responsible for populating functions in the Listing view, and came across AbstractBSimApplyTask::applyResults() and related functions in <ghidra_install_dir>\Ghidra\Features\BSim\lib\BSim-src.zip\ghidra\features\bsim\gui\search\results\apply\.

The relevant functions I've found do not appear to play nicely with the BSim scripting API exposed in the example scripts. As an example of this, a query made through QueryFunction.java provides a SimilarityResult object, but to instantiate an AbstractBSimApplyTask a BSimMatchResult object is needed, which has a lot of member overlap yet is distinguished from a SimilarityResult.

I'd like to know two things: first, is there a manageable way to perform search result application that is compatible with the kinds of objects the example scripts works with; second, if that is not the case, I'd appreciate confirmation/redirection to what parts of Ghidra code is responsible for application, so I could start adapting my current code to hook into that. Thanks for your time.

ghidracadabra commented 3 weeks ago

There's not a direct way to call these actions through a script. Note that the BSim results do not contain the signature and types of the matching function. However, applying the signature and types can be done easily with a static method in the FunctionUtility class, provided you have a Function object for the matching function.

To get that object, you will have to open the program containing the matching function and then get the Function at the appropriate address. The ghidra URL and the address are contained in the BSim results. To open the program you can use the constructor of the OpenProgramTask class that takes a ghidra URL.

We should probably add a utility method to handle all of this for you, or at least provide an example script.

harmansmithandsons commented 3 weeks ago

The method you just described that goes through FunctionUtility sounds like it has the implicit requirement of the user having access to whatever project contains the executable of the matching function. So in order to apply a search result, you would need both a database to perform the search as well as the project containing the executable of the matching function.

Does BSim as used through the listing view also require both the original project and a database? My understanding was that it was possible to use the application functionality of BSim with just a database without requiring the original project. I quickly leafed through the tutorial docs (.../docs/GhidraClass/BSim) just now again, and didn't find a mention of this requirement.

Edit: After taking a look through FunctionUtility, it looks like updateFunction() is definitely part of the behavior I was after, and is responsible for updating a function name and signature. I was having some trouble finding something in FunctionUtility that handles modifying function bodies. If you have any pointers to that behavior, I'd appreciate it.

ghidracadabra commented 3 weeks ago

In order to use the apply actions or the side-by-side comparison view you need to access the original program. The BSim database doesn't store the needed information.

The BSim server does return the name of the matching function (but not the namespace), so if that's all you care about you could write a script to rename a function using the BSim results alone.

What kind of modifications to a function body are you trying to do? Are you manually fixing switch statements?

harmansmithandsons commented 3 weeks ago

In order to use the apply actions or the side-by-side comparison view you need to access the original program. The BSim database doesn't store the needed information.

Thanks for clarifying this, it seems I had a misunderstanding.

What kind of modifications to a function body are you trying to do?

The body modifications I'm interested in are just the ones BSim already does, importing any user-defined structs/data types from a matched function and retyping any parameters/locals in the base function accordingly.

harmansmithandsons commented 2 weeks ago

I'm having some difficulty working with the OpenProgramTask class @ghidracadabra pointed me towards. I'm getting a little tripped up on what URL I should be passing into this constructor. Right now, I am creating a new OpenProgramTask with the following code:

FunctionDescription matchedFn = ...; // came from BSim SimilarityNote
String matchedFnRepoPath = matchedFn.getExecutableRecord().getRepository();
String matchedFnExeRelativePath = matchedFn.getExecutableRecord().getPath();

String fullPath = matchedFnRepoPath + matchedFnExeRelativePath;

OpenProgramTask opt = new OpenProgramTask(new URL(fullPath), new Object());

I then get a return value of null when I try opt.getOpenProgram(). One source of error that comes to my mind is that the URL I am passing into the OpenProgramTask constructor isn't what it expects. The source code for OpenProgramTask doesn't have much guidance to this end, so I'd appreciate a pointer to what the constructor is expecting.

ghidracadabra commented 2 weeks ago

Use the getURLString() method on the ExecutableResult to get the URL - I don't think the string you are using contains the program name. Also, make sure you call TaskLauncher.launch(opt) before you call opt.getOpenProgram().

harmansmithandsons commented 2 weeks ago

Thank you for those pointers! With those changes, my script is working as intended and I'm able to populate symbols across projects. I greatly appreciated your help! The below code snippet is what ended up working for me; I'll leave it below in case any others have similar questions.

FunctionDescription matchedFn = ...; // from BSim results

OpenProgramTask opt = new OpenProgramTask(new URL(matchedFn.getExecutableRecord().getURLString), new Object());
TaskLauncher.launch(opt);

Program matchedProgram = opt.getOpenProgram.getProgram();
Function replacementSource =  matchedProgram.getFunctionManager().getFunctionAt(addressOfMatchedFn);

FunctionUtility.applyNameAndNamespace(queriedFunction, replacementSource);
ghidracadabra commented 2 weeks ago

Forgot to mention, but it's probably a good idea to pass this as the consumer in the OpenProgramTask constructor. When you are done with matchedProgram, call matchedProgram.release(this). If you are iterating through a list of matches and applying them it might be worth organizing the code so that you open a program, apply all of the matches from it, and then release it.