This is a Ghidra plugin for HashDB. It allows you to compile a list of API hashes and then to query the HashDB web service for possible matching strings. It collects these associations into an enum or a struct. From there on, you are on your own.
Two options:
- Copy
HashDB.java
to yourghidra_script
directory. - Add the location where
HashDB.java
is located to your script directory search path in Ghidra's ScriptManager.
We recommend to bind the HashDB.java
script's execution to a hotkey, preferably F3.
The plugin consists of a single window containing a table showing the "currently collected" hashes. You can bring up the window by executing the script (i.e. hitting F3). In addition to opening the window (or showing it, if it was hidden) the script will do different things depending on where your cursor is:
- If the cursor is on an immediate or constant, it will add that value as a hash to the table.
- If you selected a memory region, it will interpret it as a list of hashes.
- Otherwise, it will assume that you want to scan for parameters to the currently opened function.
The GUI is actually perfect and completely intuitive to use with a great UX. But since we are also amazing at documentation, we include the following guidance:
- The plugin allows to transform the hash before lookup. You can put a JavaScript expression into the "Hash Transformation" input field. See the REvil example below.
- When you hit the "Query!" button, the script will use the HashDB web API to list all known and matching hashing algorithms. If there's only one, it will also just resolve all hashes. Otherwise, you have to select the correct algorithm in the "Hash Algorithm" field. Pretty much the same is true for the "String Permutation" field. tl;dr: just click "Query!".
- You can check the "Resolve Entire module" checkbox if you not only want to add all hashes from the table but also all other hashes from the parent DLLs.
- The "Scan Function" tab allows you to specify a function name and a parameter location. The script will crawl all function calls and add the corresponding argument to the table.
- Depending on the different switches and toggles in the "Output"-tab (which is very well-designed), the script will create one or two enums or structs. The order of fields in the resulting structs is the same as in the table. Structs are always overwritten, enums are always merged.
Consider the sample with the following SHA256 hash
de04d2402154f676f757cf1380671f396f3fc9f7dbb683d9461edd2718c4e09d
and navigate to the function at 0x00401360
. The code will look like the following:
iVar1 = FUN_00401220(0x84c05e40);
if (iVar1 != 0) {
pcVar2 = (code *)FUN_00401000(iVar1,-0x5e2ba68c);
if (pcVar2 != (code *)0x0) {
uVar6 = 0x254;
uVar5 = 8;
uVar3 = FUN_00406a40();
DAT_00417194 = (int *)(*pcVar2)(uVar3,uVar5,uVar6);
if (DAT_00417194 != (int *)0x0) {
iVar4 = FUN_00401000(iVar1,-0x5e2ba68c);
*DAT_00417194 = iVar4;
iVar4 = FUN_00401000(iVar1,-0x50ee43dc);
DAT_00417194[1] = iVar4;
iVar4 = FUN_00401000(iVar1,-0x468c4724);
DAT_00417194[2] = iVar4;
iVar4 = FUN_00401000(iVar1,-0x7b9c69f6);
/* ... */
You can either click on 0x84C05E40
, hit F3; click on -0x5E2BA68C
, hit F3,
and so on and so forth, until you are ready to "Query!". Alternatively you can double click
FUN_00401000
and then hit F3 to bring up the scan function tab. Confirm that the
pre-populated value in the "Parameter" field is correct, hit "Scan" and grab a cold cup of water.
Consider the sample with the following SHA256 hash
5f56d5748940e4039053f85978074bde16d64bd5ba97f6f0026ba8172cb29e93
and navigate to the memory region 0x004113F8
and convert it to an array of 140 DWORDs and hit
F3. This memory region contains all API hashes. Our goal is to create a struct that has
the corresponding API function pointers in the exact same location. This way, changing the type of
this global constant to the struct will make the code all pretty.
REvil's API hashing requires that you figure out a transformation that it applies to the hash. For this sample, the transformation is the following:
((((X ^ 0x76C7) << 0x10) ^ X) ^ 0xAFB9) & 0x1FFFFF /*REvil*/
It will be different for other REvil samples. Make sure to select "Generate Struct" in the "Output"
tab and "Query!". When it is done, change the type of 0x004113F8
to HashDB
. Happy times.
Contact us on Twitter
- Jesko Hüttenhain @huettenhain
- Lars Wallenborn @larsborn
or join the OALabs Discord oalabs-dev channel.