Перехват функций на основе генерации исключений

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by sn0w, 2 Sep 2009.

  1. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,291
    Reputations:
    327
    Собственно очередная техника перехвата любых функций. только в начало пишется не джамп - а привелегированная инструкция, система генерирует исключение - а обработчик то уже тоже перехвачен. поэтому управление получает наш заранее подготовленный перехватчик. недоработок много - в частности не доделал снятие этого хука - но в этом ничего сложного нет. можете и сами.

    особые спасиба кезу, который как всегда помог советом и делом =)

    вообщем, кодоманы, это для вас ;)

    сорсы с билдом тут - webfile.ru/3886408
    пасс 123123


    а это кусок движка:
    Code:
    //////////////////////////////////////////////////////////////////////////
    //	(c) Exception hook engine by sn0w. big thx to kez for good advices ;)
    //	(c) cih.[ms] community, 2009
    //////////////////////////////////////////////////////////////////////////
    
    #include <windows.h>
    #include "alldefs.h"
    
    //////////////////////////////////////////////////////////////////////////
    // анхук не поддерживается, пока. впадлу бля)
    //////////////////////////////////////////////////////////////////////////
    
    LIST_ENTRY   exception_list_head;
    
    //////////////////////////////////////////////////////////////////////////
    
    #define InitializeListHead(ListHead) (\
    	(ListHead)->Flink = (ListHead)->Blink = (ListHead))
    
    #define InsertHeadList(ListHead,Entry) {\
    	PLIST_ENTRY _EX_Flink;\
    	PLIST_ENTRY _EX_ListHead;\
    	_EX_ListHead = (ListHead);\
    	_EX_Flink = _EX_ListHead->Flink;\
    	(Entry)->Flink = _EX_Flink;\
    	(Entry)->Blink = _EX_ListHead;\
    	_EX_Flink->Blink = (Entry);\
    	_EX_ListHead->Flink = (Entry);\
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    PHOOK_ENTRY	find_hook_entry(LPVOID real_address)
    {
    	PLIST_ENTRY		current_list;
    	PHOOK_ENTRY		pcurr_hook;
    
    	current_list = exception_list_head.Flink;
    
    	while(current_list != &exception_list_head){
    		pcurr_hook = CONTAINING_RECORD(current_list, HOOK_ENTRY, list_entry);
    
    		if ((DWORD)pcurr_hook->real_address == (DWORD)real_address)
    			return pcurr_hook;
    		
    		current_list = current_list->Flink;
    	}
    
    	return NULL;
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    VOID (WINAPI * Real_KiUserExceptionDispatcher)(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextFrame);
    VOID WINAPI My_KiUserExceptionDispatcher(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextFrame)
    {
    
        if(ExceptionRecord->ExceptionCode == STATUS_PRIVILEGED_INSTRUCTION){
    		PHOOK_ENTRY hhk;
    		hhk = find_hook_entry(ExceptionRecord->ExceptionAddress);
    
    		if(hhk){
    			ContextFrame->Eip = (DWORD)hhk->handler;
    			NtContinue(ContextFrame, FALSE);
    		}
    
        }
    
        Real_KiUserExceptionDispatcher(ExceptionRecord,ContextFrame);
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    __declspec(naked) VOID WINAPI Stub_KiUserExceptionDispatcher(IN PEXCEPTION_RECORD ExceptionRecord,
    															 IN PCONTEXT ContextFrame)
    {
    	__asm{
    		sub	esp, 4
    		jmp	My_KiUserExceptionDispatcher
    	}
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    inline void generate_pushret_code(LPVOID at_address, LPVOID to_address)
    {
    	*(LPBYTE)at_address = 0x68;
    	*(DWORD*)((LPBYTE)at_address + 1) = (DWORD)to_address;
    	*((LPBYTE)at_address + 5) = 0xC3;
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    PHOOK_ENTRY	ExceptionHookFunction(LPVOID function, LPVOID handler)
    {
    	BYTE privileged_opcodes[] = {0xFA, 0xFB, 0xEE, 0xEF, 0xEC, 0x6C, 0x6F};
    	
    	LPVOID		trampoline;
    	DWORD		first_inst_len, protection;
    	PHOOK_ENTRY phhk;
    		
    	get_instruction_length(function, &first_inst_len);
    	
    	trampoline = valloc(first_inst_len + 6);
    	memcpy(trampoline, function, first_inst_len);
    	generate_pushret_code((LPVOID)((LPBYTE)trampoline + first_inst_len), (LPVOID)((LPBYTE)function + first_inst_len));
    	
    	phhk = (PHOOK_ENTRY)valloc(sizeof(HOOK_ENTRY));	
    	phhk->handler = handler;
    	phhk->real_address = function;
    	phhk->trampoline = trampoline;
    
    	InsertHeadList(&exception_list_head, &phhk->list_entry);
    	
    	VirtualProtect(function, first_inst_len, PAGE_EXECUTE_READWRITE, &protection);
    	*(LPBYTE)function = privileged_opcodes[brandom(0, sizeof(privileged_opcodes) - 1)];
    	VirtualProtect(function, first_inst_len, protection, &protection);
    
    	return phhk;
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    void InitializeExceptionHook()
    {
    	InitializeListHead(&exception_list_head);
    
    	SpliceHookFunction((DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "KiUserExceptionDispatcher"),
    			Stub_KiUserExceptionDispatcher, (DWORD*)&Real_KiUserExceptionDispatcher);
    
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    
    
     
    3 people like this.
  2. flacs

    flacs Member

    Joined:
    28 Jan 2009
    Messages:
    81
    Likes Received:
    31
    Reputations:
    6
    В принципе тоже самое можно и реализовать с помощью DebugAPI
    Так же пищем в начало функции ($CC), при обработке дебага EXCEPTION_EVENT-> EXCEPTION_BREAKPOINT, проводим перехват с помощью OpenThread/SetThreadContext
    и обработичке перехвата вызываем нужную функцию.
    Недостаток лишь в том, что оно палиться через kernel32.dll! IsDebuggerPresent, хотя можно эту функцию перехватить и вернуть false
     
    1 person likes this.
  3. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,291
    Reputations:
    327
    вот такой стаб для IsDebuggerPresent =)

    Code:
    LPVOID lpdbg;
    	DWORD prot;
    	char code[] = {0x33, 0xc0, 0xc3}; // xor eax, eax; retn;
    	lpdbg = GetProcAddress(GetModuleHandle("kernel32.dll"), "IsDebuggerPresent");
    	VirtualProtect(lpdbg, sizeof(code), PAGE_EXECUTE_READWRITE, &prot);
    	memcpy(lpdbg, code, sizeof(code));
    	VirtualProtect(lpdbg, sizeof(code), prot, &prot);
    
    и она никогда не вернет значение того что прога под отладчиком. другой вопрос что ее инструкции

    Code:
            mov     eax, dword ptr fs:[18]
            mov     eax, dword ptr ds:[eax+30]
            movzx   eax, byte ptr ds:[eax+2]
    
    могут использоваться где угодно в программе
     
    #3 sn0w, 4 Sep 2009
    Last edited: 4 Sep 2009
  4. slesh

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

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    А нахера патчить IsDebuggerPresent? Если её код
    Code:
    mov eax, fs:[18h]; // адрес TEB
    mov eax, [eax+30h] // адрес PEB
    movzx eax, [eax+2h] // флаг BeingDebugged
    ret
    
    Так что можно просто изменить значения в BeingDebugged и всё будет норм и менее паливно. Хотя с другой стороны - привязка к структуре TEB и PEB
     
  5. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,291
    Reputations:
    327
    вот добавил анхук

    Code:
    void ExceptionUnhookFunction(LPVOID real_address)
    {
    	PLIST_ENTRY	pcurr_entry;
    	PHOOK_ENTRY	pcurr_item;
    
    	pcurr_item = find_hook_entry(real_address);
    	if(!pcurr_item) return;
    
    	//repair function
    	DWORD prot, len;
    	
    	len = get_instruction_length(pcurr_item->trampoline);
    	
    	VirtualProtect(real_address, len, PAGE_EXECUTE_READWRITE, &prot);
    	memcpy(real_address, pcurr_item->trampoline, len);
    	VirtualProtect(real_address, len, prot, &prot);
    
    	vfree(pcurr_item->trampoline);
    
    	pcurr_entry = &pcurr_item->list_entry;
    	RemoveEntryList(pcurr_entry);
    
            vfree(pcurr_item);
    }
    
     
    #5 sn0w, 4 Sep 2009
    Last edited: 4 Sep 2009
    2 people like this.
  6. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,291
    Reputations:
    327

    или так
     
  7. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2sn0w - fix InsertHeadList
    Code:
    
    #if 0
    #define InsertHeadList(ListHead,Entry) {\
    	PLIST_ENTRY _EX_Flink;\
    	PLIST_ENTRY _EX_ListHead;\
    	_EX_ListHead = (ListHead);\
    	_EX_Flink = _EX_ListHead->Flink;\
    	(Entry)->Flink = _EX_Flink;\
    	(Entry)->Blink = _EX_ListHead;\
    	_EX_Flink->Blink = (Entry);\
    	_EX_ListHead->Flink = (Entry);\
    }
    #endif
    
    #define InsertHeadList(ListHead,Entry) do { \
    	LIST_ENTRY *E_Entry = (Entry); \
    	LIST_ENTRY *E_ListHead = (ListHead); \
    	E_Entry->Flink = E_ListHead->Flink; \
    	E_Entry->Blink = E_ListHead; \
    	E_ListHead->Flink->Blink = E_Entry; \
    	E_ListHead->Flink = E_Entry; \
    } while (0)
    
    
    побочный эффект в макросе 4 раза вычисляется второй аргумент плюс без do {} while (0) нельзя вставить
    что то в список в условии if else или придется не ставить точку с запятой после InsertHeadList
     
    #7 greki_hoy, 16 Apr 2010
    Last edited: 16 Apr 2010