Log in

View Full Version : Windows CSRSS cross-version API Table


j00ru vx tech blog
May 2nd, 2010, 19:17
Hello!

It seems like half a year has passed since I published the Win32k.SYS system call table ("http://j00ru.vexillium.org/?p=257") list on the net. During this time (well, it didn’t take so long http://j00ru.vexillium.org/wp-includes/smilies/icon_wink.gif ) I managed to gather enough information to release yet another API list – this time, concerning an user-mode application – CSRSS (Client/Server Runtime SubSystem). As a relatively common research subject, I think a table of this kind can make things easier for lots of people.

Before presenting the table itself, I would like to gently introduce the mechanism in consideration to the reader. As the name itself states, CSRSS is a part of the Windows Environment Subsystem, running in user-mode. It is a single process (having the highest possible – SYSTEM – privileges), which mostly takes advantage of three dynamic libraries – basesrv.dll, csrsrv.dll and winsrv.dll. These files provide support for certain parts of the subsystem functionality, such as:



Updating the list of processes / threads running on the system
Handling the Console Window (i.e. special text-mode window) events
Implementing parts of the Virtual DOS Machine support
Supplying miscellaneous functions, such as ExitWindowsEx ("http://msdn.microsoft.com/en-us/library/aa376868(VS.85).aspx")

Every Windows process running on the system does (or, at least, should) have an open connection with CSRSS, through the LPC / ALPC mechanism (depending on the system version) – which in turn stands for (Advanced) Local Procedure Calls. The ntdll.dll module provides multiple functions dedicated to the data exchange between user processes and CSRSS. Some of the examplary, exported names include, but are not limited to:



CsrClientConnectToServer
CsrGetProcessId
CsrClientCallServer
CsrAllocateMessageBuffer

Out of all the Csr~ wrapper functions, CsrClientCallServer is the most commonly used. One can find it’s references in kernel32.CreateProcess ("http://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx"), kernel32.AllocConsole ("http://msdn.microsoft.com/en-us/library/ms681944(VS.85).aspx"), kernel32.FreeConsole ("http://msdn.microsoft.com/en-us/library/ms681944(VS.85).aspx"), user32.EndTask ("http://msdn.microsoft.com/en-us/library/ms633492(VS.85).aspx") and tens of other documented API functions. At a closer look, it is easy to notice that each time a call is made to CsrClientCallServer, an unique number is pushed on the stack, differing from routine to routine. An exemplary code snippet follows:

Code:
.text:77E96D55 push 4
.text:77E96D57 push 20225h <---------- HERE
.text:77E96D5C mov [ebp+var_7C], eax
.text:77E96D5F push 0
.text:77E96D61 lea eax, [ebp+var_A4]
.text:77E96D67 push eax
.text:77E96D68 call ds:__imp__CsrClientCallServer@16 ; CsrClientCallServer(x,x,x,x)


As it turns out, these numbers are in fact indexes into special function pointer tables defined by the aforementioned libraries used by CSRSS. More specifically, a special routine – internally called CsrApiRequestThread – running in the context of a separate csrss.exe thread, is responsible for receiving user requests (that is – the CsrApi ID value together with the input buffer), handling it through appropriate dispatch tables, and returning the results. This scheme is slightly different on Windows 7, but the general idea remains the same.

In order to give the reader a better view of how many and what functions are supported on a specific OS version, as well as make cross-version comparisons easier, I’ve created two versions of the CsrAPI table:

1. A complete list of the functions present in the dispatch tables for most likely every NT-series system can be found @ http://j00ru.vexillium.org/csrss_list/api_list.html.
2. A cross-version compatibility table, for the same system version set can be found @ http://j00ru.vexillium.org/csrss_list/api_table.html.

I have done my best to make sure that the presented materials are correct and up-to-date. If, however, a mistake of any kinds is noticed, please let me know about this fact asap It is possible that I will manage to fill the red-green table with corresponding api-numbers soon – I cannot guarantee this, though.

From this point, I would like to thank all people who showed their interest and helped my with this tiny project – Thank You!
Also, please drop me a line on whether you like the idea or not


http://j00ru.vexillium.org/?p=349&lang=en

Indy
May 2nd, 2010, 20:39
j00ru
Hardkod (job table as f(Version) a bad way). It should look for an alternative to searching for a dynamic, otherwise in future versions of the system or in the package code may not be working.
Regarding the service subsystem - make better use of parsing api, this parsing code using a disassembler lengths and retrieval of SIGN/ID, or more complex, but versatile and desirable to use an analysis of the graph (and its construction means a third party engine).

Kayaker
May 2nd, 2010, 21:06
Nice work on another definitive reference j00ru. Yes, the CsrApi ID's would complete the tables and could be used as the key to intercepting the higher level API's (for hack/undocumented purposes). For example, through CsrClientCallServer and the LPC message system hooking as I discussed a bit in this thread, showing one possible way of intercepting both process and thread creation at a very early stage. Unfortunately I only parsed out a small subset of api numbers from kernel32.dll, but I'd be willing to dig around a bit more.

http://www.woodmann.com/forum/showthread.php?12679-Thread-Based-Code-behavior-Profiler

Kayaker
May 2nd, 2010, 23:50
Following up on this a bit, the following defines from the Windows Research Kernel should simplify calculating the relevant CsrApi ID's from the individual CSRSS libraries.

Code:

#define CSRSRV_SERVERDLL_INDEX 0

#define BASESRV_SERVERDLL_INDEX 1

#define CONSRV_SERVERDLL_INDEX 2

#define USERSRV_SERVERDLL_INDEX 3

#define CSR_MAKE_API_NUMBER( DllIndex, ApiIndex ) \
(CSR_API_NUMBER)(((DllIndex) << 16) | (ApiIndex))



Using the example of BaseSrvExitProcess from basesrv.dll, CSR_API_NUMBER = 0x10003

Code:

.data:75B5D080 _BaseServerApiDispatchTable
.data:75B5D080 dd offset _BaseSrvCreateProcess@8
.data:75B5D084 dd offset _BaseSrvCreateThread@8
.data:75B5D088 dd offset _BaseSrvGetTempFile@8
.data:75B5D08C dd offset _BaseSrvExitProcess@8
...


Which is confirmed from kernel32.dll:

Code:

:7C81CA6C __ExitProcess@4 proc near
...
:7C81CAB4 push 4
:7C81CAB6 push 10003h
:7C81CABB push ebx
:7C81CABC lea eax, [ebp+var_DC]
:7C81CAC2 push eax
:7C81CAC3 call ds:__imp__CsrClientCallServer@16



As Indy alludes to, a dynamic, version independant method of finding the unexported *ServerApiDispatchTable in each of the libraries would be nice. In XP the dispatch table is at offset 24h of a structure passed as arg0 of the exported DllInitialization functions for each of the cases. I haven't found a structure definition yet, but it appears to be a CsrHeap allocation.

Code:

ServerDllInitialization (basesrv.dll)
:75B5338C mov dword ptr [edi+24h], offset _BaseServerApiDispatchTable

ConServerDllInitialization (winsrv.dll)
:75B688EE mov dword ptr [eax+24h], offset _ConsoleServerApiDispatchTable

UserServerDllInitialization (winsrv.dll)
:75B68536 mov dword ptr [eax+24h], offset _UserServerApiDispatchTable

CsrServerDllInitialization (csrsrv.dll)
:75B41EF1 mov dword ptr [eax+24h], offset _CsrServerApiDispatchTable



EDIT: The structure might be similar to this from ReactOS /subsys/csr/server.h. Note the pointer to the Dispatch Table at the correct offset as well as other matches of structure use from ServerDllInitialization and CsrLoadServerDll (csrsrv.dll).

Code:

typedef struct _CSR_SERVER_DLL
{
ULONG Length;
HANDLE Event;
ANSI_STRING Name;
HANDLE ServerHandle;
ULONG ServerId;
ULONG Unknown;
ULONG ApiBase;
ULONG HighestApiSupported;
PCSR_API_ROUTINE *DispatchTable;
PBOOLEAN ValidTable;
PCHAR *NameTable;
ULONG SizeOfProcessData;
PCSR_CONNECT_CALLBACK ConnectCallback;
PCSR_DISCONNECT_CALLBACK DisconnectCallback;
PCSR_HARDERROR_CALLBACK HardErrorCallback;
PVOID SharedSection;
PCSR_NEWPROCESS_CALLBACK NewProcessCallback;
PCSR_SHUTDOWNPROCESS_CALLBACK ShutdownProcessCallback;
ULONG Unknown2[3];
} CSR_SERVER_DLL, *PCSR_SERVER_DLL;

Indy
May 3rd, 2010, 03:38
Kayaker
Quote:
Using the example of BaseSrvExitProcess from basesrv.dll, CSR_API_NUMBER = 0x10003

If you use ldasm, there will be a small problem with the disclosure of procedural branches(ExitProcess() -> _ExitProcess()). When searching for parsing graph(control flow) no such problems. A signature will be defined as:
- Nesting Level = 1 or 2.
- (Push 4)__(push ID)__(Line code, not specified)__(call [CsrClientCallServer]).
Because procedural branch is a simple reference raises the level of nesting, it can lay the entire problem on engine. The function is defined as a hash on its name.
There is a semiprivate and opensource engine for creating and parsing of the graph - GCBE. It performs the disassembly(basic) and the construction/tracing of the graph is open(extensions, such as the optimizer, builder, etc. private. Public parts enough to parse the code and extract the necessary signatures).

j00ru
May 3rd, 2010, 06:18
@Indy, Kayaker: I certainly agree with you that the table should also contain CsrAPI opcode numbers, which in turn would be detected dynamically.

Let me write a few words on each of the two methods suggested.

1. Because of the fact that the only system libraries communicating with CSRSS are kernel32.dll and user32.dll, one could try to detect all the CsrClientCallServer (or CsrClientConnectToServer in case of Windows 7) calls and assign the contant numbers pushed on the stack to exported function names.

Let's consider, for example, kernel32.CreateProcessA. After a program calls this API, the execution goes through

CreateProcessA
CreateProcessInternalA
CreateProcessInternalW
CsrClientCallServer(0x10000)

In order to give the the 0x10000 value a particular name, the call path should be rebuilt, and eventually the number would become known as something like "SrvCreateProcessA".

Even though it seems to be doable, I don't find this method really convenient. And more importantly, based on such analysis, we still wouldn't retrieve the original CSR-api names, but its equivalent from the Windows API instead.

2. These numbers could also be extracted directly from the CSRSS-related binaries, most likely at run-time. The definitions and code snippets presented by Kayaker would be indeed helpful; however, there are a few additional factors that should be taken into consideration.

2.1 As the operating systems are evolving, so is the security / functionality design. Some examples of this are:
the console management code has been moved from CSRSS (winsrv.dll) running on SYSTEM privileges to a dedicated conhost.exe process, running with the local user's rights. More information can be found here: http://blogs.technet.com/askperf/archive/2009/10/05/windows-7-windows-server-2008-r2-console-host.aspx
- the number of the server dispatch table is not guaranteed to remain the same thorough respective OS versions. For instance, an additional sxssrv.dll file is loaded by CSRSS since Windows 7. In general, information regarding the libraries containing dispatch tables could be gathered from the CSRSS.exe command line, which usually takes the following form:
Code:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileC
This means that the contant values / macros from WRK are deprecated even now, not to mention the future.


2.2 Calculating the relevant CsrApi ID's is slightly more complicated than you presented. More specifically, a simple pseudocode of the code responsible for calling the corresponding handlers looks similar to the following:

Code:
CsrApiID = WaitForRequest();

if((CsrApiID>>16) > NUMBER_OF_DISPATCH_TABLES)
continue;

ServerTable = CsrLoadedServerDll[(CsrApiID>>16)];
if(ServerTable)
{
if((CsrApiID - ServerTable->ApiBase) < (ServerTable->HighestApiSupported - ServerTable->ApiBase))
{
ServerTable->DispatchTable[CsrApiID - ServerTable->ApiBase];
}
}
which means that three fields of the _CSR_SERVER_DLL are in fact required to rebuild the CsrApi numbers.



To sum everything up - IMHO the best method of achieving a version-independent table dumper would:
Find the CSRSS process associated with the current user, and detect the loaded server libraries
Heuristically parse the server initialization function in search of the three values (ApiBase, HighestApiSupported and DispatchTable). OR
Heuristically find the CsrLoadedServerDll table inside csrsrv.dll, and parse each _CSR_SERVER_DLL structure separately
Obtain the symbols for function pointers present in the tables, and associate names with the contants.


Any suggestions regarding the above? It is likely that I'll take up this project and let you test some generic piece of code

Cheers

evilcry
May 3rd, 2010, 08:59
Nice work, thanks!

Indy
May 3rd, 2010, 11:08
Quote:
Even though it seems to be doable, I don't find this method really convenient. And more importantly, based on such analysis, we still wouldn't retrieve the original CSR-api names, but its equivalent from the Windows API instead.

You are wrong. For such tasks, more so for problems that are of concern, such as search LdrpDllNotificationList or LdrpInitializeTls.. analysis of the graph is the only normal solution. Graph can renounce the binary code, considering the functional in general.
The nesting level of procedures, such as CreateProcessA -> CreateProcessInternalA.. etc. in the analysis of the graph is irrelevant. He wondered if the construction of the graph, to truncate the extra code.
This is the only normal solution.

j00ru
May 3rd, 2010, 12:57
Hey Indy,
I don't see your point Indeed, execution graph analysis is some solution, but why calling it the only reasonable one (as far as I understand) ?

Here are my doubts:

Even if we're able to find & analyze all the x-refs to CsrClientCallServer, present in kernel32.dll and user32.dll, where do we take the corresponding, internal CsrApi names from? I've mentioned this issue before (you've even quoted it).
The purpose of such project would be to gather information about all the existing functions. What is your guarantee that every supported CsrApi function is called from somewhere within the WinAPI implementation libraries? I don't have one.


By the way, could you please clarify what a normal solution means here? I thought we're looking for something rather effective (even if out-of-the-box) than normal in any terms

Cheers

Indy
May 3rd, 2010, 13:51
j00ru
1. Of course the internal names are not needed. Necessary identification. They are linked with the name of the exported API functions. For example ExitProcess -> BasepExitProcess.
2. Prove otherwise
Of course not all services are used. But they are not needed. In addition, most of the opensource(w2k, w2000 etc.).
Normal decision means universal, stable and independent of the version.
Definition of table ID I regard as extremely bad decision. This is my imho, I can not hardly be mistaken in this
As proof, I can build a sample

j00ru
May 4th, 2010, 03:18
Quote:
[Originally Posted by Indy;86399]j00ru
1. Of course the internal names are not needed. Necessary identification. They are linked with the name of the exported API functions. For example ExitProcess -> BasepExitProcess.

Name identification is one thing. The information about where a specific CsrApi is implemented is the second one.

Quote:
2. Prove otherwise
Of course not all services are used. But they are not needed. In addition, most of the opensource(w2k, w2000 etc.).
Normal decision means universal, stable and independent of the version.
Definition of table ID I regard as extremely bad decision. This is my imho, I can not hardly be mistaken in this
As proof, I can build a sample

Documenting CSRSS only through the Windows API libraries sounds like trying to create a complete system-call table (e.g. graphical calls) based on static user32.dll analysis.
Looking at the above quote, I'm under the impression that we've got a different point of view, on what exactly we would like to create in the first place. Well, maybe just let it be this way

Cheers

Indy
May 4th, 2010, 06:20
j00ru
An example of a well reflecting the essence. Example is not full(dasm cut that not can disclose the import, so not everywhere will work on XP will)

Kayaker
May 5th, 2010, 00:28
I wanted to try extracting out the CSR_SERVER_DLL structure from memory for each of the CSRSS subsystems, to get the address of the corresponding DispatchTable and other interesting bits dynamically.

If you trace back from the CsrServerApiDispatchTable entry in csrsrv.dll you find that the parent CSR_SERVER_DLL structure is initially allocated on the heap in CsrLoadServerDll. Some of the structure elements are defined here, others later in CsrServerDllInitialization (csrsrv), ServerDllInitialization (basesrv), or ConServerDllInitialization & UserServerDllInitialization (winsrv).

I'm not sure how all the modules tie together - who loads who and in what order or how the individual heap blocks comprising each of the CSR_SERVER_DLL structures are allocated, but it doesn't matter for the discussion.

Code:

int __stdcall CsrLoadServerDll

:75B4338C movzx ebx, [ebp+DestinationString.MaximumLength]
:75B43390 add ebx, 4Ch
:75B43393 push ebx
:75B43394 mov eax, _CsrBaseTag ; 0 in memory
:75B43399 add eax, 40000h
:75B4339E push eax
:75B4339F push _CsrHeap ; 0x160000 in memory
:75B433A5 call ds:__imp__RtlAllocateHeap@12

RtlAllocateHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN ULONG Size );


Note that the HeapHandle (_CsrHeap) is actually the address of the Default Process Heap (for csrss.exe).


I started with TopToBottomNt and viewed the memory of csrss.exe, searching for the *ServerApiDispatchTable addresses obtained from disassembly of each of the CSR modules. I found them all in the Default Process Heap beginning at 0x160000.

At this point a better tool was called for so I used HeapMemView from NirSoft. Perfect! All of the individual heap allocations for csrss.exe can be examined. After a little bit of digging I found each of the CSR_SERVER_DLL structures with pointers to the Data Address and Header Address for each block (they are all of block size 88, 0x58).



Next I created a structure definition in WinDbg for CSR_SERVER_DLL for use with the dt (Display Type) command. I had a problem though with attaching (non-invasively) to csrss.exe in usermode - it would lock up. Instead I used LiveKD in local kernel mode.

To create the custom structure type so it was available in kernel mode in any context, I used the trick of Blabberer to ADD the struct definition to the already existing win32k.pdb, whose symbols are loaded by default in all contexts. You can use .reload /u win32k.sys to unload the pdb, modify your custom defs and recompile, then reload the new symbol with .reload win32k.sys.

http://www.woodmann.com/forum/showthread.php?10295-Mysteries-of-win32k-amp-GDI&p=72632&viewfull=1#post72632


Here is the structure definition I created which was added to the win32k.pdb list of symbols. Note that the last field, ULONG Unknown2[3]; given in the ReactOS version in an earlier post I redefined as UCHAR NameString[12], since that's what the ANSI_STRING Buffer field points to.

Code:

typedef unsigned long ULONG;
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef unsigned long HANDLE;
typedef unsigned long PBOOLEAN;
typedef unsigned long PCHAR;
typedef unsigned long PVOID;
typedef unsigned long PCSR_API_ROUTINE;
typedef unsigned long PCSR_CONNECT_CALLBACK;
typedef unsigned long PCSR_DISCONNECT_CALLBACK;
typedef unsigned long PCSR_HARDERROR_CALLBACK;
typedef unsigned long PCSR_NEWPROCESS_CALLBACK;
typedef unsigned long PCSR_SHUTDOWNPROCESS_CALLBACK;

typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} ANSI_STRING, *PANSI_STRING;

