Windows Subsytem Csrss

septembre 3rd, 2008 at 03:26 admin

De retour après une longue absence durant laquelle je faisais semblant de taff avec mon maître. Pour ceux qui croyaient que j’avais une vie sociale c’est raté, j’étais plutôt enfermé dans une salle sombre, humide et froide ; travaillant sur des sujets douteux avec pour unique lumière un laptop qui éclairait mon visage pâle et amaigrit, nourrit au café, écoutant du black métal : un rêve pour certains en quelque sorte. Cette période étant finie, je peux reprendre une activité normale. Cette fois on va descendre toujours plus loin dans notre OS préféré pour s’intéresser à un composant méconnu mais extrêmement important. Je veux parler du subsystem constitué par le process csrss.exe.

Le processes csrss (Client Server Runtime Process), crée par le système lors du boot, est responsable de la gestion des threads et processes en maintenant une liste interne de ceux-ci comme on peut le lire ici pour pouvoir effectuer diverses opérations sur ses objets en cas de besoin, même si j’ai du mal à comprendre exactement pourquoi. Csrss est aussi responsable de la Console Win32 et c’est sur ce sujet que je vais m’attarder ici. Csrss agit simplement comme un serveur fournissant aux processes console un ensemble de features non accessibles via l’API standard de Windows.

Quand on regarde le binaire csrss.exe sous IDA on s’aperçoit qu’il est très petit, 6ko seulement, par contre il charge une DLL csrss.dll qui export un ensemble de fonctions assez sympas :

->Export Table
   Characteristics:        0x00000000
   TimeDateStamp:          0x48023843  (GMT: Sun Apr 13 16:43:47 2008)
   MajorVersion:           0x0000
   MinorVersion:           0x0000  -> 0.00
   Name:                   0x000078D6  ("CSRSRV.dll")
   Base:                   0x00000001
   NumberOfFunctions:      0x00000023
   NumberOfNames:          0x00000023
   AddressOfFunctions:     0x00007778
   AddressOfNames:         0x00007804
   AddressOfNameOrdinals:  0x00007890

   Ordinal RVA        Symbol Name
   ------- ---------- ----------------------------------
   0x0001  0x000053C5 "CsrAddStaticServerThread"
   0x0002  0x00004160 "CsrCallServerFromServer"
   0x0003  0x00003FCE "CsrConnectToUser"
   0x0004  0x00005C9C "CsrCreateProcess"
   0x0005  0x00006056 "CsrCreateRemoteThread"
   0x0006  0x00005F86 "CsrCreateThread"
   0x0007  0x00006375 "CsrCreateWait"
   0x0008  0x000062D8 "CsrDebugProcess"
   0x0009  0x000062E5 "CsrDebugProcessStop"
   0x000A  0x00004F8E "CsrDereferenceProcess"
   0x000B  0x00005520 "CsrDereferenceThread"
   0x000C  0x00006502 "CsrDereferenceWait"
   0x000D  0x00005ECE "CsrDestroyProcess"
   0x000E  0x00006110 "CsrDestroyThread"
   0x000F  0x00005438 "CsrExecServerThread"
   0x0010  0x00005010 "CsrGetProcessLuid"
   0x0011  0x00004E6C "CsrImpersonateClient"
   0x0012  0x000052D3 "CsrLockProcessByClientId"
   0x0013  0x00005353 "CsrLockThreadByClientId"
   0x0014  0x0000658C "CsrMoveSatisfiedWait"
   0x0015  0x000064A4 "CsrNotifyWait"
   0x0016  0x00002A17 "CsrPopulateDosDevices"
   0x0017  0x00003FC3 "CsrQueryApiPort"
   0x0018  0x00004F20 "CsrReferenceThread"
   0x0019  0x00004EB3 "CsrRevertToSelf"
   0x001A  0x0000305E "CsrServerInitialization"
   0x001B  0x00004CA2 "CsrSetBackgroundPriority"
   0x001C  0x000050F2 "CsrSetCallingSpooler"
   0x001D  0x00004C7B "CsrSetForegroundPriority"
   0x001E  0x000061C5 "CsrShutdownProcesses"
   0x001F  0x00003204 "CsrUnhandledExceptionFilter"
   0x0020  0x00005330 "CsrUnlockProcess"
   0x0021  0x000061A2 "CsrUnlockThread"
   0x0022  0x00004421 "CsrValidateMessageBuffer"
   0x0023  0x0000449D "CsrValidateMessageString"

