I have a program which loads DLLs and I need to call one of the non-exported functions it contains. Is there any way I can do this, via searching in a debugger or otherwise? Before anyone asks, yes I have the prototypes and stuff for the functions.
I'm afraid there are no "safe" way to do so if referred library does not explicitly export its object (class/func). Because you will have no idea where is the required object mapped in code memory.
However, by using RE tools, you can find offset for interested object within the library, then add it to any known exported object address to obtain the "real" memory location. After that, prepare a function prototype etc and cast into your local structure for usage.
Yes there is, at least sort of, but it isn't a good idea.
In C/C++ all a function pointer is, is an address in memory. So if you somehow where able to find the address of this function you could call it.
Let me ask some questions though, how do you know this DLL contains this function? Do you have the source code? Otherwise I don't know how you could no for certain this function exists or if it is safe to call. But if you have the source code, then just expose the function. If the DLL writer didn't expose this function, they never expect you to call it and can change/remove the implementation at any time.
Warnings aside, you can find the function address if you have debug symbols or a MAP file you can find the offset in the DLL. If you don't have anything but the DLL, then there is no way to know where that function exists in the DLL - it is not stored in the DLL itself.
Once you have the offset you can then insert that into the code like so:
const DWORD_PTR funcOffset = 0xDEADBEEF;
typedef void (UnExportedFunc)();
....
void CallUnExportedFunc() {
// This will get the DLL base address (which can vary)
HMODULE hMod = GetModuleHandle("My.dll");
// Calcualte the acutal address
DWORD_PTR funcAddress = (DWORD_PTR)hMod + funcOffset;
// Cast the address to a function poniter
UnExportedFunc func = (UnExportedFunc)funcAddress;
// Call the function
func();
}
Also realize that the offset of this function WILL CHANGE EVERY TIME the DLL is rebuilt so this is very fragile and let me say again, not a good idea.
If the function you want isn't exported, then it won't be in the export address table. Assuming Visual Studio was used to produce this DLL and you have its associated PDB (program database) file, then you can use Microsoft's DIA (debug interface access) APIs to locate the desired function either by name or, approximately, by signature.
Once you have the function (symbol) from the PDB, you will also have its RVA (relative virtual address). You can add the RVA to the loaded module's base address to determine the absolute virtual address in memory where the function is stored. Then, you can make a function call through that address.
Alternatively, if this is just a one-off thing that you need to do (i.e. you don't need a programmatic solution), you can use windbg.exe in the Debugging Tools for Windows toolkit to attach to your process and discover the address of the function you care about. In WinDbg, you can use the x
command to "examine symbols" in a module.
For example, you can do x mymodule!*foo*
to see all functions whose name contains "foo". As long as you have symbols (PDB) loaded for your module, this will show you the non-export functions as well. Use .hh x
to get help on the x
command.
The most general way to do this (and it's still a bad idea, as everyone else pointed out already) is to scan the DLL code at runtime after it's loaded, and look for a known, unique section of code in that function, and then use code similar to that in shf301's answer to call it. If you know that the DLL won't ever change, than any solution based on determining the offset in the DLL should work.
To find that unique section of code, disassemble the DLL using a disassembler that can show you the machine code in addition to the assembly language mnemonics (I can't think of anything that won't do that) and watch out for call and jmp instructions.
I actually had to do something similar once to apply a binary patch to a DOS exe; it was a bug fix, and the code wasn't under revision control so that was the only way to fix it.
I'd be really curious to know why you need this, by the way.