typedef struct _CSR_SERVER_DLL
{
ULONG Length;
HANDLE Event;
ANSI_STRING Name;
HANDLE ServerHandle;
ULONG ServerId;
ULONG Unknown;
ULONG ApiBase;
ULONG HighestApiSupported;
PCSR_API_ROUTINE *DispatchTable;
PBOOLEAN ValidTable;
PCHAR *NameTable;
ULONG SizeOfProcessData;
PCSR_CONNECT_CALLBACK ConnectCallback;
PCSR_DISCONNECT_CALLBACK DisconnectCallback;
PCSR_HARDERROR_CALLBACK HardErrorCallback;
PVOID SharedSection;
PCSR_NEWPROCESS_CALLBACK NewProcessCallback;
PCSR_SHUTDOWNPROCESS_CALLBACK ShutdownProcessCallback;
UCHAR NameString[12];
} CSR_SERVER_DLL, *PCSR_SERVER_DLL;




OK, let's get to the results already...


So we start with finding the csrss.exe process

kd> !process 0 0

PROCESS 831c7020 Image: csrss.exe

and switch context to csrss

kd> .process 831c7020
Implicit process is now 831c7020


Using the Data Address from HeapMemView for the relevant heap allocations, apply the custom CSR_SERVER_DLL structure definition.