La fonction importante dans cette liste est CsrServerInitialization, c’est celle-ci qui va vraiment initialiser le subsystem, notamment en chargeant les autres fournisseurs. La ligne de commande de csrss.exe est assez marrante :

C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16

On retrouve 2 noms de DLL, baserv.dll et winsrv.dll suivis de 2 noms faisant partit de leurs APIs exportées, UserServerDllInitialization et ConServerDllInitialization. Ces DLLS sont en fait des providers que csrss wrappe pour l’interfacer avec le reste du système. La communication avec ses providers est basée sur les LPC à travers 2 ports \Windows\ApiPort et \Windows\SbApiPort crées par cssrv.dll et gérés par les threads CsrApiRequestThread. On peut le voir facilement en regardant les stacks des threads de csrss avec Process Explorer et puis il y existe une ré-implémentation dans ReactOS ici. Pour info le LPC ApiPort gère toutes les fonctions du subsytem alors que le SbApiPort est utilisé par csrsrv.dll dans le contexte des sessions. J’ai repéré aussi d’autres providers possible qui sont consrv.dll et usersrv.dll mais ceux ci ne semblent pas chargés sur mon système.

Maintenant voyons la liste des fonctions qui sont fournies par basesrv.dll et winsrv.dll :

bassrv.dll
kd> dds basesrv!BaseServerApiDispatchTable l 20
75aed080  75ae4dab basesrv!BaseSrvCreateProcess
75aed084  75ae4b61 basesrv!BaseSrvCreateThread
75aed088  75ae4d10 basesrv!BaseSrvGetTempFile
75aed08c  75ae4cad basesrv!BaseSrvExitProcess
75aed090  75ae4d37 basesrv!BaseSrvDebugProcess
75aed094  75ae8410 basesrv!BaseSrvCheckVDM
75aed098  75ae84fe basesrv!BaseSrvUpdateVDMEntry
75aed09c  75ae702e basesrv!BaseSrvGetNextVDMCommand
75aed0a0  75ae857d basesrv!BaseSrvExitVDM
75aed0a4  75ae523b basesrv!BaseSrvIsFirstVDM
75aed0a8  75ae76ee basesrv!BaseSrvGetVDMExitCode
75aed0ac  75ae5a7d basesrv!BaseSrvSetReenterCount
75aed0b0  75ae4d44 basesrv!BaseSrvSetProcessShutdownParam
75aed0b4  75ae4d7d basesrv!BaseSrvGetProcessShutdownParam
75aed0b8  75ae8718 basesrv!BaseSrvNlsSetUserInfo
75aed0bc  75ae87a5 basesrv!BaseSrvNlsSetMultipleUserInfo
75aed0c0  75ae8bf3 basesrv!BaseSrvNlsCreateSection
75aed0c4  75ae64c9 basesrv!BaseSrvSetVDMCurDirs
75aed0c8  75ae65fb basesrv!BaseSrvGetVDMCurDirs
75aed0cc  75ae65aa basesrv!BaseSrvBatNotification
75aed0d0  75ae78b5 basesrv!BaseSrvRegisterWowExec
75aed0d4  75ae9f92 basesrv!BaseSrvSoundSentryNotification
75aed0d8  75ae9a30 basesrv!BaseSrvRefreshIniFileMapping
75aed0dc  75ae40e9 basesrv!BaseSrvDefineDosDevice
75aed0e0  75ae966e basesrv!BaseSrvSetTermsrvAppInstallMode
75aed0e4  75ae8a9a basesrv!BaseSrvNlsUpdateCacheCount
75aed0e8  75ae2db6 basesrv!BaseSrvSetTermsrvClientTimeZone
75aed0ec  75aea764 basesrv!BaseSrvSxsCreateActivationContext
75aed0f0  75ae4d37 basesrv!BaseSrvDebugProcess
75aed0f4  75ae4c01 basesrv!BaseSrvRegisterThread
75aed0f8  75ae86a7 basesrv!BaseSrvNlsGetUserInfo
75aed0fc  75ae283a basesrv!BaseSrvAppHelpQueryModuleData


