I'm currently working on a binary that has encrypted strings, using IDA 7.0. The encrypted data is copied to another location in memory, which is then decrypted. I have already decrypted the strings in-place, but due to the way the strings are accessed the decompiler is having trouble resolving them.

This is an example of ASM that accesses a decrypted string:

mov     rcx, cs:StringTableOffset
lea     rax, cs:180000000h
lea     rcx, [rcx+rax+130A4h]
call    cs:GetModuleHandleA

For the purpose of reversing, StringTableOffset can be considered 0, so the final lea instruction effectively just moves the value 0x1800130A4 into rcx. The decompiler doesn't know that, though, and shows pointer arithmetic instead.

|| ((v11 = GetModuleHandleA((LPCSTR)(StringTableOffset + 0x1800130A4i64))

Is there any way to signal to the decompiler that the value of rcx (the argument to GetModuleHandleA) is known, so that it can display the defined string?

1 Answer 1


The answer to the specific question that you asked is: not directly through the UI, but it's possible with a plugin. Sample plugin #18 in the Hex-Rays SDK. The comment at the top of the plugin says:

* It shows how to specify a register value at a desired location.
* Such a functionality may be useful when the code to decompile is
* obfuscated and uses opaque predicates.

However, there's a better answer to your question. The assembly code that you displayed is commonly generated by MSVC when accessing global arrays or data structures on x64. Basically, one register will be set to the base address of the containing module (rax in your snippet above), and then array accesses will be encoded as [rax+r64+RVA_of_array], where r64 is some register being used for indexing purposes.

Hex-Rays trips over snippets like this one, but there is a way to fix it: namely, by turning the numeric offset in the memory expression into an RVA. As far as I know, there's currently no way to do that through the GUI (EDIT: in the comments below, Igor Skochinsky informs us how to do this through the GUI), but you can do it through a small IDAPython script.

def make_rva(ea, n):
    idaapi.op_offset(ea, n, ida_nalt.REFINFO_RVAOFF | ida_nalt.REF_OFF64, idaapi.BADADDR, idaapi.get_imagebase(), 0)

Now, you can run the make_rva function by providing the address of the instruction with the memory expression as the first argument. The second argument is the operand number: 0 for left-hand side, 1 for right-hand side. Here's an example from a database I have open. Before:

0000000180019AEC cmp     byte ptr [rcx+rdx+138C84h], 0

After executing make_rva(0x0000000180019AEC,0):

0000000180019AEC cmp     rva stru_180138C80.sectors_per_cluster[rcx+rdx], 0

I recently released a database of a malware sample that I had analyzed exhaustively, where I used these tricks as part of the analysis. You can get the database here, and then jump to address 0000000180014607 to check out the effects.

EDIT, 08/24/2021: I reported the issue discussed above to Hex-Rays, who fixed it as of 7.6.

  • This is exactly what I was looking for. Thanks!
    – quickgame
    Oct 16, 2020 at 3:47
  • 1
    via UI: Ctrl-R, OFF64 and "use imagebase as offser base"
    – Igor Skochinsky
    Oct 22, 2020 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.