Code:

kd> dt win32k!_CSR_SERVER_DLL -v -b 162b58
struct _CSR_SERVER_DLL, 19 elements, 0x58 bytes
+0x000 Length : 0x52
+0x004 Event : 0x14
+0x008 Name : struct _STRING, 3 elements, 0x8 bytes
"CSRSS"
+0x000 Length : 5
+0x002 MaximumLength : 6
+0x004 Buffer : 0x162ba4 (points to NameString below)
+0x010 ServerHandle : 0
+0x014 ServerId : 0
+0x018 Unknown : 0
+0x01c ApiBase : 0
+0x020 HighestApiSupported : 5
+0x024 DispatchTable : 0x75b48004
+0x028 ValidTable : 0x75b48018
+0x02c NameTable : (null)
+0x030 SizeOfProcessData : 0
+0x034 ConnectCallback : 0
+0x038 DisconnectCallback : 0
+0x03c HardErrorCallback : 0
+0x040 SharedSection : 0x7f6f0000
+0x044 NewProcessCallback : 0
+0x048 ShutdownProcessCallback : 0
+0x04c NameString : (12 elements) "CSRSS"
[00] 0x43 'C'
[01] 0x53 'S'
[02] 0x52 'R'
[03] 0x53 'S'
[04] 0x53 'S'
[05] 0 ''
[06] 0 ''
[07] 0 ''
[08] 0 ''
[09] 0 ''
[10] 0 ''
[11] 0 ''