winsrv.dll
kd> dds winsrv!UserServerApiDispatchTable l B
75b2d560  75b08910 winsrv!SrvExitWindowsEx
75b2d564  75b08f18 winsrv!SrvEndTask
75b2d568  75af7977 winsrv!SrvLogon
75b2d56c  75af7b6c winsrv!SrvRegisterServicesProcess
75b2d570  75b0796f winsrv!SrvActivateDebugger
75b2d574  75af16a9 winsrv!SrvGetThreadConsoleDesktop
75b2d578  75b078cc winsrv!SrvDeviceEvent
75b2d57c  75af7d09 winsrv!SrvRegisterLogonProcess
75b2d580  75b0787c winsrv!SrvWin32HeapFail
75b2d584  75b0787c winsrv!SrvWin32HeapFail
75b2d588  75afbd30 winsrv!SrvCreateSystemThreads

kd> dds winsrv!ConsoleServerApiDispatchTable l 55
75af89f0  75afbae8 winsrv!SrvOpenConsole
75af89f4  75b05a50 winsrv!SrvGetConsoleInput
75af89f8  75b16627 winsrv!SrvWriteConsoleInput
75af89fc  75b0694d winsrv!SrvReadConsoleOutput
75af8a00  75b169ab winsrv!SrvWriteConsoleOutput
75af8a04  75b16c0b winsrv!SrvReadConsoleOutputString
75af8a08  75b16cc1 winsrv!SrvWriteConsoleOutputString
75af8a0c  75b058c6 winsrv!SrvFillConsoleOutput
75af8a10  75af24e5 winsrv!SrvGetConsoleMode
75af8a14  75b10a82 winsrv!SrvGetConsoleNumberOfFonts
75af8a18  75b115b0 winsrv!SrvGetConsoleNumberOfInputEvents
75af8a1c  75af411a winsrv!SrvGetConsoleScreenBufferInfo
75af8a20  75b06a60 winsrv!SrvGetConsoleCursorInfo
75af8a24  75b10b3f winsrv!SrvGetConsoleMouseInfo
75af8a28  75b10b77 winsrv!SrvGetConsoleFontInfo
75af8a2c  75b10c15 winsrv!SrvGetConsoleFontSize
75af8a30  75b10c93 winsrv!SrvGetConsoleCurrentFont
75af8a34  75af2814 winsrv!SrvSetConsoleMode
75af8a38  75b10dc9 winsrv!SrvSetConsoleActiveScreenBuffer
75af8a3c  75b10e2c winsrv!SrvFlushConsoleInputBuffer
75af8a40  75b06495 winsrv!SrvGetLargestConsoleWindowSize
75af8a44  75b061cd winsrv!SrvSetConsoleScreenBufferSize
75af8a48  75b05c72 winsrv!SrvSetConsoleCursorPosition
75af8a4c  75b10e8f winsrv!SrvSetConsoleCursorInfo
75af8a50  75b06293 winsrv!SrvSetConsoleWindowInfo
75af8a54  75b10f0e winsrv!SrvScrollConsoleScreenBuffer
75af8a58  75b05ec4 winsrv!SrvSetConsoleTextAttribute
75af8a5c  75b110d2 winsrv!SrvSetConsoleFont
75af8a60  75b11189 winsrv!SrvSetConsoleIcon
75af8a64  75b05da3 winsrv!SrvReadConsole
75af8a68  75af358a winsrv!SrvWriteConsole
75af8a6c  75af41ff winsrv!SrvDuplicateHandle
75af8a70  75b17428 winsrv!SrvGetHandleInformation
75af8a74  75b17490 winsrv!SrvSetHandleInformation
75af8a78  75af4186 winsrv!SrvCloseHandle
75af8a7c  75af267c winsrv!SrvVerifyConsoleIoHandle
75af8a80  75b0d1a7 winsrv!SrvAllocConsole
75af8a84  75b0d35d winsrv!SrvFreeConsole
75af8a88  75af28c6 winsrv!SrvGetConsoleTitle
75af8a8c  75af8f17 winsrv!SrvSetConsoleTitle
75af8a90  75b16d8d winsrv!SrvCreateConsoleScreenBuffer
75af8a94  75b1611c winsrv!SrvInvalidateBitMapRect
75af8a98  75b15e6e winsrv!SrvVDMConsoleOperation
75af8a9c  75b0ee09 winsrv!SrvSetConsoleCursor
75af8aa0  75b0eeda winsrv!SrvShowConsoleCursor
75af8aa4  75b07402 winsrv!SrvConsoleMenuControl
75af8aa8  75b0d742 winsrv!SrvSetConsolePalette
75af8aac  75b0d9b6 winsrv!SrvSetConsoleDisplayMode
75af8ab0  75b06e32 winsrv!SrvRegisterConsoleVDM
75af8ab4  75b0dcf3 winsrv!SrvGetConsoleHardwareState
75af8ab8  75b0f725 winsrv!SrvSetConsoleHardwareState
75af8abc  75afc0f4 winsrv!SrvGetConsoleDisplayMode
75af8ac0  75b13b46 winsrv!SrvAddConsoleAlias
75af8ac4  75b13d48 winsrv!SrvGetConsoleAlias
75af8ac8  75b13f2c winsrv!SrvGetConsoleAliasesLength
75af8acc  75b11ef6 winsrv!SrvGetConsoleAliasExesLength
75af8ad0  75b1400d winsrv!SrvGetConsoleAliases
75af8ad4  75b11f4e winsrv!SrvGetConsoleAliasExes
75af8ad8  75b14570 winsrv!SrvExpungeConsoleCommandHistory
75af8adc  75b145d9 winsrv!SrvSetConsoleNumberOfCommands
75af8ae0  75b14648 winsrv!SrvGetConsoleCommandHistoryLength
75af8ae4  75b146da winsrv!SrvGetConsoleCommandHistory
75af8ae8  75b12081 winsrv!SrvSetConsoleCommandHistoryMode
75af8aec  75af278f winsrv!SrvGetConsoleCP
75af8af0  75b1129b winsrv!SrvSetConsoleCP
75af8af4  75b0de0e winsrv!SrvSetConsoleKeyShortcuts
75af8af8  75b0dd83 winsrv!SrvSetConsoleMenuClose
75af8afc  75b0dae0 winsrv!SrvConsoleNotifyLastClose
75af8b00  75b10d19 winsrv!SrvGenerateConsoleCtrlEvent
75af8b04  75b06bad winsrv!SrvGetConsoleKeyboardLayoutName
75af8b08  75b114e6 winsrv!SrvGetConsoleWindow
75af8b0c  75b11a6a winsrv!SrvGetConsoleCharType
75af8b10  75b11b3b winsrv!SrvSetConsoleLocalEUDC
75af8b14  75b11619 winsrv!SrvSetConsoleCursorMode
75af8b18  75b1168f winsrv!SrvGetConsoleCursorMode
75af8b1c  75b11704 winsrv!SrvRegisterConsoleOS2
75af8b20  75b11783 winsrv!SrvSetConsoleOS2OemFormat
75af8b24  75b117cd winsrv!SrvGetConsoleNlsMode
75af8b28  75b118c7 winsrv!SrvSetConsoleNlsMode
75af8b2c  75b11984 winsrv!SrvRegisterConsoleIME
75af8b30  75b11a35 winsrv!SrvUnregisterConsoleIME
75af8b34  75af2730 winsrv!SrvGetConsoleLangId
75af8b38  75b0d3cb winsrv!SrvAttachConsole
75af8b3c  75b10acd winsrv!SrvGetConsoleSelectionInfo
75af8b40  75b1151d winsrv!SrvGetConsoleProcessList

