ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > Программирование > Реверсинг
   
 
 
Опции темы Поиск в этой теме Опции просмотра

Работа с буфером обмена в kernel-mode
  #1  
Старый 29.03.2009, 12:53
Аватар для 0x0c0de
0x0c0de
Постоянный
Регистрация: 25.05.2007
Сообщений: 448
Провел на форуме:
4226446

Репутация: 1564
Отправить сообщение для 0x0c0de с помощью ICQ
По умолчанию Работа с буфером обмена в kernel-mode

[Работа с буфером обмена в ядре.]

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

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

Код:
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.

Код:
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

Код:
.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

Код:
.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.

Код:
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.

Код:
 
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 хендл, второй - буфер, куда надо поместить данные, третий - длина буфера, четвертый - опциональный параметр - указатель на переменную, куда следуемт поместить количество требуемых байт.

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

Код:
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);
А вызывать функции будем так

Код:
pNtGetForegroundWindow = 

(_NtUserGetForegroundWindow_)ShadowSsdtTable[1].ServiceTable[_puServiceTable[NtUserGetForegroundWindow]];

...

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

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

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

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

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

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

Код:
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. Добавить код проверок дополнительных не проблема. В качестве демонстрации итак норм.

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

Код:
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);
Функция, которая подготавливает к началу трассировки

Код:
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;
}
Сам обработчик проверяет, на какой инструкции мы находимся и если номер сервиса найден, то заканчивает трассировку

Код:
// собственно трейсер, юзающий  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;
}
Пример использования

Код:
// dwNested - сколько вызовов пропускать
			//NtUserGetForegroundWindow
			dwNested = 0;
			
			dwIndexService = NtUserGetForegroundWindow;
			
			_GetForegroundWindow_  MapGetForegroundWindow = 

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

Код:
#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

Код:
			//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
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Установка и настройка Tor&Privoxy d3ath *nix 2 24.10.2006 22:02



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