Code:

kd> dt win32k!_CSR_SERVER_DLL -v -b 162bb8
struct _CSR_SERVER_DLL, 19 elements, 0x58 bytes
+0x000 Length : 0x54
+0x004 Event : 0x14
+0x008 Name : struct _STRING, 3 elements, 0x8 bytes
"basesrv"
+0x000 Length : 7
+0x002 MaximumLength : 8
+0x004 Buffer : 0x162c04
+0x010 ServerHandle : 0x75b50000
+0x014 ServerId : 1
+0x018 Unknown : 0
+0x01c ApiBase : 0
+0x020 HighestApiSupported : 0x20
+0x024 DispatchTable : 0x75b5d080
+0x028 ValidTable : 0x75b5d104
+0x02c NameTable : (null)
+0x030 SizeOfProcessData : 0
+0x034 ConnectCallback : 0x75b52a89
+0x038 DisconnectCallback : 0x75b52ab2
+0x03c HardErrorCallback : 0
+0x040 SharedSection : 0x7f6f06a0
+0x044 NewProcessCallback : 0
+0x048 ShutdownProcessCallback : 0
+0x04c NameString : (12 elements) "basesrv"
[00] 0x62 'b'
[01] 0x61 'a'
[02] 0x73 's'
[03] 0x65 'e'
[04] 0x73 's'
[05] 0x72 'r'
[06] 0x76 'v'
[07] 0 ''
[08] 0 ''
[09] 0 ''
[10] 0 ''
[11] 0 ''