La console n’étant pas gérée par les APIS Win32 GDI classiques, il suffit de regarder un peu comment sont implémentées ces fonctions pour comprendre. En effet, toutes les requêtes sont en fait passées au subsystem et plus précisément aux fonctions de winsrv.dll. Maintenant, il reste à savoir comment s’interfacer avec le subsystem pour profiter des ces APIs. Tout ceci est bien évidemment non documenté donc si on veut plus d’infos il faut reverser, googler et lire les sources de ReactOS. En regardant le code des fonctions de kernel32.dll comme GetConsoleTitle, on peut voir quelles utilisent une partie des APIS exportées de ntdll qui sont :

ntdll export table :

CsrAllocateCaptureBuffer
CsrAllocateMessagePointer
CsrCaptureMessageBuffer
CsrCaptureMessageMultiUnicodeStringsInPlace
CsrCaptureMessageString
CsrCaptureTimeout
CsrClientCallServer
CsrClientConnectToServer
CsrFreeCaptureBuffer
CsrGetProcessId
CsrIdentifyAlertableThread
CsrNewThread
CsrProbeForRead
CsrProbeForWrite
CsrSetPriorityClass

Ce sont ces fonctions qui permettent de communiquer avec winsrv.dll, voici les prototypes des plus importantes.

from umfuncs.h@ndk

