Работа с буфером обмена в kernel-mode

Discussion in 'Реверсинг' started by 0x0c0de, 29 Mar 2009.

  1. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    [Работа с буфером обмена в ядре.]

    Решила написать такую вот небольшую заметку о работе с буфером обмена в ядре.

    Для начала рассмотрим типичный код для работы с буфером обмена в юзермоде.

    Code:
    if(OpenClipboard(GetForegroundWindow()))
    	{
    		EmptyClipboard();
    	
    		hGlobalData = GlobalAlloc(GHND,150);
    
    		if(hGlobalData)	
    		{	
    			
    			lstrcpy((LPWSTR)GlobalLock(hGlobalData),
    				L"Hello from clipboard");
    	
    			GlobalUnlock(hGlobalData);
    
    			SetClipboardData(CF_UNICODETEXT,hGlobalData);
    			
    		}	
    		CloseClipboard();
    }
    
    Из этого становиться ясна последовательность действий. Сначала открыть буфер обмена, очистить его, выделить память, передать ее хендл в функцию SetClipboardData. И закрыть буфер обмена. OpenClipboard,EmptyClipboard, GetForegroundWindow сводятся к вызову сервисов win32k.sys. И с вызовом в ядре проблем не возникнет, так как мы тестировать драйвер будем DeviceIoControl - ом. В общем случае можно приаттачиться к какому-нибудь процессу, что тоже известно как делать из драйвера.
    Теперь к ресечу непосредственно. Каким образом вставить данные в буфер обмена? Обратимся к коду функции SetClipboardData.

    Code:
    
    7E380F83   6A 00            PUSH 0
    7E380F85   53               PUSH EBX
    7E380F86   8975 FC          MOV DWORD PTR SS:[EBP-4],ESI
    7E380F89   E8 BB000000      CALL USER32._ConvertMemHandle@8
    7E380F8E   8BF8             MOV EDI,EAX
    
    ...
    7E380FCC   50               PUSH EAX
    7E380FCD   57               PUSH EDI
    7E380FCE   FF75 08          PUSH DWORD PTR SS:[EBP+8]
    7E380FD1   8975 F8          MOV DWORD PTR SS:[EBP-8],ESI
    7E380FD4   E8 5CFFFFFF      CALL USER32._NtUserSetClipboardData@12
    7E380FD9   85C0             TEST EAX,EAX
    
    
    Первым делом вызывается неэкспортируемая функция ConvertMemHandle, возвращаемое значение которой и будет передано в NtUserSetClipboardData.Надо сказать, что передаваемые адреса должны быть юзермодные. Посмотрим на код NtUserConvertMemHandle

    Code:
    .text:BF8ECE5C                 mov     esi, [ebp+arg_4]
    .text:BF8ECE5F                 test    esi, esi
    .text:BF8ECE61                 jz      short loc_BF8ECE75
    .text:BF8ECE63                 mov     ecx, [ebp+arg_0]
    .text:BF8ECE66                 lea     eax, [ecx+esi]
    .text:BF8ECE69                 cmp     eax, ecx
    .text:BF8ECE6B                 jb      short loc_BF8ECE93
    .text:BF8ECE6D                 cmp     eax, _Win32UserProbeAddress
    .text:BF8ECE73                 ja      short loc_BF8ECE93
    .text:BF8ECE75
    .text:BF8ECE75 loc_BF8ECE75:                           ; CODE XREF: NtUserConvertMemHandle(x,x)+1Aj
    .text:BF8ECE75                                         ; NtUserConvertMemHandle(x,x)+52j
    .text:BF8ECE75                 or      [ebp+ms_exc.disabled], 0FFFFFFFFh
    .text:BF8ECE79                 push    esi
    
    В NtUserSetClipboardData

    Code:
    .text:BF8ECD89                 xor     esi, esi
    .text:BF8ECD8B                 cmp     eax, esi
    .text:BF8ECD8D                 jnz     short loc_BF8ECDC2
    .text:BF8ECD8F                 mov     [ebp+ms_exc.disabled], esi
    .text:BF8ECD92                 mov     edx, [ebp+arg_8]
    .text:BF8ECD95                 mov     eax, _Win32UserProbeAddress
    .text:BF8ECD9A                 cmp     edx, eax
    .text:BF8ECD9C                 jnb     short not_usermode 
    
    Как видим, адрес сверяется с Win32UserProbeAddress - наибольшим адресом для юзермода.
    Значит в драйвере заюзаем функции RtlCreateHeap и RtlAllocateHeap.

    Code:
    hHeap = RtlCreateHeap(0,
    			0,
    			0,
    			0,
    			0,
    			0);
    
    
    	pMem = RtlAllocateHeap(hHeap,HEAP_ZERO_MEMORY,30);
    		
    
    	if(pMem)
    		{
    ....
    ...
    
                                              RtlFreeHeap(hHeap,0,pMem);
    		}
    
    RtlDestroyHeap(hHeap);
    

    Теперь о том, как получить данные из буфера обмена. Все дело в том, что просто так, вызовом NtUserGetClipboardData нам данные из буфера обмена не получить. Так как эта функция возвращает хендл. Чтобы по хендлу получить нужные данные нужна функция CreateLocalMemHandle.
    Код внутренней функции CreateLocalMemHandle.

    Code:
     
    7E380ECC > 8BFF             MOV EDI,EDI
    7E380ECE   55               PUSH EBP
    7E380ECF   8BEC             MOV EBP,ESP
    7E380ED1   51               PUSH ECX
    7E380ED2   56               PUSH ESI
    7E380ED3   8D45 FC          LEA EAX,DWORD PTR SS:[EBP-4]
    7E380ED6   50               PUSH EAX
    7E380ED7   6A 00            PUSH 0
    7E380ED9   6A 00            PUSH 0
    7E380EDB   FF75 08          PUSH DWORD PTR SS:[EBP+8]
    7E380EDE   E8 3E000000      CALL USER32._NtUserCreateLocalMemHandle@>
    7E380EE3   3D 230000C0      CMP EAX,C0000023 //STATUS_BUFFER_TOO_SMALL
    7E380EE8   75 2E            JNZ SHORT USER32.7E380F18
    7E380EEA   FF75 FC          PUSH DWORD PTR SS:[EBP-4]
    7E380EED   6A 00            PUSH 0
    7E380EEF   FF15 2C13367E    CALL DWORD PTR DS:[<&KERNEL32.GlobalAllo>; kernel32.GlobalAlloc
    7E380EF5   8BF0             MOV ESI,EAX
    7E380EF7   85F6             TEST ESI,ESI
    7E380EF9   74 1D            JE SHORT USER32.7E380F18
    7E380EFB   6A 00            PUSH 0
    7E380EFD   FF75 FC          PUSH DWORD PTR SS:[EBP-4]
    7E380F00   56               PUSH ESI
    7E380F01   FF75 08          PUSH DWORD PTR SS:[EBP+8]
    7E380F04   E8 18000000      CALL USER32._NtUserCreateLocalMemHandle@>
    7E380F09   85C0             TEST EAX,EAX
    7E380F0B   0F8C 4E6F0100    JL USER32.7E397E5F
    7E380F11   8BC6             MOV EAX,ESI
    7E380F13   5E               POP ESI
    7E380F14   C9               LEAVE
    7E380F15   C2 0400          RETN 4
    
    То есть сначала по идее надо запросить количество байт, требуемых под буфер, потом выделить память и снова вызвать сервис NtUserCreateLocalMemHandle. Из этого кода можно уже сказать, что возвращаемое значение типа NTSTATUS. Первый параметр - возвращенный NtUserGetClipboardData хендл, второй - буфер, куда надо поместить данные, третий - длина буфера, четвертый - опциональный параметр - указатель на переменную, куда следуемт поместить количество требуемых байт.

    Теперь для вызова функций по указателю напишем в драйвере

    Code:
    typedef HANDLE (__stdcall*_NtUserGetForegroundWindow_)(VOID);
    typedef ULONG (__stdcall*_NtUserOpenClipboard_)(HANDLE,PHANDLE);
    typedef ULONG (__stdcall*_NtUserEmptyClipboard_)(VOID);
    typedef HANDLE (__stdcall*_NtUserGetClipboardData_)(ULONG, PVOID);
    typedef ULONG (__stdcall*_NtUserSetClipboardData_)(ULONG, HANDLE, PULONG);
    typedef ULONG (__stdcall*_NtUserCloseClipboard_)(VOID);
    typedef HANDLE (__stdcall*_NtUserConvertMemHandle_)(PVOID,ULONG);
    typedef NTSTATUS (__stdcall*_NtUserCreateLocalMemHandle_)(HANDLE,PVOID,ULONG,PULONG);
    
    А вызывать функции будем так

    Code:
    pNtGetForegroundWindow = 
    
    (_NtUserGetForegroundWindow_)ShadowSsdtTable[1].ServiceTable[_puServiceTable[NtUserGetForegroundWindow]];
    
    ...
    
    hWindow = pNtGetForegroundWindow();
    
    Нужные нам сервисы находятся в win32k.sys, поэтому найдем сначала shadow ssdt. Я думаю, приводить его уже не надо, так как это всем известный метод.

    Хорошо, вроде как разобрались. Теперь другой вопрос - получение номеров нужных системных сервисов. Получать их будем конечно же в юзермодной проге. Я очень не люблю привязываться к версии оси, это крайний случай. Однако, я уже почти согласилась на него.

    Почему сейчас расскажу. Из всего вышеизложенного понятно, что нам надо добраться до номеров сервисов

    NtUserGetForegroundWindow - так как я не хотела создавать свое окно. В общем случае лучше создавать свое, конечно
    NtUserOpenClipboard
    NtUserEmptyClipboard
    NtUserCloseClipboard
    NtUserConvertMemHandle
    NtUserSetClipboardData
    NtUserGetClipboardData
    NtUserCreateLocalMemHandle

    Со списком определились. Посмотрим на код юзермодной GetForegroundWindow.

    Code:
    7E379823 > B8 94110000      MOV EAX,1194
    7E379828   BA 0003FE7F      MOV EDX,7FFE0300
    7E37982D   FF12             CALL DWORD PTR DS:[EDX]
    7E37982F   C3               RETN
    
    То есть вот отсюда спокойно можно откопировать нужный номер. В функции OpenClipboard чуть больше кода и номер системного сервиса надо взять из подпроцедуры. Что тоже не проблема.

    Code:
    7E380277 > 8BFF             MOV EDI,EDI
    7E380279   55               PUSH EBP
    7E38027A   8BEC             MOV EBP,ESP
    7E38027C   56               PUSH ESI
    7E38027D   8D45 08          LEA EAX,DWORD PTR SS:[EBP+8]
    7E380280   50               PUSH EAX
    7E380281   FF75 08          PUSH DWORD PTR SS:[EBP+8]
    7E380284   E8 18000000      CALL USER32._NtUserOpenClipboard@8
    7E380289   837D 08 00       CMP DWORD PTR SS:[EBP+8],0
    7E38028D   8BF0             MOV ESI,EAX
    7E38028F   0F85 F6090000    JNZ USER32.7E380C8B
    7E380295   8BC6             MOV EAX,ESI
    7E380297   5E               POP ESI
    7E380298   5D               POP EBP
    7E380299   C2 0400          RETN 4
    
    Не проблема, если есть дизассемблер длин. Я сначала думала как обычно продизасмить, дойти до инструкции call адрес и скопировать номер сервиса. Но потом взглянула на функцию SetClipboardData и поняла, что так не получиться. Так как на разных системах код сильно отличается и универсального способа, вроде как нет. Но только вроде как. Я точно знаю, что первый вызов в SetClipboardData - NtUserConvertMemHandle, а второй - NtUserSetClipboardData, так воспользуемся этим. Трассировку ведь никто не отменял. Дада, самую обычную трассировку с флагом TF. Загрузить копию user32 (чтобы избежать хуков в этой либе), найти адреса нужных функций и потрейсить их в поисках нужных адресов сервисов. Алгоритм поиска прост. Устанавливаем векторный обработчик исключений. Находим адреса соответствующих функций в копии user32.dll, потом ставим int 3 на начало функции. Вызываем эту функцию в копии библиотеки. Первая инструкция вызываемой функции будет int 3 и нас перебросит в векторый обработчик исключений. Он, в свою очередь проверит, какой эксепшн его вызвал, если это было 0x80000003, то восставливаем затертый int 3 байт. Далее трассировка идет уже с использованием флага TF. Писать про то, что эксепшн может быть и не мой (в смысле int 3 сработало, но не там, где я его поставила), не надо, я в курсе. По идее да, надо проверять, там ли мы брякнулись и в случае, если не там возвращать EXCEPTION_CONTINUE_SEARCH. Добавить код проверок дополнительных не проблема. В качестве демонстрации итак норм.

    Для вызова функций удобно сделать такие объявления

    Code:
    typedef HWND (__stdcall*_GetForegroundWindow_)(VOID);
    typedef BOOL (__stdcall*_OpenClipboard_)(HWND);
    typedef BOOL (__stdcall*_EmptyClipboard_)(VOID);
    typedef HANDLE (__stdcall*_SetClipboardData_)(UINT,HANDLE);
    typedef HANDLE (__stdcall*_GetClipboardData_)(UINT);
    typedef BOOL (__stdcall*_CloseClipboard_)(VOID);
    
    Функция, которая подготавливает к началу трассировки

    Code:
    DWORD SearchServiceNumberStart(DWORD DllBase,LPCSTR FunctionName)
    {
    
    	dwAddress4Disasm = (DWORD)GetProcAddress((HMODULE)DllBase,FunctionName);
    
    	// вычисляем новый адрес функции в смэппленом образе. rva не меняется, поэтому просто меняем базу
    	dwAddress4Disasm = dwAddress4Disasm - DllBase + (DWORD)hUsrDll;
    
    
    	// сохраняем переписанный int 3 байт в глобальной переменной
    	 bSavedByte = *(PUCHAR)dwAddress4Disasm;
    
    	 // ставим int 3 на начало функции
    	*(PUCHAR)dwAddress4Disasm = 0xCC;
    
    
    	// возвращаем адрес функции в смэппленом образе
    	return dwAddress4Disasm;
    }
    
    Сам обработчик проверяет, на какой инструкции мы находимся и если номер сервиса найден, то заканчивает трассировку

    Code:
    // собственно трейсер, юзающий  VEH
    LONG WINAPI HandlerSearch(PEXCEPTION_POINTERS ExceptionInfo)
    {
    
    	DWORD dwCurrentAddress;
    
    	dwCurrentAddress = (DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress;
    
    	// если мы в самом начале функции, то переписываем затертый байт обратно
    	if(ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_BREAKPOINT)
    	{
    
    		*(PUCHAR)dwCurrentAddress = bSavedByte;
    
    	}
    
    		// мы на инструкции mov eax,imm32, а следующая mov edx,imm32? 
    
    		
    
    if((*(PUCHAR)dwCurrentAddress==0xB8)&(*((PUCHAR)dwCurrentAddress+5)==0xBA)&(*(PULONG)(dwCurrentAddress+1)>=0x1000))
    		{
    
    			if(!dwNested)
    			{
    			
    				// записываем номер сервиса - 0x1000 
    			
    				ServiceNumberTable[dwIndexService] = *(PULONG)(dwCurrentAddress+1) - 0x1000;
    
    	
    			}else
    			{
    				// если надо пропустить какой-либо сервис
    				dwNested -= 1;
    
    				// продолжаем трейсить
    				
    				// устанавливаем TF
    				ExceptionInfo->ContextRecord->EFlags |=0x100; 
    
    			}
    
    			// продолжаем трейсить
    		}else ExceptionInfo->ContextRecord->EFlags |=0x100; 
    
    	return EXCEPTION_CONTINUE_EXECUTION;
    }
    
    Пример использования

    Code:
    // dwNested - сколько вызовов пропускать
    			//NtUserGetForegroundWindow
    			dwNested = 0;
    			
    			dwIndexService = NtUserGetForegroundWindow;
    			
    			_GetForegroundWindow_  MapGetForegroundWindow = 
    
    (_GetForegroundWindow_)SearchServiceNumberStart((DWORD)pUser32Base,
    				"GetForegroundWindow"); 
    			
    			// вызываем функцию, запускаем трейс
    			HWND hwndCurr = MapGetForegroundWindow();
    
    dwIndexService - это переменная, в которой храниться номер в массиве сервисов, который мы передадим в драйвер. Я все проименовала, чтобы было читабельней.

    Code:
    #define NtUserGetForegroundWindow 0
    #define NtUserOpenClipboard 1
    #define NtUserEmptyClipboard 2
    #define NtUserSetClipboardData 3
    #define NtUserGetClipboardData 4
    #define NtUserCloseClipboard 5
    #define NtUserConvertMemHandle 6
    #define NtUserCreateLocalMemHandle 7
    
    Если вы читали внимательно комменты в коде, то уже наверно поняли, что я пропускаю нужное количество вызовов. В случае с SetClipboardData

    Code:
    
    			//NtUserConvertMemHandle
    			dwNested = 0;
    
    			dwIndexService = NtUserConvertMemHandle;
    			
    			_SetClipboardData_  MapSetClipboardData= 
    
    (_SetClipboardData_)SearchServiceNumberStart((DWORD)pUser32Base,
    				"SetClipboardData"); 
    			
    			HANDLE hGlobalData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,10);
    
    			RtlCopyMemory(hGlobalData,L"Run",6);
    
    			MapSetClipboardData(CF_UNICODETEXT,hGlobalData);
    
    			//NtUserSetClipboardData
    
    			dwNested = 1;
    
    			dwIndexService = NtUserSetClipboardData;
    			
    			MapSetClipboardData= (_SetClipboardData_)SearchServiceNumberStart((DWORD)pUser32Base,
    				"SetClipboardData"); 
    
    
    			MapSetClipboardData(CF_UNICODETEXT,hGlobalData);
    
    dwNested для первого вызова -0, для второго (1 пропускаем) - 1.
    Насчет другой функции - GetClipboardData. Первый вызов NtUserGetClipboardData, а второй - NtUserCreateLocalMemHandle, в общем по аналогии делается (dwNested = 0 для NtUserGetClipboardData и 1 для NtUserCreateLocalMemHandle).
    Только не говорите, что надо мэппить все остальные либы, учитывать все возможные хуки еще и в других либах, а в коде хука может быть антитрассировка и прочее, уже не хотелось с этим заморачиваться просто. У меня вот стояли хуки комодо в ntdll, через них трейс нормально прошел. Как видите, дизассемблер тут даже не нужен.

    Драйвер использует функции RtlCreateHeap, RtlAllocateHeap, RtlFreeHeap. А если мы посмотрим в msdn

    http://msdn.microsoft.com/en-us/library/ms797739.aspx

    "This routine is available on Microsoft Windows XP and later."

    + я использую векторную обработку исключений. Так что кодес для систем начиная с XP.

    Этот код я протестировала на висте, своей XP SP3, SP2 и win7. Работает нормально.

    На этом пока прощаюсь, надеюсь кому-то было познавательно.

    ссылка на сорс

    http://dump.ru/file/2315384
     
    7 people like this.
  2. Qws

    Qws Elder - Старейшина

    Joined:
    17 Jun 2008
    Messages:
    46
    Likes Received:
    57
    Reputations:
    5
    Молодца... так держать.
     
  3. 0verbreaK

    0verbreaK Elder - Старейшина

    Joined:
    30 Apr 2008
    Messages:
    318
    Likes Received:
    42
    Reputations:
    -3
    Спасибо за статью, по больше бы таких, будет ли еще про ядро?
     
  4. desTiny

    desTiny Elder - Старейшина

    Joined:
    4 Feb 2007
    Messages:
    1,006
    Likes Received:
    444
    Reputations:
    94
    ммм... а как тебе такой метод определения номера сервиса:
    вспоминаем, что в KTHREAD есть ServiceTable, в юзермоде это Shadow SDT.
    узнаём размер SST и создаём SST такого же размера где-нибудь, записываем где-то по адресу X UD2 столько раз, сколько записей в SST, забиваем её целиком адресами этих разных уд-шек.
    После чего вызываем функу, которая переводит стрелки на системный сервис и вуаля получаем инвалид опкод. Адрес, где этот опкод выполнился, узнать нетрудно, а зная адрес получаем номер как (адрес-X)/2 (2 - длина UD2) (кста можно вообще без исключений - вместо UD2 поставить инструкцию типа mov [], 1)

    //edit: надеюсь, все поняли, что под юзермодом следует понимать гуй :)
     
    #4 desTiny, 25 Apr 2009
    Last edited: 26 Apr 2009
  5. AlexGT

    AlexGT Banned

    Joined:
    21 Jan 2008
    Messages:
    1
    Likes Received:
    18
    Reputations:
    0
    0x0c0de ты ассемблерная террористка :)
     
  6. winterfrost

    winterfrost Elder - Старейшина

    Joined:
    18 Aug 2008
    Messages:
    42
    Likes Received:
    18
    Reputations:
    15
    0x0c0de спасибо за исходники, класные =)
    desTiny интересная идея, а у тебя негде примерчика на завалялось? =). Насколько я понял, надо будет вызвать соответсвующюю юзермодную api в нашем треде (с фэйковой табличкой сервисов)? Т.е. определить номер сериса не выйдет не выходя из ядра? И смещение указателя на таблицу сервисов в KTHREAD изменяеться в разных версиях, как и номера сервисов.
     
  7. desTiny

    desTiny Elder - Старейшина

    Joined:
    4 Feb 2007
    Messages:
    1,006
    Likes Received:
    444
    Reputations:
    94
    >> примерчика на завалялось?
    нет.. просто такой вот крестьянский способ с трассировкой мне не понравился, и такой в голову пришёл. Реализацию (от меня), если раньше не будет, ждать можно не раньше июня.
    >> Т.е. определить номер сериса не выйдет не выходя из ядра?
    ну можно и из ядра, если юзермодная функция не оперирует никакими юзермодными адресами - то просто её и в ядре вызвать можно) если юзает - то поймать ошибки тоже можно - нам почти наплевать на большую часть их кода - нам только перед сисэнтером интересно, какая константа записывается
    >>И смещение указателя на таблицу сервисов в KTHREAD изменяеться в разных версиях
    разве меняется? вроде это документированная структура... а даже если и меняется... вот я только что такой чит придумал - берём какой-нить тред, для которого гарантировано сдт - это не шадоу и сканим всю структурку на известный адрес - вот и смещение)
    >>номера сервисов
    вот их-то мы-то и хотим найти-то)
     
  8. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Сори, что так долго молчала. Решаю проблемы с учебой.

    desTiny: крестьянский способ. Нормальный способ и рабочий (но долгий). Как я уже сказала тебе в жабере, что пока практической реализации нет, по поводу твоего способа ничего не хочу писать. В выходные постараюсь собраться и накодить если что.

    Сегодня реализовала через хук KiFastSystemCall. Все отлично. Никакой трассировки нет, работает быстрее.

    //8.05.09: исправила, KiFastSystemCall конечно же, а не KiFastSystemCallRet=\
     
    #8 0x0c0de, 8 May 2009
    Last edited: 8 May 2009
  9. winterfrost

    winterfrost Elder - Старейшина

    Joined:
    18 Aug 2008
    Messages:
    42
    Likes Received:
    18
    Reputations:
    15
    ато! ещё как меняется =), вот например xp sp2:
    kd> dt _KTHREAD
    +0x0e0 ServiceTable : Ptr32 Void

    а вот виста sp1:
    +0x12c ServiceTable : Ptr32 Void
    Ну и ещё она не документированная. Впрочем, вижу что тебя это не смущает =)
     
  10. desTiny

    desTiny Elder - Старейшина

    Joined:
    4 Feb 2007
    Messages:
    1,006
    Likes Received:
    444
    Reputations:
    94
    А я и не говорю, что не нормальный) Просто он.. как бы сказать.. не изящен.. а когда я писал, мне что-то вспомнился препод по матану из первого семестра, а он любил подобные способы крестьянскими называть)

    Да, такая идея тоже напрашивается. Но всё-таки не хочется ничего глобального портить.. а свой поток положить на жертвенный алтарь не так страшно. Но это действительно самый лаконичный вариант..

    Обидно.. но тогда можно считать, что я предложил новый способ нахождения shadow sdt )
     
  11. desTiny

    desTiny Elder - Старейшина

    Joined:
    4 Feb 2007
    Messages:
    1,006
    Likes Received:
    444
    Reputations:
    94
    Finito:
    В результате продолжительного обсуждения было принято решение о том, что оказывается KiFastSystemCall и KiFastSystemCallRet экспортируются. Вроде при таком допущении номер сервиса выводится в одно действие )
     
  12. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Как и обещала, метод я реализовала. Время на радостях после успешно завершенной зачетной недели выкроилось.
    Создаем свою SDT, ntoskrnl SST оставляем прежней (копируем из оригинальной SST), создаем свою таблицу вместо win32k.sys SST (при этом указатель на argument table берем из оригинальной таблицы), а адреса всех функций заполняются адресом моего обработчика, затем прописываем новую SDT в KTHREAD.ServiceTable.

    Обработчик для всех функций, логирующий номера вызываемых сервисов.

    Т.к. приложение, юзающее такое определение номеров сервисов (отсылающее драйверу ioctl запросы) консольное- проблем не возникает.

    Code:
     
    VOID _declspec(naked) MyHandlerServiceSearch(VOID)
    {
    	_asm
    	{
    		pushad
    
    		mov uEax,eax
    	}
    	KdPrint(("MyHandlerServiceSearch-> Service number = %X\n",uEax));
    
    	if(uServiceCount<6)
    	{
    		// сохраняем номер сервиса
    		uServTable[uServiceCount] = uEax;
    
    		uServiceCount++;
    
    	}
    	else KdPrint(("Too many calls"));
    
    	// вычисляем адрес оригинальной функции
    	uOriginalCallAddress = (ULONG)uShadow[1].ServiceTable[uEax];
    
    	_asm
    	{
    		popad
    
    		// вызываем оригинальную функцию
    		jmp [uOriginalCallAddress]
    
    	}
    }
    
    

    [​IMG]
    [​IMG]

    Надо сказать, что на win7 там иногда не с первого раза определяются номера. причина этого мне неизвестна. Я сделала цикл до тех пор, пока не определится. Кстати, на скрине с семеркой видно, что первая попытка неуспешна, зато на второй раз все определилось.

    Однако, все равно остаюсь при своем способе. этот пускай будет
     
    #12 0x0c0de, 1 Jun 2009
    Last edited: 1 Jun 2009
    2 people like this.