UserServerDllInitialization

Code:

kd> dt win32k!_CSR_SERVER_DLL -v -b 163a80
struct _CSR_SERVER_DLL, 19 elements, 0x58 bytes
+0x000 Length : 0x53
+0x004 Event : 0x14
+0x008 Name : struct _STRING, 3 elements, 0x8 bytes
"winsrv"
+0x000 Length : 6
+0x002 MaximumLength : 7
+0x004 Buffer : 0x163acc
+0x010 ServerHandle : 0x75b60000
+0x014 ServerId : 3
+0x018 Unknown : 0
+0x01c ApiBase : 0x400
+0x020 HighestApiSupported : 0x40b
+0x024 DispatchTable : 0x75b9d560
+0x028 ValidTable : 0x75b9d458
+0x02c NameTable : (null)
+0x030 SizeOfProcessData : 0
+0x034 ConnectCallback : 0x75b61c5d
+0x038 DisconnectCallback : 0
+0x03c HardErrorCallback : 0x75b7b3b9
+0x040 SharedSection : 0x7f6f0000
+0x044 NewProcessCallback : 0
+0x048 ShutdownProcessCallback : 0x75b78f84
+0x04c NameString : (12 elements) "winsrv"
[00] 0x77 'w'
[01] 0x69 'i'
[02] 0x6e 'n'
[03] 0x73 's'
[04] 0x72 'r'
[05] 0x76 'v'
[06] 0 ''
[07] 0xff ''
[08] 0xff ''
[09] 0xff ''
[10] 0xff ''
[11] 0xff ''


ConServerDllInitialization

Code:

kd> dt win32k!_CSR_SERVER_DLL -v -b 163be0
struct _CSR_SERVER_DLL, 19 elements, 0x58 bytes
+0x000 Length : 0x53
+0x004 Event : 0x14
+0x008 Name : struct _STRING, 3 elements, 0x8 bytes
"winsrv"
+0x000 Length : 6
+0x002 MaximumLength : 7
+0x004 Buffer : 0x163c2c
+0x010 ServerHandle : 0x75b60000
+0x014 ServerId : 2
+0x018 Unknown : 0
+0x01c ApiBase : 0x200
+0x020 HighestApiSupported : 0x255
+0x024 DispatchTable : 0x75b689f0
+0x028 ValidTable : 0x75b68b48
+0x02c NameTable : (null)
+0x030 SizeOfProcessData : 0x58
+0x034 ConnectCallback : 0x75b61a5a
+0x038 DisconnectCallback : 0x75b618de
+0x03c HardErrorCallback : 0
+0x040 SharedSection : 0x7f6f0000
+0x044 NewProcessCallback : 0x75b61963
+0x048 ShutdownProcessCallback : 0x75b7d5c7
+0x04c NameString : (12 elements) "winsrv"
[00] 0x77 'w'
[01] 0x69 'i'
[02] 0x6e 'n'
[03] 0x73 's'
[04] 0x72 'r'
[05] 0x76 'v'
[06] 0 ''
[07] 0xff ''
[08] 0xff ''
[09] 0xff ''
[10] 0xff ''
[11] 0xff ''