//
// CSR Functions
//
PVOID
NTAPI
CsrAllocateCaptureBuffer(
    ULONG ArgumentCount,
    ULONG BufferSize
);

ULONG
NTAPI
CsrAllocateMessagePointer(
    struct _CSR_CAPTURE_BUFFER *CaptureBuffer,
    ULONG MessageLength,
    PVOID *CaptureData
);

VOID
NTAPI
CsrCaptureMessageBuffer(
    struct _CSR_CAPTURE_BUFFER *CaptureBuffer,
    PVOID MessageString,
    ULONG StringLength,
    PVOID *CapturedData
);


NTSTATUS
NTAPI
CsrClientCallServer(
    struct _CSR_API_MESSAGE *Request,
    struct _CSR_CAPTURE_BUFFER *CaptureBuffer OPTIONAL,
    ULONG ApiNumber,
    ULONG RequestLength
);


VOID
NTAPI
CsrFreeCaptureBuffer(
    struct _CSR_CAPTURE_BUFFER *CaptureBuffer
);

Après, en cherchant un peu dans sur le net et surtout dans les headers du NDK on retrouve les définitions des structures qui doivent être envoyées au LPC. Le listing suivant donne juste les structures de bases. Il existe en effet une structure spécifique en fonction de l’API qu’on veut appeler, qui se trouve dans l’union de la structure CSR_API_MSG.

/************************************************************************************/
//
// Csrss headers
//

#define WINSS_OBJECT_DIRECTORY_NAME     L"\\Windows"

#define CSRSRV_SERVERDLL_INDEX          0
#define CSRSRV_FIRST_API_NUMBER         0

#define BASESRV_SERVERDLL_INDEX         1
#define BASESRV_FIRST_API_NUMBER        0

#define CONSRV_SERVERDLL_INDEX          2
#define CONSRV_FIRST_API_NUMBER         512

#define USERSRV_SERVERDLL_INDEX         3
#define USERSRV_FIRST_API_NUMBER        1024

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

#define CSR_APINUMBER_TO_SERVERDLLINDEX( ApiNumber ) \
    ((ULONG)((ULONG)(ApiNumber) >> 16))

#define CSR_APINUMBER_TO_APITABLEINDEX( ApiNumber ) \
    ((ULONG)((USHORT)(ApiNumber)))

//
// This structure is filled in by the client prior to connecting to the CSR
// server.  The CSR server will fill in the OUT fields if prior to accepting
// the connection.
//

typedef struct _CSR_API_CONNECTINFO {
    OUT HANDLE ObjectDirectory;
    OUT PVOID SharedSectionBase;
    OUT PVOID SharedStaticServerData;
    OUT PVOID SharedSectionHeap;
    OUT ULONG DebugFlags;
    OUT ULONG SizeOfPebData;
    OUT ULONG SizeOfTebData;
    OUT ULONG NumberOfServerDllNames;
    OUT HANDLE ServerProcessId;
} CSR_API_CONNECTINFO, *PCSR_API_CONNECTINFO;

typedef struct _CSR_CLIENTCONNECT_MSG {
    IN ULONG ServerDllIndex;
    IN OUT PVOID ConnectionInformation;
    IN OUT ULONG ConnectionInformationLength;
} CSR_CLIENTCONNECT_MSG, *PCSR_CLIENTCONNECT_MSG;

#define CSR_NORMAL_PRIORITY_CLASS   0x00000010
#define CSR_IDLE_PRIORITY_CLASS     0x00000020
#define CSR_HIGH_PRIORITY_CLASS     0x00000040
#define CSR_REALTIME_PRIORITY_CLASS 0x00000080

