Let me start by telling you that what you want would be impossible, because of how well-known DLLs work. You can attempt something similar with tools like PEBundle or dllpackager, but that will usually (I'd say certainly) fail with the well-known DLLs (such as system DLLs as well as even the MSVC runtime DLLs in their different incarnations). See this and this on the relevance and meaning of well-known DLLs.
kernel32.dll plays a very special role in in the Win32 subsystem in that it helps to register Win32 threads and processes with the subsystem (
Answering the part from the comment of the OP on the question:
in fact, I was not looking for performance advantage. I thought, in this way, I can remove every symbols just like Linux stripped binary and make the reversing harder.
There is no point in doing it this way then. You could still only import a single function and use a convoluted way of importing DLLs and/or resolving functions. I.e. concealing which functions you are importing from which DLLs. One thing that is rather popular in hacker circles is to hash the exported function names and then walk the exports of the loaded image yourself, hashing each of the function names found and comparing with the known hashed values.
Here's a good paper on one method used for what you want, because shell code has no clue about imported function addresses in a hijacked process.
As Igor pointed out
kernel32.dll will be loaded into the process and AFAIR the order of that has changed as well with Vista (previously
ntdll.dll was the first one in the PEB's DLL list, aka
LoaderData). So the exact method has been laid out in above paper.
A few more points:
- if you don't want to use
LoadLibrary (or its
ntdll.dll counterpart) to dynamically load the DLLs, you can keep a reference to a single imported function in the IAT - this is how some executable packers do it.
- if not, start by resolving
LoadLibraryA, loading the DLLs you want and then using the resolved
GetProcAddress (or your own method used already on
kernel32.dll and outlined in the paper) to load more functions.
- you may be making your life harder while not making it noticeably harder to a skilled/experienced reverse engineer. Most of them will have seen a similar scheme ;) ... dynamic analysis will easily reveal your tricks and enable a reverse engineer to work around them.
As an alternative you could resort to the system call numbers by writing a simplified disassembler that is able to pick out the index into the SSDT (system service descriptor table) and then you do the rest yourself. This has been documented long ago because it is how people used to find the index into the SSDT when they wanted to hook it from within a kernel mode driver. Roughly, if you have the pointer to the function in
ntdll.dll to which you need the SSDT index, you'd check your assumptions and then retrieve the appropriate value. In Windows NT 4 through 2003 (32 bit) this would look like
B8 ?? ?? ?? ??
B8 is for
mov eax, ???????? and the question marks are the index into the SSDT. So after checking for the
B8 you'd skip over it and fetch the next DWORD. Example in C code:
if ((lpAddr) && *((unsigned char *)lpAddr) == 0xB8)
result = *((ULONG *)((unsigned char *)lpAddr+1));
Things will be different on different operating system versions and depending on the bitness - you have been warned.
But I don't see any advantage - neither performance-wise nor in deterring reverse engineering efforts.