So what good is all this? While it might not be directly applicable to finding the CsrAPI Id's or their definitions, if we can find a reliable way to "walk" the Default Process Heap of csrss.exe and recognize the _CSR_SERVER_DLL allocations, it's a simple matter to get absolute addresses of PCSR_API_ROUTINE *DispatchTable and other elements for each of the subsystems. Assuming the whole house of cards doesn't fall in Vista or Windows 7...

The heap allocations are preceded by an 8 byte Header, recognized by HeapMemView as a unique Header Address immediately before the Data Address. This unknown "HEAP_HEADER" might be similar to a POOL_HEADER, though when I applied that definition to the structure it didn't parse like a normal Pool Header (that I recognized anyway).

Hopefully someone has some knowledge of how to parse/walk a process heap and might be able to clarify things.

Kayaker

Indy
May 5th, 2010, 02:31
Kayaker
Quote:
I'm not sure how all the modules tie together - who loads who and in what order or how the individual heap blocks comprising each of the CSR_SERVER_DLL structures are allocated, but it doesn't matter for the discussion.

http://virustech.org/f/viewtopic.php?id=25
http://files.virustech.org/release/autorundoc/autorun_doc_03.01.2010.rar
The only question for what it is you need

j00ru
May 5th, 2010, 13:32
@Kayaker: Really nice work!
Quote:
Assuming the whole house of cards doesn't fall in Vista or Windows 7...
As far as my observations go, the situation is generally the same on both Windows Vista and 7 (obviously the CSR_DLL_SERVER structures differ from version to version).

