FakePDB icon indicating copy to clipboard operation
FakePDB copied to clipboard

add debug section to exe

Open Trass3r opened this issue 5 years ago • 7 comments

referencing the newly created pdb.

Trass3r avatar Jan 11 '20 14:01 Trass3r

a possible way todo this would be to store a kind of template debug section for both x86 and x64 targets and configure it as necessary on the fly before writing it to the dll/exe

jfm535 avatar Jul 21 '21 19:07 jfm535

typedef struct _IMAGE_DEBUG_DIRECTORY {
  DWORD Characteristics;
  DWORD TimeDateStamp;
  WORD  MajorVersion;
  WORD  MinorVersion;
  DWORD Type;
  DWORD SizeOfData;
  DWORD AddressOfRawData;
  DWORD PointerToRawData;
} IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY;

this seems to be the format for a debug directory entry

struct CV_INFO_PDB70
{
  DWORD  CvSignature;
  GUID Signature;
  DWORD Age;
  BYTE PdbFileName[];
} ;

And this seems to be the format of the inner debug data. source used: http://beefchunk.com/documentation/sys-programming/os-win32/debug/www.debuginfo.com/articles/debuginfomatch.html

jfm535 avatar Jul 21 '21 20:07 jfm535

Adding to this:

Some of the executables I created a fake .pdb for with IDA labels have the Debug Directory stripped. In this case, it would need to artificially be added to the executable. At the same time, if a fake random PDB id is generated, it would have to be patched in the executable as well.

P.S.: If you know any PE Editors that let you add the Debug Directory on the fly, do let me know.

sunbeam906 avatar Aug 03 '21 12:08 sunbeam906

Sadly I dont know of any pe editors that expose that functionality. A possibly simple outcome would be to expand the executable's last section a certain number of bytes or add a new section at the end of the executable.

jfmherokiller avatar Aug 05 '21 18:08 jfmherokiller

I've sorted it out.

  1. To "create" the Debug Directory, you need to find a spot where to write 0x1C bytes. I recommend using an existing section, at the end of it, rather than creating a new empty section. Why.. because PE Editors, as well as tools that parse the exe will not find the RSDS data (Debug Directory) information if an empty section, last section of the executable, and at the end of the section.

I planted it as I saw in most executables, at the end of .rdata section:

image

image

  1. Then patch the Debug Directory RVA and Size in your PE editor (I use CFF Explorer):

image

Size is always 0x1C bytes.

  1. Then patch in the Debug Directory information:

image

  1. Then patch in the RSDS information (in my case AddressOfRawData points to where this should be; I put it close by so it's continguous -> 000A6200 + 400000 = 4A6200):

image

SizeOfData represents the whole block + null terminator. The RSDS section has to have the first 4 characters as 'RSDS', then GUID as the next 16 bytes, then Age as DWORD (01 00 00 00) and lastly the path to the pdb. I chose to use the pdb name directly, no path given. The GUID is also something random (well, a GUID I borrowed from another pdb).

Once you've done all this, ONLY THEN open the executable in IDA and dump the fake .pdb. WHY? Because if you've done your work right, this will happen:

FakePDB/generate pdb (with function labels):
    * generating JSON: C:\Users\SunBeam\Desktop\debug\lithtech.exe.json
    * generating PDB: C:\Users\SunBeam\Desktop\debug\lithtech.pdb
    * symserv EXE id: b'3AE746D9C7000'
    * symserv PDB id: b'3810F85E542A40B394ABED5EDF1399C31'
    * done

Instead of:

FakePDB/generate pdb (with function labels):
    * generating JSON: F:\Games\NOLF\lithtech.exe.json
    * generating PDB: F:\Games\NOLF\lithtech.pdb
    * symserv EXE id: b'3AE746D9C7000'
    * symserv PDB id: b'00000000000000000000000'
    * done

Notice how, without a Debug Directory, the symserv PDB id is 0. If you patch your executable beforehand, then FakePDB will read that information and use it when generating the PDB ;)

I've opened a ticket here thinking it was an x64dbg issue not being able to load the .pdb. The x64dbg author mentioned in there that you can force the pdb to be loaded by editing the x32dbg/x64dbg .ini to force load the pdb. Might work without all the work I mentioned above.. to just load the .pdb in.. Thought it'd be interesting for you to read up on it :)

BR, Sun

sunbeam906 avatar Aug 05 '21 18:08 sunbeam906

P.S.: If you know any PE Editors that let you add the Debug Directory on the fly, do let me know.

There is none but CFF Explorer is scriptable, here's a starting point:

filename = GetOpenFile()
pehandle = OpenFile(filename)

local random = math.random
local function uuid()
    local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
    return string.gsub(template, '[xy]', function (c)
        local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
        return string.format('%x', v)
    end)
end
math.randomseed(os.time())
function string.random(length)
	local randomString = ''
	for i = 1, length do
		randomString = randomString .. string.char(math.random(0, 255))
	end
	return randomString
end

data = "RSDS" .. string.random(16) .. string.char(1, 0, 0, 0) .. "executablenamebuffer.pdb"
AddSectionWithData(pehandle, data, ".debug", IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA)

Trass3r avatar Aug 30 '21 13:08 Trass3r

As this page points out: https://lordjeb.com/2023/03/10/how-the-hell-things-work-how-windows-debugger-finds-symbols-for-your-code/
The debugger (WindDbg et al) will actually look first for a GUID that is made up of the module timestamp plus the size of the module (which incidentally makes smaller GUID).

So if the module doesn't have a debug section, the debugger will look for the short GUID version,

If you manually add a module pdb to you're symbol store doing:
symstore.exe add /f MyModule.pdb /s C:\ProgramData\Dbg\sym /t AnyName
("C:\ProgramData\Dbg\sym" is the default WinDbg symbol store location, yours might be different.)
It should figure automatically make the shorter GUID version when it sees it doesn't have the other.
Worth a try.

If the tool doesn't. Then you can manually create it using a script.

At any rate do:

!sym noisy
.reload /i /v /f MyModule.dll

It will show the search for the PDB and you'll see the problem if any.

kweatherman avatar Jul 19 '23 21:07 kweatherman