Negated structure offsets

A month ago I received a support request:
If I have an instruction like

     mov eax, [edi-0ch]

and I know that that’s really the sum of an offset to a structure not
at edi and the offset of a member within that structure, how do I get
IDA to display it as such without using a manual operand?
A legitimate question, which is somewhat hard to answer.

To understand what’s going on, let’s draw a picture:

       +0x000 Name                 : char[8]
       +0x008 Misc                 : __unnamed
       +0x00c VirtualAddress       : DWORD
       +0x010 SizeOfRawData        : DWORD
edi--> +0x014 PointerToRawData     : DWORD
       +0x018 PointerToRelocations : DWORD
       +0x01c PointerToLinenumbers : DWORD
       +0x020 NumberOfRelocations  : DWORD
       +0x022 NumberOfLinenumbers  : DWORD
       +0x024 Characteristics      : DWORD

As we see, edi points to the middle of the structure. If we subtract 0xC from it,
we end up with the Misc field, which is a union.

Pressing T to convert the operand to
a struct offset
does not help. By default, this command assumes that the offsets are calculated from the beginning
of the structure. We need a more powerful form of this command which would allow us to specify the offset
deltas. We can do that by making a selection with the mouse before pressing T: in this case
another, more powerful dialog form will appear.

We enter 0x14 as the delta value, select the desired structure type (IMAGE_SECTION_HEADER),
and (since it is a union) the exact union field we want to see (say, VirtualSize). Here is the result:

  mov     eax, dword ptr 

Alas, this is not what we want. IDA took our request to have 0x14 as the delta too literally.
The delta 0x14 is present in the operand, but the whole expression is subtracted from edi.
In fact, we wanted the expression (-0x0C) to be converted into another expression that
is not subtracted but added to edi. We did not tell IDA about it and it
represented things exactly the way we asked: take 0x0C and convert it into a structure offset.
If we tell IDA that we want to change the sign of the expression (the hotkey is ‘_‘, underscore),
we get the desired result:

  mov     eax, [edi+(IMAGE_SECTION_HEADER.Misc.VirtualSize-14h)]

To summarise, all we had to do is this:

  • invert the operand sign by pressing _ (underscore)
  • select the instruction
  • press T. delta is 0x14, select the desired structure and its field

As you see, you can combine operand negation (and bitwise negation too) with other operand types.
This trick might help when you need to convert an operand to a struct offset
or a symbolic constant or a character constant but the number must be inverted first.