The only thing in the presented approach that bothers me is the non-deterministic method of finding the aforementioned structures on the default CSRSS.exe heap. I am not a win32-heap specialist, but w/e method is used, we end up "scanning" the heap blocks in search of known signs of CSR_DLL_SERVER presence - there is still no certainty that we will find every and only desired structures (or maybe I've just missed your point?).

One way or another, I myself have came up with a few new ideas of how to obtain the three critical values (ApiBase, HighestApiSupported, DispatchTable) in a stable manner.

1. The first concept is about making direct use of the server initialization routines, exported by particular server libraries (such as winsrv.ConServerDllInitialization, winsrv.UserServerDllInitialization or basesrv.ServerDllInitialization). In fact, I can see a couple of possibilities how to take advantage of these functions:

- Load these DLLs in current process context. Call the server initialization routines, supplying a pointer to the buffer controlled by us - and pray that none of the functions crash. If the above succeeds, our buffer would contain the desired values + dispatch table pointer (still in local process' memory).
If no memory address randomization is involved, most likely we've just figured out the DispatchTable address inside CSRSS.exe.

- Try to perform an analogue trick, but inside CSRSS itself rather than our process. More precisely - create a remote thread and respectively call the init routines, supplying a controlled buffer as the output parameter. However, a bit of a prayer is still necessary here, due to the fact that the system never initializes server libraries twice under normal circumstances - such a technique could result in unknown behavior (i.e. disabling all the console windows already present on the system [this one I've encouraged on my own skin during tests] and other unpredictable consequences).

- As I've mentioned earlier, the actual list of server dlls, which CSRSS is supposed to load, is passed to the process through command line! To be more exact, the command line path, used by SMSS to spawn the csrss process can be found in a registry value:

Code:
HKLM\System\CurrentControlSet\Control\Session Manager\SubSystems\CSRSS\Windows

containing something like:
Code:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,12288,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16


This, in turn, means that the the OS admin can easily control the names of server files and initialization functions by simply modifying the above reg value. My idea is to create a wrapper library for each of the srv DLLs. A majority of the exported functions would be simply forwarded to the original images; only the execution of most interesting routines would be intercepted, e.g. in the following way:

Code:
DWORD STDCALL ConServerDllInitialization(CSR_SERVER_DLL* ConServer)
{
DWORD dwReturn = orgConServerDllInitialization(ConServer);

LogToFile("ConServer->ApiBase = 0x%.8x\n"
"ConServer->HighestApiSupported = 0x%.8x\n"
"ConServer->DispatchTable = 0x%.8x\n",
ConServer->ApiBase,ConServer->HighestApiSupported,ConServer->DispatchTable);

return dwReturn;
}


This would let us to parse the CSR_SERVER_DLL structure right after it is filled by the appropriate routine, and during legitimate system behavior - the wrappers would be called by CSRSS itself!

The only problem is that we can't apply this technique in relation to csrsrv.dll - that's because the library is not a basic server dll - it is highly connected with the csrss.exe image, and thus the CsrServer initialization takes place locally, without any exported function involvement.

I might create a some PoC code to prove that the above works in practice
Concurrently, I would like to hear your opinion regarding the presented ideas ;p

Cheers

Kayaker
May 5th, 2010, 21:58
Quote:
[Originally Posted by Indy;86408]The only question for what it is you need


Thanks for the info Indy. Need? Oh there's no need. In fact it shouldn't even be considered

That is always the problem of course j00ru, one needs to make certain assumptions. However in the simple situation, if we assume a known CSR_SERVER_DLL structure that will be used in the expected manner, then the ANSI_STRING Name field should always point to one of the 3 module names.


Here is a nice article describing a protocol for walking the heap

http://www.informit.com/articles/article.aspx?p=1081496

So as a thought experiment we could propose:

- Access csrss.exe PEB

- PEB.ProcessHeaps points to default process heap

- Walk the heap looking for allocations of appropriate size

- Test for identifiable Name string

ConServerDllInitialization and UserServerDllInitialization are both under "winsrv", but if you look at the results above, the ServerId field seems to match the DllIndex values from these defines, a second thing one could use for confirmation:

#define CSRSRV_SERVERDLL_INDEX 0
#define BASESRV_SERVERDLL_INDEX 1
#define CONSRV_SERVERDLL_INDEX 2
#define USERSRV_SERVERDLL_INDEX 3

Using HeapMemView and the proposed search criteria (allocation size + csr name string), the 4 CSR_SERVER_DLL structures are easily picked out with no false positives.


The header before the heap allocation is now identified as a HEAP_ENTRY structure. Similar to a POOL_HEADER it will give the Size of the allocation and is key to "walking" the entire heap accurately.

Now we can go back and look at the heap headers for the CSR_SERVER_DLL allocations. Like in POOL_HEADER, the true size of the allocation = (Size * granularity) = (0xc * 8) = 0x60 bytes for CSR_SERVER_DLL.

Code:

kd> dt _HEAP_ENTRY -b -v 162b50
struct _HEAP_ENTRY, 7 elements, 0x8 bytes
+0x000 Size : 0xc
+0x002 PreviousSize : 3
+0x000 SubSegmentCode : 0x0003000c
+0x004 SmallTagIndex : 0xa5 ''
+0x005 Flags : 0x1 ''
+0x006 UnusedBytes : 0xe ''
+0x007 SegmentIndex : 0 ''

kd> dt _HEAP_ENTRY -b -v 162bb0
struct _HEAP_ENTRY, 7 elements, 0x8 bytes
+0x000 Size : 0xc
+0x002 PreviousSize : 0xc
+0x000 SubSegmentCode : 0x000c000c
+0x004 SmallTagIndex : 0xb9 ''
+0x005 Flags : 0x1 ''
+0x006 UnusedBytes : 0xc ''
+0x007 SegmentIndex : 0 ''

kd> dt _HEAP_ENTRY -b -v 163a78
struct _HEAP_ENTRY, 7 elements, 0x8 bytes
+0x000 Size : 0xc
+0x002 PreviousSize : 9
+0x000 SubSegmentCode : 0x0009000c
+0x004 SmallTagIndex : 0x80 ''
+0x005 Flags : 0x1 ''
+0x006 UnusedBytes : 0xd ''
+0x007 SegmentIndex : 0 ''

kd> dt _HEAP_ENTRY -b -v 163bd8
struct _HEAP_ENTRY, 7 elements, 0x8 bytes
+0x000 Size : 0xc
+0x002 PreviousSize : 8
+0x000 SubSegmentCode : 0x0008000c
+0x004 SmallTagIndex : 0xb4 ''
+0x005 Flags : 0x1 ''
+0x006 UnusedBytes : 0xd ''
+0x007 SegmentIndex : 0 ''



Enough of that anyway, it's just another way of looking at things.

Very clever idea of using a wrapper or calling the module initialization routines supplying your own buffer. The DONT_RESOLVE_DLL_REFERENCES or LOAD_LIBRARY_AS_DATAFILE flags on loading the modules might help to not "shake the boat" too much... The initialization of the CSR_SERVER_DLL structure seems to be scattered though, some fields are filled in CsrLoadServerDll, some in the individual *ServerDllInitialization routines. So just calling the init routines with a supplied buffer might not get all the definitions, but maybe the ones you need at least.

Anticipating POC...

j00ru
May 10th, 2010, 09:16
Well, sorry for such a delay, I've been quite busy lately.

@Kayaker: Thanks for sheding some more light on your concept Heap scanning is indeed a 99.9% stable method, it's just me that doesn't like basing on certain assumptions that might not always be true
Anyway, good work!

As for the wrapper DLL idea, and the Proof of Concept.
Quote:
The DONT_RESOLVE_DLL_REFERENCES or LOAD_LIBRARY_AS_DATAFILE flags on loading the modules might help to not "shake the boat" too much...

Well, what's the purpose of not fully loading the original server libraries? If we're performing a dll-in-the-middle attack, by supplying a wrapper fake_winsrv.dll file, for instance - we still want winsrv.dll to be fully functional, since it is still responsible for normal server execution (csrss would not load winsrv.dll by himself).

Quote:
The initialization of the CSR_SERVER_DLL structure seems to be scattered though, some fields are filled in CsrLoadServerDll, some in the individual *ServerDllInitialization routines. So just calling the init routines with a supplied buffer might not get all the definitions, but maybe the ones you need at least.

Certainly... However, don't you think that it would be pointless if it was CSRSS that filled the ApiBase, HighestApiSupported, DispatchTable fields instead of individual libraries? I personally think it would make no sense :-P

To prove that my method works, I've speedily written the aforementioned wrapper lib, filtering the ConServerDllInitialization and UserServerDllInitialization exports, and storing the data initialized by winsrv in a text file [since it's a PoC, I haven't implemented any symbol-recognition mechanism to actually assoc the addresses with their names].

After setting up the library and rebooting the system, C:\log.txt contains the following:
Code:
UserServer->ApiBase = 0x00000400
UserServer->HighestApiSupported = 0x0000040b
UserServer->DispatchTable = 0x75b6d560
-- Server Dispatch Table:
[0x00000400] 0x75b48910
[0x00000401] 0x75b48f18
[0x00000402] 0x75b37977
[0x00000403] 0x75b37b6c
[0x00000404] 0x75b4796f
[0x00000405] 0x75b316a9
[0x00000406] 0x75b478cc
[0x00000407] 0x75b37d09
[0x00000408] 0x75b4787c
[0x00000409] 0x75b4787c
[0x0000040a] 0x75b3bd30
ConServer->ApiBase = 0x00000200
ConServer->HighestApiSupported = 0x00000255
ConServer->DispatchTable = 0x75b389f0
-- Server Dispatch Table:
[0x00000200] 0x75b3bae8
[0x00000201] 0x75b45a50
[0x00000202] 0x75b56627
[0x00000203] 0x75b4694d
[0x00000204] 0x75b569ab
[0x00000205] 0x75b56c0b
[0x00000206] 0x75b56cc1
[0x00000207] 0x75b458c6
[0x00000208] 0x75b324e5
[0x00000209] 0x75b50a82
[0x0000020a] 0x75b515b0
[0x0000020b] 0x75b3411a
[0x0000020c] 0x75b46a60
[0x0000020d] 0x75b50b3f
[0x0000020e] 0x75b50b77
[0x0000020f] 0x75b50c15
[0x00000210] 0x75b50c93
[0x00000211] 0x75b32814
[0x00000212] 0x75b50dc9
[0x00000213] 0x75b50e2c
[0x00000214] 0x75b46495
[0x00000215] 0x75b461cd
[0x00000216] 0x75b45c72
[0x00000217] 0x75b50e8f
[0x00000218] 0x75b46293
[0x00000219] 0x75b50f0e
[0x0000021a] 0x75b45ec4
[0x0000021b] 0x75b510d2
[0x0000021c] 0x75b51189
[0x0000021d] 0x75b45da3
[0x0000021e] 0x75b3358a


In fact, there's nothing magic about the DLL source code. A dirty example of the initialization filters follow:

Code:
DWORD STDCALL ConServerDllInitialization(CSR_SERVER_DLL* ConServer)
{
DWORD dwReturn = orgConServerDllInitialization(ConServer);
DWORD i;

FILE* f = fopen("C:\\log.txt","a+";
if(f)
{
fprintf(f,"ConServer->ApiBase = 0x%.8x\n"
"ConServer->HighestApiSupported = 0x%.8x\n"
"ConServer->DispatchTable = 0x%.8x\n",
ConServer->ApiBase,ConServer->HighestApiSupported,ConServer->DispatchTable);

fputs(" -- Server Dispatch Table:\n",f);
for( i=ConServer->ApiBase;i<ConServer->HighestApiSupported;i++ )
fprintf(f," [0x%.8x] 0x%.8x\n",i,ConServer->DispatchTable[i-ConServer->ApiBase]);

fclose(f);
}

return dwReturn;
}

DWORD STDCALL UserServerDllInitialization(CSR_SERVER_DLL* UserServer)
{
DWORD dwReturn = orgUserServerDllInitialization(UserServer);
DWORD i;

FILE* f = fopen("C:\\log.txt","a+";
if(f)
{
fprintf(f,"UserServer->ApiBase = 0x%.8x\n"
"UserServer->HighestApiSupported = 0x%.8x\n"
"UserServer->DispatchTable = 0x%.8x\n",
UserServer->ApiBase,UserServer->HighestApiSupported,UserServer->DispatchTable);

fputs(" -- Server Dispatch Table:\n",f);
for( i=UserServer->ApiBase;i<UserServer->HighestApiSupported;i++ )
fprintf(f," [0x%.8x] 0x%.8x\n",i,UserServer->DispatchTable[i-UserServer->ApiBase]);

fclose(f);
}

return dwReturn;
}


The installation of such a dll is fairly simple - it is enough to replace the "winsrv" string in the registry value mentioned previously, with our library filename (i.e. fake_winsrv), and copy the file itself to \Windows\system32. I hope everything is clear here ;-)

Cheers

BanMe
June 18th, 2010, 19:26
Well this seems to almost be the path that I decided to take,except I've wondered down the path to actualy implementing a 'ServerDll' inside csrss, by doing this allows me to use more user mode coding techniques(though I don't..yet) and to separate off the threads that use these functions..Thats some interesting stuff you got going though..