//
// This helps out the Wow64 thunk generater, so we can change
// RelatedCaptureBuffer from struct _CSR_CAPTURE_HEADER* to PCSR_CAPTURE_HEADER.
// Redundant typedefs are legal, so we leave the usual form in as well.
//
struct _CSR_CAPTURE_HEADER;
typedef struct _CSR_CAPTURE_HEADER CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER;

typedef struct _CSR_CAPTURE_HEADER {
    ULONG Length;
    PCSR_CAPTURE_HEADER RelatedCaptureBuffer;
    ULONG CountMessagePointers;
    PCHAR FreeSpace;
    ULONG_PTR MessagePointerOffsets[1]; // Offsets within CSR_API_MSG of pointers
} CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER;

typedef ULONG CSR_API_NUMBER;

typedef struct _CSR_API_MSG {
    PORT_MESSAGE h;
    union {
        CSR_API_CONNECTINFO ConnectionRequest;
        struct {
            PCSR_CAPTURE_HEADER CaptureBuffer;
            CSR_API_NUMBER ApiNumber;
            ULONG ReturnValue;
            ULONG Reserved;
            union {
                //
                // Place data for srv here
                //
            } u;
        };
    };
} CSR_API_MSG, *PCSR_API_MSG;
/************************************************************************************/

Une fois qu’on connaît ces structures on peut s’interfacer avec le subsystem en utilisant la fonction CsrClientCallServer. Le problème c’est qu’il faut connaître les structures spécifiques aux APIs qu’on veut utiliser et là, ce n’est pas gagner. Celles de winsrv.dll qui gèrent la console se reversent assez facilement et sont en partie disponibles ici, et celles de basesrv.dll le sont .

Remarquez dans la ligne de commande de csrss les indices après les noms des fonctions d’init :
ServerDll=basesrv,1
ServerDll=winsrv:ConServerDllInitialization,2
ServerDll=winsrv:UserServerDllInitialization,3

Ces indices permettent de savoir quel provider utiliser puis quelle fonction appeler (les fonctions ont aussi un indice). Attention comme winsrv.dll fournit 2 interfaces il existe 2 indices de départ : BASESRV_FIRST_API_NUMBER qui vaut 0 et CONSRV_FIRST_API_NUMBER qui vaut 512.

En fait, le mécanisme intéressant est la manipulation des buffers entre le process client et le subsystem, car un LPC ne se casse pas la tête, il transmet juste un buffer entre 2 processes. Si le client à besoin de fournir une zone mémoire destinée à recevoir des données du serveur, il doit utiliser un CaptureBuffer avec CsrAllocateCaptureBuffer qui va allouer de l’espace dans un heap spécial du process, le CsrPortHeap. Ce heap vaut de l’or car il est situé dans une section qui est accessible par le subsystem ! C’est ce qu’on appel un Port Memory section … Pour s’en assurer vous pouvez regarder la fonction CsrpConnectToServer de ntdll et voir un appel à ZwCreateSection. Ensuite il faut appeler CsrCaptureMessageBuffer afin que le pointeur sur le buffer de destination des datas pointe sur notre CaptureBuffer. Par exemple :

typedef struct _CSR_CAPTURE_HEADER {
    ULONG Length;
    PCSR_CAPTURE_HEADER RelatedCaptureBuffer;
    ULONG CountMessagePointers;
    PCHAR FreeSpace;
    ULONG_PTR MessagePointerOffsets[1]; // Offsets within CSR_API_MSG of pointers
} CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER;

CSR_CAPTURE_HEADER CaptureBuffer;

CaptureBuffer=CsrAllocateCaptureBuffer(1, b->TitleLen);

/* valeurs lues sous olly
CsrAllocateCaptureBuffer
CsrHeader.Length=0x11C
CsrHeader.RelatedCaptureBuffer=0x260178
CsrHeader.CountMessagePointers=0
CsrHeader.FreeSpace=0x26069C
CsrHeader.MessagePointerOffsets[0]=0;
*/

CsrCaptureMessageBuffer(CaptureBuffer,
                           	NULL,
                           	b->TitleLen,
                          	(PVOID *)&b->Title);
