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 là.
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
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