30

I have compiled following C source code in VS2010 console project.

#include <stdio.h>
int main(int argc, char* argv[]){
    printf("hello world\n");
    return 0;
}

then I used /MT option for release mode to statically link the C-runtime library. However, as far as I know, C-runtime library still invokes lower level system functions - for example, C-runtime function printf eventually calls WriteFile Windows API.

And the actual function body of WriteFile is in kernel32.dll. So, even if I link the C-runtime library statically, the binary doesn't contain the entire routine including the SYSENTER, or INT 0x2E instructions... The core part is still in a DLL. The following diagram describes how I understand it:

enter image description here

What I want is to statically link EVERYTHING into single EXE file. Including kernel32.dll, user32.dll to eliminate the necessity of loader parsing the IAT and resolving the function names.

The following picture describes what I want:

enter image description here

I understand this is simple in Linux with gcc. All I have to do is give the option -static

Is there any option like this in VS2010? Please correct me if I'm misunderstanding.

5
  • 3
    The answer is clearly "no". You would have to write your own OS specific syscall layer. The question is: why don't you want to use a standard CRT? To what advantage?
    – newgre
    Commented May 22, 2013 at 8:37
  • 1
    thank you. 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.
    – daehee
    Commented May 22, 2013 at 10:42
  • 1
    @daehee: that gives your question a whole different angle, you see.
    – 0xC0000022L
    Commented May 22, 2013 at 16:39
  • 2
    Your description is correct, overall, but you need to add yet another layer, the NT native api: fread(msvcrt) -> ReadFile(kernel32) -> NtReadFile(ntdll.dll) -> kernel Commented May 23, 2013 at 8:34
  • 1
    With enough effort you might be able to do inline windows functions, but then you're bound to that specific windows version, and you'd probably violate MS's copyright. Commented Sep 20, 2013 at 10:43

4 Answers 4

20

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 (csrss.exe).


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:

  1. 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.
  2. 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.
  3. 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 ?? ?? ?? ??

where 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.

1
  • 1
    actually, the only import that you need is GetProcAddress, because you can GetProcAddress("LoadLibrary") and go from there. Commented May 23, 2013 at 15:44
17

The Windows kernel, unlike Linux or OS X, does not use consistent syscall numbering across versions. The numbers can change even after a servicepack release. For example, the NtReadFile syscall was 0x0086 on Windows NT 4 but on Windows 7 it's 0x0111 (see here for the full list). That's why all proper programs use the kernel32.dll (or ntdll.dll) to perform the actual call - these DLLs are guaranteed to use the syscall numbers matching the kernel.

By the way, you won't save anything by not listing kernel32.dll in your IAT - it's always mapped into Win32 processes by the system loader (starting from Windows 2000 IIRC).

2
  • I thought Windows syscall numbering was same as Linux. thank you for the information, it helps my understanding a lot!
    – daehee
    Commented May 22, 2013 at 10:39
  • @daehee: it changes every once in a while. It's the reason why file lists the kernel version for ELF files.
    – 0xC0000022L
    Commented Sep 18, 2013 at 16:51
2

Probably with full intent from MS, not only does syscall numbering change between versions, but also many DLL ordinal values do, too. Need to bind to Win32 and use the full function name if you want your code to work across OS releases.

1

Other people have mentioned the downsides of this, but if you're still interested in this path, then here's a lib that converts the ntdll API names into syscalls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.