1

I'm pretty new to assembly, I'm trying to understand things better.

My goal is to change the default text colour of controls in a win32 x86 application, these are stored as RGB values for different states of elements (like normal, hovered, disabled).

For most things this is pretty simple as I can just change the RGB value directly to a new RGB value. But for elements that use the colour #000000, the assembly seems to use some optimization whereby it's not stored as '000000', it's stored as '00'. So I can't just change this to a different RGB value as there's no room for it.

Below is an example of what I'm working with, this is the color of checkbox controls. puVar4 is the color of the text for a checkbox, local_14c is an X position of an image sprite for multiple states of a checkbox (0 is the normal state, 0xe is active, 0x1c is disabled).

The disabled text color is 0xc0c0c0, the active state is 0x5645be, the normal state is 0x000000 but represented as 0x0 using what I assume is an optimization XOR EDI, EDI which assigns 0.

1

So I know I need to change XOR EDI, EDI but I need room for the change, and presumably the only way I can do this is to JMP to an empty area in memory to perform the change, and then JMP back to the next instruction? My process of attempting this is as follows:

I move the TEST instruction up to where the XOR was, and NOP the 2 bytes excess along with the MOV instruction. Then at the first NOP position I insert a JMP, this leaves me with 3 excess NOP.

2

I JMP to 0x4002a9 which is near the start of the assembly and seems to be a large area of nothing so I think it's okay to use. I need to re-add those instructions I NOP'd, where I can assign a RGB value to EDI, I assign the color red with 0xff0000. For some reason, Ghidra doesn't allow me to add instructions with the label local_14c in these new instructions, so I change it to 0xfffffeb8 (the instruction info of the original MOV shows this is what local_14c refers to so I assume it's correct). I then JMP back to the next instruction proceeding which was the JZ

The decompiled code seems to show my changes are without error.

3

And here is the instruction that the JMP leads back to, which is after the area I NOP'd

4

When I export these changes and attempt to run the patched application, it crashes on startup. Is there something I'm missing? Any guidance on where to go from here would be most appreciated!

Update:

From what Luke mentioned, I now understand that I can only insert instructions into areas of memory that are executable.

From the below with this in mind, it seems I'd only be able to add new instructions within the .text block. (Though I assume I could also create new executable blocks if desired?)

5

I started over without the changes done above, I see that there's some space below the function that I'm making changes in.

6

I think the db 6h lines are related to switch statements in the function, so I'm not touching those. A varying number of ?? CCh lines are present at the end of every function so I assume these are free to use.

There are only 14 of these for the function I'm working with which isn't enough to make all the patches I did above, so I'll only change the local_14c variable for testing.

7

So basically, all I've done here is replace where local_14c was assigned with a JMP to the end of the function where it is then assigned instead, and then JMP back to the next instruction.. which should work fine?

8

But nope, it's the same issue

Here is the error output from WinDbg

(70f8.4604): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=02ea7470 ebx=00b90c52 ecx=eb080000 edx=00000000 esi=02e60450 edi=00000000 eip=0000d302 esp=00afecc0 ebp=00afee6c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246 0000d302 ?? ??? 0:000> g

And when I resume, it's the same thing:

(70f8.4604): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=4dff1590 ebx=007e08ba ecx=eb080000 edx=00000000 esi=02e60450 edi=00000000 eip=0000d302 esp=00afecc0 ebp=00afee6c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246 0000d302 ?? ??? 0:000> g

I think this is because the application is actually handling the error, as there is an error prompt that shows, but it does not show any text, I assume the application is in a hanged state before the text can show.

iegrm
  • 57
  • 5
  • Did they already fix the export option in ghidra? It used to not produce correct binaries when exporting. – Paweł Łukasik Apr 08 '23 at 10:23
  • I've been able to successfully export without issue when making changes to instructions that don't require jumping, so I don't think it's an issue with Ghidra? I've tried doing an "empty jump" of sorts where I just JMP to empty space at end of function with a NOP, and then JMP to next instruction and this causes crash, so I'm not too sure. – iegrm Apr 08 '23 at 11:01
  • 1
    Can you provide the error diagnostics when starting the patched executable, ideally running under a debugger (e.g. [WinDbg Preview](https://apps.microsoft.com/store/detail/windbg-preview/9PGJGD53TN86)). – IInspectable Apr 08 '23 at 18:10
  • I followed your instructions, here's the error produced: (71ac.56e8): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00c87470 ebx=00a70ab8 ecx=c6b30000 edx=00000000 esi=006c0450 edi=0000000f eip=007002a9 esp=0053e960 ebp=0053eb0c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246 windapp+0x2a9: 007002a9 bf0000ff00 mov edi,0FF0000h – iegrm Apr 08 '23 at 19:27
  • 1
    Please add the information [to the question](https://stackoverflow.com/posts/75964283/edit). Also, as the debugger output tells you, a first chance exception *"may be expected and handled"*. You'll need to keep going until you hit a second chance exception (see [Structured Exception Handling](https://learn.microsoft.com/en-us/windows/win32/debug/structured-exception-handling)). Though I guess the assumption that there were *"a large area of nothing so I think it's okay to use"* is fairly optimistic. – IInspectable Apr 09 '23 at 07:20
  • 1
    It looks like you're inserting code into unused space in the PE header which is not executable. – Luke Apr 09 '23 at 08:20
  • With both of your comments in mind, I've updated my question under the "Update" section. I'm unable to get a "second chance exception", it only repeats the first chance exception. – iegrm Apr 09 '23 at 19:10
  • 1
    Your JMP opcode is truncating EIP to 16 bits. – Luke Apr 09 '23 at 21:33
  • Okay I did a bit of reading on EIP, it's a register which points to the address of the next instruction. But I don't really understand how my JMP would be truncating it, could you provide more detail? Thanks! – iegrm Apr 10 '23 at 04:37
  • 1
    The `66h` operand-size prefix sets the operand-size of the `jmp` to 16-bit, making it truncate EIP to IP. https://www.felixcloutier.com/x86/jmp - "*If the operand-size attribute is 16, the upper two bytes of the EIP register are cleared, resulting in a maximum instruction pointer size of 16 bits.*" (This is unlike GP integer instructions where `add ax, bx` would use a 66h prefix to encode 16-bit operand-size, but would leave the upper 16 bits of EAX unmodified.) – Peter Cordes Apr 10 '23 at 08:26

0 Answers0