/*	valeurs lues sous olly
CsrAllocateCaptureBuffer
CsrHeader.Length=0x11C
CsrHeader.RelatedCaptureBuffer=0x260178
CsrHeader.CountMessagePointers=1
CsrHeader.FreeSpace=0x2607A0
CsrHeader.MessagePointerOffsets[0]=0x12FF40; //Pointeur sur l'adresse du buffer alloué par CsrAllocateCaptureBuffer, les datas suivent le CaptureBuffer
*/

En gros toutes ces opérations servent à translater les pointeurs des buffers du client vers le serveur. Pour finir, la fonction CsrClientCallServer effectuera le traitement final en remplissant le champ CaptureBuffer de la structure CSR_API_MSG puis appellera ZwRequestWaitReplyPort. Arrivé dans le serveur avec le LPC, un des threads CsrApiRequestThread qui attend avec ZwReplyWaitReceivePort dispatchera la requête. Bien évidemment le buffer est vérifié dans le serveur avec CsrValidateMessageBuffer :P

Le code suivant est simplement une ré-implemenation de GetConsoleTitle (ou plutôt de GetConsoleTitleInternal pour être précis) de kernel32.dll. On retrouve quasiment le même code dans ReactOS.

HANDLE GetConsoleHandle(VOID)
{
	__asm
	{
		mov eax, fs:[18h]
		mov eax, [eax+30h]
		mov eax, [eax+10h]
		mov eax, [eax+10h]
	}
}

int main(int argc, char ** argv)
{	
	NTSTATUS Status;
	CSR_API_MSG m;
	PCONSOLE_TITLE_MSG b=&m.u.ConsoleTitle;
	PCSR_CAPTURE_HEADER CaptureBuffer;
	
	RtlZeroMemory(&m, sizeof(m));
	
	b->ConsoleHandle=GetConsoleHandle();
	b->TitleLen=260;
	b->Unicode=0;
	
	CaptureBuffer=CsrAllocateCaptureBuffer(1, b->TitleLen);
	
	CsrCaptureMessageBuffer(CaptureBuffer,
                           	NULL,
                           	b->TitleLen,
                          	(PVOID *)&b->Title);
	
	Status=CsrClientCallServer((PCSR_API_MSG)&m, 
							CaptureBuffer,
							CSR_MAKE_API_NUMBER(CONSRV_SERVERDLL_INDEX, CONSRV_FIRST_API_NUMBER+38), //38=SrvGetConsoleTitle index
							sizeof(*b));
	if(!NT_SUCCESS(Status))
	{
		printf("Error with CsrClientCallServer : 0x%X\n", Status);
		CsrFreeCaptureBuffer(CaptureBuffer);
		return 0;	
	}
	
	printf("ConsoleTitle is : %s\n", m.u.ConsoleTitle.Title);
	
	CsrFreeCaptureBuffer(CaptureBuffer);
	return 0;	
}

Pour les autres interfaces, je vous laisser creuser :], vous pouvez reverser les APIs de kernel32.dll pour retrouver les structures et faire mumuse avec le subsystem.

Voici le code+binaire de MyGetConsoleTitle :
http://ivanlef0u.fr/repo/MyGetConsoleTitle.rar

En espérant que vous continuerez à fouiner le sujet :]

Entry Filed under: RE

