0

I have a (C++) program where, in one of its dll's, the following is done:

if (m_Map.GetMaxValue() >= MAX_CLASSES) {

I have two binaries of this program (compiled with various versions of Visual Studio), one where MAX_CLASSES was #define'd to 50, and one where it was 75. These binaries were made from different branches of the code and the other functionality is different as well. What I need is a version of the binary where the MAX_CLASSES was defined as 50, except with the higher limit i.e. 75.

So a sane person would change the constant in the source code of the branch I need, rebuild and go home. But, building this software is complex because it's old, the dependencies and tooling are old, etc.; plus I have issues with building the installers, and data and so on. So, I thought, how about I just patch this binary so that this one constant is changed directly in the DLL. I have vague recollections of doing similar things in the 1990's for, eh, probably 'educational' purposes.

But times have changed and I barely remember doing it, let alone how I did things back then. I opened the DLL (one where the limit is set to 75, this is the binary I have at hand - I will have to re-do this as soon as I have the actual binary with the 50 limit, so the following references 75 i.e. 0x4b for illustrating the principle) in Ghidra and after some poking around, I found the following:

   18005160e 3c 4b           CMP        AL,0x4b
   180051610 0f 82 19        JC         LAB_18005172f
             01 00 00

Which in the decompiler window I could link back to

if (bVar3 < 0x4b)

and some operations after that that I can map to the source code of the function I have.

Now my questions are:

  • how do I interpret the values above (the Ghidra output) wrt to the binary layout of the dll? When I hover over the first column value ('18005160e') in Ghidra, I get values for 'imagebase offset', 'memory block offset', 'function offset' and 'byte source offset'. Is this 'byte source offset' the physical address from the start of the dll where these instructions start? The actual value in this hover balloon is 50a0eh - is that Ghidra's notation for 0x50a0e ? I.e. does the trailing 'h' denote 'hex'?

  • I then tried to open the dll in a regular hex editor ('Hex Editor Neo' which I like to use to view/edit binary data files), and went to offset 0x50a0e, and looked for the values '3c 4b' around there which I didn't find. I searched for this byte sequence in the whole file, and found 7 occurrences, none of which are around 0x50a0e, leading me to think I'm misinterpreting Ghidra's 'byte source offset' here.

  • how do I make a 'patcher' for this? I would think what I need is a program that only does

    FILE* fh = fopen('mydll.dll); fseek(fh, 0x[magic constant]); fwrite(fh, 0x4b); fclose(fh);

where '0x[magic constant]' is hopefully just the value I got from Ghidra in 'byte source offset'? Or is there anything else I need to consider here? Is there any software tool that can generate a patcher program?

Thanks.

Roel
  • 19,338
  • 6
  • 61
  • 90
  • You found 7 occurrences of 3c 4b; look for one followed by 0f 82. – prl Aug 10 '22 at 15:40
  • I have a patcher program that does exactly what you suggest (using command line parameters for the file name, offset, and values, of course). It's absolutely the best way to do this if you need to do it more than once. It doesn't help with determining the file offset, though. – prl Aug 10 '22 at 15:45
  • Don't forget that the compiler could change ">= 50" to "> 49". You have to look at the surrounding code to be sure you've found the right place. – prl Aug 10 '22 at 15:47
  • Thanks - what program do you use for patching? Sounds like exactly what I need. Wrt to the offset: it turned out that I was looking at two different builds of the dll (the build process is very complex...) and after fixing all of that, all offsets now line up and my assumption in my question are right after all (i.e., in the hex editor I can jump to the Ghidra offset, and find the constant at exactly the address I expected). – Roel Aug 10 '22 at 16:08

1 Answers1

2

18005160e is a VA, a Virtual Address.

It is the sum of a Base Address (most likely 180000000) and an RVA, a Relative Virtual Address.

Find the Base Address of the DLL with any PE inspecting tool (e.g. CFF Explorer) or Ghidra itself.

Subtract the base address from 18005160e to the RVA. Let's say the result is 5160e.

Now you need to find which section this RVA lies in. Again use an PE inspecting tool to find the list of the sections and their RVA/Virtual start and RVA/Virtual size.
Say the RVA lies in the .text section with start at the RVA 1000.

Subtract this start RVA from the result above: 5160e - 1000 = 4160e.
This is the offset of the instruction in the .text section.

To find the offset in the file, just add the raw/offset start of the section (again you can find this with a PE inspecting tool). Say the .text section starts at the offset 400, then 4160e + 400 = 41a0e is the offset corresponding to the VA 18005160e.

This is all PE 101.

Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
  • Thanks. To confirm, the terms you use here is the PE terminology, right? So what in PE parlance is 'Relative Virtual Address', Ghidra calls 'Imagebase Offset', and the 'offset in the instruction in the .text section' (which doesn't have a proper name in PE?) is 'Memory Block Offset'? – Roel Aug 10 '22 at 16:32
  • @Roel RVA, VA, Raw address, Raw size and Virtual size is PE terminology, but each tool uses slightly different words. I don't know Ghidra by memory, I'll need the context to understand its terminology. My advice: Use CFF Explorer. It's free, small and doesn't need to be installed, look at the sections and when your patcher is done you can just delete it. IRRC it also has a tool for converting a VA to an offset automatically. – Margaret Bloom Aug 10 '22 at 16:42