ApiMapSet – deroko of ARTeam

=== Story in general ===

When windows 7 came out first time, most of us were surprised to see kernelbase.dll and all weird imports in system executables and system dlls with names as :

Most of us figured that those are used to redirect imports to kernelbase.dll, but funny thing is that no body really went into details of investigating it. Yes, we all figured that this is thunking or redirection to different dlls but no body went in details how it’s done. This is to me somehow strange, as I’m quite sure that 3-4 years ago many top reversers would be on this. Not because it’s security risk, or has something unique, but for sake of challenge of investigating something undocumented. This has been on my todo list for a while, and I’m really surprised that no body actually took this as a challenge, just for fun.

Here we go with all details of implementation.

=== Where is API-MS* compare? ===

First of all, in PEB there is field called PEB.ApiMapSet which is located at offset 0x38 from PEB, which points to some data (we will come to this later).
Second, function which identifies dlls, and makes all these redirections is called _ApiSetResolveToHost. One of the arguments that is being passed in is PEB.ApiMapSet:

Function checks if the first 4 characters are “API-“, and if so will proceed with identifying dll that should be redirected, otherwise, nothing happens and function simply exits. So we continue our quest:

Function, simply skips 8 bytes of original string, as those are unicode “API-“ and UNICODE_STRING cares only about length in bytes, also function checks if there is “.” at the end of dll name to strip it, as ApiMapSet doesn’t contained “.dll” extension in names.

Before I start explaining how everything is organized, and how dll name is found, I will show you binary representation of this data, so we can reference to it, and explain how everything is done. Dump is intentionally set to be with base 0x00000000 so it’s easier to follow, as all offsets inside of this binary data are relative to start of PEB.ApiMapSet.

Now starts code which is dealing with searching and mapping dll names:

At offset “[ecx+4]” or “PEB.ApiMapSet” is stored number of structures which is located at “0x00000004” and is set to be “0x23” or “35” decimal. Code checks immediately if number of structs is 0, if so, code exits with error. Also at offset “0x00000000” there is number “2” for which I assume is version of ApiMapSet, as I haven’t found that it’s used, I can only assume that this is version of structures so in the future code can be changed for future needs, and still keep backward compatibility. Yup, you have to like Microsoft because of backward compatibility, which is probably one of the things that make windows great operating system.

From this code we can figure that struct which follows first 8 bytes of PEB.ApiMapSet are 0xC bytes wide. We can see also layout of these structures:

So we examine 1st structure at offset 0x00000008

0x00000008” tells us that dll name is located at “0x000001AC”
“0x0000000C” tells us that dll name is 0x34 bytes long

this sounds correct? Now that we have found our matched dll, we can go and extract real dll name:

3rd DWORD bring us to more structures which point to name of a real dll to which redirection is applied. In our case that would be offset at “0x00000010” and relative offset “0x1E0”:

We can document now first structure found at the beginning:

Everytime 2nd stage structure is found, code checks how many of them are there. So far I haven’t seen more then 1, but only future knows what will MS introduce, also code is aware of this posibility. If there is only one structure code is straight forward:

Now look at “0x000001E0” and you will see how code works. It skips “dwCount” at “77F252C6”, and gets from offset +8 relative offset to real dll name, and from offset +0x0C it gets length in bytes of dll name. If we follow this code we see that these values are:

“000001EC” = “0x000001F4”
“000001F0” = “0x00000018”

And at offset 0x000001F4 we can clearly see name “kernel32.dll” which is returned back after redirection is properly applied.

And we can document 2nd set of structures as :

You might be wondering about NameOffsetRealName and LengthRealName. Well those are used only when dwCount > 2, and these names are used to compare dll which is loaded, and name which is compared comes from “LDR_DATA_TABLE_ENTRY” of currently loaded dll. Code snippet:

Now we are in the code of function we previously looked at:

and inside of this function, pretty much same search is performed as you have seen earlier seen with RtlCompareUnicodeString except offsets ”’+0”’ and ”’+4”’ are used to get name offset and Length. And they are compared with DllBaseName passed to this function. Maybe this is only prepared for future version of windows. Lets see how they will use it in the future.

=== Where is PEB.ApiMapSet set? ===

If you have ever traced initialization of process you know where to start, and that’s “LdrInitializeThunk” as every created thread starts from there. And nowhere else! I’ve started my quest, and eventually after a while figured that this stuff definitely is not set in ntdll.dll, to verify this I wrote simple C program:

Woops, running this program gave me some address :

So second guess is that’s is either mapped by “NtCreateProcess/NtCreateProcessEx” or windows added extra shared user data memory. Logically, we need to check PEB creation from ntoskrnl.exe, and as always look for “MmCreatePeb” as that’s place where PEB is created for a new process, so any change to PEB will happen in this function, or after it. “MmCreatePeb” was no go, as obviously offset “0x38” wasn’t referenced from it, but after a little bit of looking after call to “MmCreatePeb” I found interesting name “_PspMapApiSetView@4” (heh next time just search symbols for whatever you are looking, much easier, right?), and critical code:

That’s where PEB.ApiMapSet is initialized, so it comes from kernel. Full function:

Note also 3 variables : “_PspApiSetMap”“_PspApiSetSize”, and “_PspApiSetOffset”. Follow references to them and you endup at : “PspInitializeApiSetMap()”.

This functions is kinda long, but very simple:
1. It opens file :

2. Creates section, gets object of section, and maps it to system memory, and section object stays there forever:

3. Goes through pe header until it finds section named “.apiset”

And sets global vars to VirtualAddress and VirtualSize of a section:

Now to make sure that we understood everything, open “C:\windows\system32\ApiSetSchema.dll”, look into section “.apiset” and you will see that it’s identical to stuff you see if you view “PEB.ApiMapSet”. Quest done.

=== Ending ===

I have no idea why did MS introduce this. Only reason to me is that somebody at Microsoft was bored, and somehow managed to make huge mess. Maybe they want to keep this so in the future, they can separate various parts of kernel32.dll or other dlls to differently named dlls, and “apisetschema.dll” is the one to fix this mess. Probably code for kernel32.dll and other dlls which survived this thunking became huge, so for easier maintance split it into more new dlls, and keep backward compatibility through ApiMapSet rules.

=== Greetings ===

I wish to say thank you to all my mates from ARTeam for being kewl guys and always sharing knowledge, to friendly people at woodmann.cjb.net for nice discussions, unpack.cn guys, and of course ex-29a virus writing group for sharing their knowledge.

=== Author ===
deroko of ARTeam
=== Source Code ===