13 Comments

  • 1. Taron  |  septembre 3rd, 2008 at 21:01

    Sympathique, et on peut espérer quoi d’un point de vue plus « sombre »? parce que j’avais déjà vu le poste sur sysinternals qui lister tous les process.. c’est possible de les planque ?


  • 2. admin  |  septembre 3rd, 2008 at 21:13

    Waip on peut.


  • 3. newsoft  |  septembre 6th, 2008 at 10:25

    Tu as oublié de préciser: « (…), entouré de psychopathes, qui mangent des fruits de mer périmés, chient à même le sol, voire écoutent du rap ».

    En tout cas je suis ravi de voir que tout cela ne t’a pas dégouté de l’informatique :)


  • 4. shikamaru  |  septembre 6th, 2008 at 23:29

    enfin un post, aller un peut de serieux c’est vraiment trop cool ton blog continue


  • 5. Orkblutt  |  septembre 12th, 2008 at 11:20

    Très interessant!

    Taron, peux tu nous donner le lien du post sur sysinternals ?
    Merci


  • 6. Orkblutt  |  septembre 12th, 2008 at 11:25

    ooops j’avais mal cherché…
    http://forum.sysinternals.com/forum_posts.asp?TID=15457&KW


  • 7. Elo  |  septembre 12th, 2008 at 13:10

    Je viens de lire l’article sur sysinternals,et je me pose une question. Toute la technique repose sur la récupération de CsrHashThread, mais je ne suis pas sûr de la manière dont il s’y prend. Il dit utiliser une ligne de ce type « lea edx, CsrHashThread.anonymous_0[eax*8] » dans une fonction exportée… Mais comment récupère-t-il edx ? Parce que ce registre est modifié avant la sortie de la fonction… Il faut débugger la fonction ? Cela nécessite des droits particuliers non ?


  • 8. admin  |  septembre 12th, 2008 at 15:49

    Il sait juste que la fonction exportée CsrLockThreadByClientId contient à l’offet 0xXX depuis son début un pointeur sur la variable globale CsrThreadHashTable, il la récupère comme ça. Après évidemment pour obtenir ces infos il faut s’inject dans le process csrss.exe et donc avec le SeDebugPrivilege.


  • 9. Elo  |  septembre 12th, 2008 at 16:01

    J’ai commencé à implémenter le tout en user land, et je me pose plusieurs questions :
    - Lorsqu’on déclare une variable dans une DLL, celle-ci n’est pas globale ? Est-ce qu’on ne pourrait pas tout simplement charger la DLL csrss.dll en mémoire dans le processus courant afin de retrouver CsrRootProcess ? Ou serait-ce un CsrRootProcess différent de celui maintenu par csrss.exe ?
    - Dans son executable, l’auteur du post sur sysinternals appelle les fonctions ZwXxX (par exemple ZwOpenProcess ou ZwAllocateVirtualMemory) pour dialoguer avec csrss.exe . Quel avantage par rapport aux fonctions « classiques », comme OpenProcess ou VirtualAlloc ?
    - Enfin, comment faire cela sans le SeDebugPrivilege ? Pour moi, ce token est nécessaire rien que pour OpenProcess… Pourtant, l’executable de sysinternals fonctionne même sans ce token, seul un message d’avertissement apparaît disant que ce n’est pas grave… pourquoi chercher à l’obtenir si ce n’est pas utile ?

    Merci d’avance !


  • 10. 0vercl0k  |  septembre 14th, 2008 at 12:31

    Salut à toi « Shadow »,

    Alors tout d’abord, tu peux bien sur utiliser un LoadLibrary afin de charger la dll dans ton implémentation ; tu pourras la scanner à la recherche du « mov esi, _CsrRootProcess ».
    M’enfin il faut tout de même savoir que cette valeur est un LIST_ENTRY**.
    Une fois récupéré cette valeur il te suffit de lire la mémoire du processus afin de récupérer le simple pointeur sur la LIST_ENTRY (celui-ci pointerait dans le tas de csrss.exe).

    Ensuite, concernant les appels des fonctions ZwXXX, il n’y a aucun « avantage » dans ce cas-ci à les utiliser en userland ; les apis tels que OpenProcess, WriteProcessMemory te faciliteront bien plus la tâche que d’appeler directement les apis natifs.

    Concernant le changement de token, il est obligatoire pour aller trafiqué le subsystem csrss.

    En espérant avoir répondu à tes intérrogations, bonne soirée à toi.
    Cordialement 0vercl0k.


  • 11. xxx  |  septembre 19th, 2008 at 08:46

    how can i download the rar?


  • 12. Yo  |  octobre 31st, 2010 at 20:28

    Sérieusement ton blog c’est de la bombe pour le peu que j’ai vu. J’ai hâte de voir la suite. Bye


  • 13.  |  mai 15th, 2012 at 16:31

    [...] as well. This breakpoint won't work if someone writes a version of runpe that talks directly to the csrss subsystem to launch a process, but let's just hope that never happens. After breaking on [...]


Trackback this post


Calendar

novembre 2019
L Ma Me J V S D
« fév    
 123
45678910
11121314151617
18192021222324
252627282930  

Most Recent Posts