Перенаправление DbgPrint и KdPrint

Тема в разделе "WASM.NT.KERNEL", создана пользователем haxorart, 13 мар 2010.

  1. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    haxorart
    Тебе потребуется.
    1) ядро, которое ты будешь реверсить. вероятно, от твоей же винды. копируешь ntoskrnl.exe или ntkrnlpa.exe из папки Windows\System32, в зависимости от того, какое ядро загружено (с поддержкой PAE или без. сейчас она есть почти везде).
    2) символы к этому ядру. хоть оно и зовется (скорее всего) ntkrnlpa.exe, но его истинное имя скорее всего ntkrpamp.exe - Дело в том, что MP версии ядер для не-PAE систем не выпускают. поэтому на многопроцессорной машине истинное имя файла ядра будет ntkrpamp.exe, на однопроцессорной ntkrnlpa.exe. Это написано в original file name свойствах файла. Но файл берем по-прежнему, ntkrnlpa.exe. Это я все к тому, чтобы народ не удивлялся тому, что ядро у них ntkrnlpa.exe, а символы грузятся ntkrpamp.pdb. Это нормально.
    Как слить символы? Очень просто. Много путей есть. Самый простой из них: тебе потребуется Debugging tools for Windows (предполагается, что он уже установлен в систему). Дальше открываешь окно cmd.exe (командная строка).
    Переходишь в системный каталог (cd %windir%\System32); вызываешь "C:\Program Files\Debugging Tools for Windows (x86)\symchk.exe" ntkrnlpa.exe /s srv*D:\Symbols*http://msdl.microsoft.com/download/symbols /v
    После этого в D:\Symbols должны появиться символы - папочка ntkrnlpa.pdb/ntkrpamp.pdb в зависимости от многоядерности(многопроцессорности) твоей машины.
    Дальше ты проходишь по каталогам вглубь, входишь в каталог с длинным именем из цифер и букв (это GUID + AGE для PDB), в нем находится твой .pdb. Его вместе с самим ntkrnlpa.exe копируешь в отдельную папочку, допустим, D:\reverse.
    Теперь открываем IDA Pro с плагином Hex-Rays (очень поможет) и пихаем туда ntkrnlpa.exe. Он должен спросить насчет отладочной информации - ответь "да". Если все ок, он подцепит твои символы и через несколько минут (анализ ядра длится долго) ты сможешь приступить к просмотру внутренностей твоего ядра. Рекомендую сразу же после заверщения анализа (initial autoanalysis) сохранить .idb файл иды через Ctrl-W, чтобы потом не было мучительно больно, если что-то вылетит, а ты там наизменяешь много ине успеешь сохранить.
    Далее в открытом в иде ядре ты идешь во вкладочку names и вводишь туда (прямо туда) _KiTrap0E (к примеру)
    Оно находит символ _KiTrap0E. Переходишь на него. В окошечке IDA View-A тебе высветят код этой функции. Это обработчик исключения 0x0E, он же Page Fault, он же #PF, которое генерируется процессором при ошибке страницы. Мы открыли его для примера. Смотрим что делает обработчик. Первым делом он сохраняет контекст того места, в котором произошло прерывание - так называемый KTRAP_FRAME. (много инструкций push делают именно это - формируют его на стеке).
    После создания KTRAP_FRAME можно смело включать прерывания, что и делает последующий sti.
    Потом вызывается MmAccessFault, которая обрабатывает все типы ошибок страниц, подгружает с диска страницы, если #PF возник из-за отсутствия подкачиваемой страницы в памяти, или же генерит бсод - зависит от ситуации.
    Но смотрим далее - если подкачиваемую страницу подгрузить не удалось, управление идет в CommonDispatchException - общая функция обработки исключений, как и видно из названия, со статусом STATUS_IN_PAGE_ERROR.

    CommonDispatchException формирует на стеке EXCEPTION_RECORD, дергает KiDispatchException, а потом джампит в Kei386EoiHelper (jmp в Kei386EoiHelper - это восстановление сохраненного ранее TRAP_FRAME. короче, полный возврат из обработчика _KiTrap03 в данном случае. но вызывается оно из всех мест, где управление попадает в ядро. это практическси единственный путь выхода из обработчика ядра).

    Далее KiDispatchException проверяет наличие отладчика, превращает KTRAP_FRAME со стека в понятную юзермоду структуру CONTEXT, а дальше идет недокументированный фокус покус - вызов функции по адресу KiDebugRoutine (если адрес не нулевой).

    Фокус-покус заключается в том, что подменив этот дворд, мы сможем перехватывать все исключения, возникающие в системе. Они все сходятся в одной точке, Clerk был прав.

    в KiDebugRoutine обычно лежит адрес KdpStub, если ядерный отладчик отключен, или адрес KdpTrap, если включен. Первая не делает ничего полезного, а вот вторая отстукивает отладчику о том, что произошло исключение - так вот WinDbg/kd их и ловит.

    Именно этот метод (подмена дворда KiDebugRoutine) я юзал когда-то в своем отладчике ядра, который так и не дописан... а надо бы..


    PS. В обработчике KiDebugRoutine нельзя юзать DbgPrint для отладки, поскольку DbgPrint сам по себе сводится к генерации исключения, что повлечет за собой рекурсию и в конце концов бсод UNEXPECTED_KERNEL_MODE_TRAP.
     
  2. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Тут остается только пробовать. Если будет что-нибудь необычное, интересное или непонятное отпишусь.
     
  3. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Ну вот наконец-то достаточно подробно, надеюсь. Пишите как будут проблемы.

    Сервисная техническая поддержка WASM всегда к вашим услугам. (сарказм)
     
  4. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Сейчас сложно найти более точную и толковую тех. поддержку чем здесь.(не шутка)
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    haxorart
    Код (Text):
    1. .code
    2.     include VirXasm32b.asm
    3.  
    4. ; +
    5. ; Проверяет валидность заголовка модуля.
    6. ;
    7. CheckImageNtHeader proc ImageBase:PVOID, ImageHeader:PIMAGE_NT_HEADERS
    8.     mov edx,ImageBase
    9.     mov eax,STATUS_INVALID_IMAGE_FORMAT
    10.     assume edx:PIMAGE_DOS_HEADER
    11.     cmp [edx].e_magic,'ZM'
    12.     jne @f
    13.     add edx,[edx].e_lfanew
    14.     assume edx:PIMAGE_NT_HEADERS
    15.     cmp [edx].Signature,'EP'
    16.     jne @f
    17.     cmp [edx].FileHeader.SizeOfOptionalHeader,sizeof(IMAGE_OPTIONAL_HEADER32)
    18.     jne @f
    19.     cmp [edx].FileHeader.Machine,IMAGE_FILE_MACHINE_I386   
    20.     jne @f
    21.     test [edx].FileHeader.Characteristics,IMAGE_FILE_32BIT_MACHINE
    22.     je @f
    23.     mov ecx,ImageHeader
    24.     xor eax,eax
    25.     mov dword ptr [ecx],edx
    26. @@:
    27.     ret
    28. CheckImageNtHeader endp
    29.  
    30. ; KeEnterKernelDebugger:
    31. ;   ...
    32. ;   Call KdInitSystem
    33. ;   push DBG_STATUS_FATAL   ; 5
    34. ;   call KiBugCheckDebugBreak
    35. ;
    36. ; KdInitSystem:
    37. ;   ...
    38. ;   mov dword ptr ds:[KiDebugRoutine],@KdpStub
    39.  
    40. OPCODE_CALL equ 0E8H
    41. OPCODE_RET  equ 0C3H
    42.  
    43. ENTRY1_MAX_LENGTH equ 50H   ; for KeEnterKernelDebugger()
    44. ENTRY2_MAX_LENGTH equ 80H   ; for KdInitSystem()
    45.  
    46. ; +
    47. ; Определение RVA переменной KiDebugRoutine.
    48. :
    49. QueryKiDebugRoutine proc uses ebx esi edi NtImageBase:PVOID, Rva:PVOID
    50. Local ImageHeader:PIMAGE_NT_HEADERS, ImageLimit:PVOID
    51. Local pKeEnterKernelDebugger:PVOID
    52.     mov edi,NtImageBase
    53.     invoke CheckImageNtHeader, Edi, addr ImageHeader
    54.     test eax,eax
    55.     lea ebx,pKeEnterKernelDebugger
    56.     jnz Exit
    57.     invoke QueryExportEntry, Edi, addr pKeEnterKernelDebugger   ; "KeEnterKernelDebugger"
    58.     test eax,eax
    59.     mov esi,pKeEnterKernelDebugger
    60.     jnz Exit
    61.     lea ebx,[esi + ENTRY1_MAX_LENGTH]
    62. F1:
    63.     Call VirXasm32
    64.     add esi,eax
    65.     cmp byte ptr [esi],OPCODE_CALL  ; call KdInitSystem
    66.     jne N1
    67.     cmp word ptr [esi + 5],056AH    ; push DBG_STATUS_FATAL
    68.     jne N1
    69.     cmp byte ptr [esi + 7],OPCODE_CALL  ; call KiBugCheckDebugBreak
    70.     jne N1
    71.     mov eax,ImageHeader
    72.     add esi,dword ptr [esi + 1]
    73.     mov eax,IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage[eax]
    74.     add esi,5       ; @KdInitSystem
    75.     add eax,edi
    76.     lea ebx,[esi + ENTRY2_MAX_LENGTH]
    77.     mov ImageLimit,eax
    78.     cmp esi,edi
    79.     jna Error
    80.     cmp esi,eax
    81.     jae Error
    82. F2:
    83.     Call VirXasm32
    84.     add esi,eax
    85.     cmp word ptr [esi],05C7h    ; mov dword ptr ds:[KiDebugRoutine],@KdpStub
    86.     jne N2
    87.     mov ecx,dword ptr [esi + 2] ; @KiDebugRoutine
    88.     mov edx,dword ptr [esi + 6] ; @KdpStub
    89.     cmp ecx,edi
    90.     mov esi,Rva
    91.     jna Error
    92.     cmp edx,edi
    93.     jna Error
    94.     cmp ImageLimit,ecx
    95.     jbe Error
    96.     cmp ImageLimit,edx
    97.     jbe Error
    98.     sub ecx,Edi
    99.     xor eax,eax
    100.     mov dword ptr [esi],ecx
    101.     jmp Exit
    102. N2:
    103.     cmp byte ptr [esi],OPCODE_CALL
    104.     je Error
    105.     cmp esi,ebx
    106.     jc F2
    107.     jmp Error  
    108. N1:
    109.     cmp byte ptr [esi],OPCODE_RET   ; Ret
    110.     je Error
    111.     cmp esi,ebx
    112.     jc F1
    113. Error:
    114.     mov eax,STATUS_NOT_FOUND
    115. Exit:
    116.     ret
    117. QueryKiDebugRoutine endp
    Здесь вычесляется RVA переменной, относительно текущей проекции ядра, так как обычно разбирается модуль на диске, тоесть затем нужно прибавить реальную базу загрузки ядра.
    QueryExportEntry() - это ваша функция, которая ищет KeEnterKernelDebugger в экспорте ядра.
    VirXasm32b.asm - дизасм длин, берётся тут:
    http://www.eof-project.net/sources/Malum/VirXasm32_v1.5adv/src/VirXasm32b.asm
     
  6. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    haxorart
    Мой сарказм был на тему того, что неплохо бы сначала покумекать самому, потом бежать на форум.
    Нужен тебе отладочный вывод? Ну и беги смотри в дизассемблере как устроены функции:
    - OutputDebugStringA/W (kernel32.dll)
    - DbgPrint, DbgPrint...(не помню их там много), DbgPrompt (ntdll.dll)
    - DbgPrint, DbgPrint*, DbgPrompt (ядро)

    Неужели так сложно проанализировать все эти ф-ии и составить для себя соответствующие пути перехвата данных, которые в них идут.
    И посмотреть там:
    DbgPrint вызывает vDbgPrintExWithPrefixInternal. Ага.. смотрим vDbgPrintExWithPrefixInternal.
    А там все просто:
    - если не подцеплен отладчик ядра или если процесс отлаживают (NtCurrentTeb()->Peb.BeingDebugged != FALSE), то генерится исключение с определенным кодом
    - иначе вызывается DebugPrint
    Сразу видно, что к чему. (этот листинг HexRays'а ф-ии vDbgPrintExWithPrefixInternal я, правда, чуток скорректировал, чтобы получилось более читабельно).
    Если текущий процесс отлаживается, то отладчик поймает исключение (на всякий случай вызов RtlRaiseException обернут в __try/__except, вдруг отладчик не поймает) и отобразит у себя отладочный вывод этот.
    Так же проверяется и наличие подцепленного WinDbg/kd. Если их нет, тоже генерится исключение. Авось кто поймает.

    Иначе вызывается ф-я DebugPrint, которая вызывает DebugService, и в конце концов выполняется INT 2D (а сразу после нее INT3). В EAX кладется номер сервиса (прерывание 2д используется не только для вывода отладочных строк, но и для нотификации отладчика ядра о загрузке образов и тп.), для DbgPrint это 1 (BREAKPOINT_PRINT), в ECX/EDX (а в последних ядрах еще и EBX,EDI) - параметры, 2 штуки (в новых ядрах 4).
    В случае DbgPrint в ECX кладется адрес строки печатаемой, а в EDX 0 (не используется типа второй аргумент отладочного сервиса).

    Обработчик прерывания 2D в ядре (KiDebugService) сохраняет KTRAP_FRAME (как любой порядочный обработчик), инкрементит Eip в KTRAP_FRAME (чтоб он указывал на INT3, следующую за INT 2D), читает eax,ecx,edx из KTRAP_FRAME в регистры и дальше джампит в обработчик KiTrap03DebugService.
    А там сразу и исключение генерируется (видите call CommonDispatchException) с кодом STATUS_BREAKPOINT. Но это не простой брекпоинт, у него особые значения EAX/ECX/EDX(/EBX/EDI) (которые заботливо достал из фрейма KiDebugService). Отладчик ядра понимает это и выводит отладочную строку.
    Вот такое вот необычное исключение бывает STATUS_BREAKPOINT (для этого и стояло int3 сразу после int 2d. оно все равно не выполнится. после отсылки строки отладчику управление уйдет на инструкцию после int3).

    DbgPrint из экспорта ядра работает аналогично - вызывает отладочный сервис 2d точно так же.
    Не поверишь, но и третий вариант отладочного вывода (OutputDebugStringA/W из kernel32.dll) тоже сводится к исключению. Подробностей писать не буду, открыть в ольку и сделать ctrl+g, OutputDebugStringA, и подсмотреть сможет каждый, если понадобится.

    А все это можно было разреверсить самостоятельно и не спрашивать на форуме :) Лень, банальная лень...)
    А вам лишь готовые мануалы подавай да на вопросы отвечай.. эх, народ пошел...)
     
  7. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Уже давно тут http://www.wasm.ru/forum/viewtopic.php?id=34151&p=1 лежит семпл перенаправляющий отладочный вывод в консоль в юзермоде. Для ядра подобным образом(параметры и структуры теже).
     
  8. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    2Great & 2Clerk
    Спасибо за помошь, стало намного понятнее.
    2Great
    Это далеко не лень, я кое-что из вышесказанного просто не знал и до конца бы просто не дошел.
     
  9. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Я реализовал данный механизм перехвата. Новозникли некоторые трудности.
    На ХР всё работает как часы. На Viste если запустить мою программу ничего просто не происходит либо всё падает в BSOD. Я естественно начал рыть в сторону своих ошибок. Прогнал весь алгорит и выяснилось, что в бсод падает на записи в память. Покопав данном направлении понял, что это происходит если запись ещё не закончилась, а что-то в системе пытается вызвать KiDebugRoutine. У Ms-Rem'a нашел решение - это остановка прерываний, но это равнозначно наивысшему приорите. На сколько я понимаю это небезопасно. Отсюда первый вопрос, как можно сделать безопасную запись в ядро? Но с методом Ms-Rem'а BSOD'ы закончились. Но вывод перехва всё равно не работает. Попробовал поиграться с DbgView, обнаружил странную вещь. Если запустить сначала DbgView, а потом мою прогу вывод начинает работать(!), но через некоторое время наступает BSOD. Отсюда 2: Что делает такое DbgView и чего не учел я? Как я понял при дезассемблировании DbgView, он использует DbgSetDebugPrintCallback. Но ведь метод предложенный Clerk'ом должен быть универсальным. И моя программа начинает работать после запуска DbgView, значит дело скорее всего в каких-то флагах или чём-то подобном.
     
  10. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    haxorart
    В какую память, откуда(бактрейс) ?
    Вобще Рема тут какое отношение имеет со своим паскалем ?
    мб это патчгуард ?
     
  11. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    1 проблема была в том, что драйвер при выражении типо KiDebugRoutine=MyRoutine иногда падал. На ХР это работало, видимо частота обработки сепшнов на Vista+ выше чем на ХР. Поэтому память просто не успевала дописаться и происходило обращение к ней. Что сделал я:
    Код (Text):
    1.     __asm
    2.     {
    3.         cli                    
    4.         mov eax, cr0
    5.         mov CR0Reg,eax
    6.         and eax,0xFFFEFFFF    
    7.         mov cr0, eax
    8.     }
    9.  
    10.            KiDebugRoutine=MyRoutine
    11.  
    12.     __asm
    13.     {
    14.         mov eax, CR0Reg    
    15.         mov cr0, eax          
    16.         sti                    
    17.     }
    После этого прога в бсод падать перестала. Но зато отладчик отваливается на этом месте и я не могу посмотреть, что дальще. Оно и понятно - я останавливаю прерывания. Отсюда вопрос, как можно переделать этот участок кода, чтобы он не бсодил.
    Ну и второй вопрос он был описан выше: почему если сначала запустить DbgView, то и моя прога работает сек 5 , выводит всё как надо, а потом опять бсод. Ну бсод может быть именно из-за 2-го перехвата(это вполне возможно). Но почему она выводить то начинает? Это начинает наталкивать на существование каких-то флагов или чего-то подобного, что я не учитываю при перехвате.
    P.S. Конечно можно использовать DbgSetDebugPrintCallback, но это как-то неспортивно и неуниверсально.
     
  12. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    На счет патч гуарда хз... Но вроде не похоже.
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    haxorart
    Атомарно изменяйте ссылку. Хотя врядле изза этого падает. Крэшдамп не помешал бы.
     
  14. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Так я и так всё в ядре делаю. После того как код рема добавил он уже не падает. Меня, если честно, 2 пункт волнует намного сильнее. Неужели на Viste и 7 этот метод не прокатывает?
     
  15. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    haxorart
    Манипуляция с Cr0 это отключение аппаратной защиты страниц от записи. Не должно этого быть. Переменная находится в секции данных(.data|almostro)которая доступна для записи. На то это и переменная. Строка не понятна:
    Код (Text):
    1. KiDebugRoutine=MyRoutine
    Может вы не туда чтото пишите. Вобщем гадание какоето. Факты нужны.
     
  16. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Тогда опишу вкратце как я что делаю.
    Как говорилось выше сначала ищу KiDebugRoutine из цепочки KeEnterKernelDebugger->KdInitSystem->KiDebugRoutine
    Код (Text):
    1. DWORD FindSignature (int* Signature/*q[]*/, PBYTE Memory/*s[]*/,DWORD SizeOfSignature,DWORD SizeOfMemory){
    2.     int i, j;//, N, M;
    3.     int *pi =(int*)ExAllocatePool(PagedPool,SizeOfSignature*sizeof(int));
    4.     i=0;
    5.     j=-1;
    6.     pi[0]=-1;
    7.     while(i<SizeOfSignature-1){
    8.         while((j>=0) && (Signature[j]!=Signature[i]) && (Signature[j]>=0))
    9.             j = pi[j];
    10.         i++;
    11.         j++;
    12.         if((Signature[i]==Signature[j])||(Signature[j]<0))
    13.             pi[i]=pi[j];
    14.         else
    15.             pi[i]= j;
    16.         }
    17.     /* поиск */
    18.     for(i=0,j=0;(i<SizeOfMemory)&&(j<SizeOfSignature); i++,j++){
    19.         DBG(DbgPrint("Find:%X",Memory[i]));
    20.         while((j>=0)&&(Signature[j]!=Memory[i])&&(Signature[j]>=0))
    21.             j=pi[j];
    22.     }
    23.     ExFreePool(pi);
    24.     DBG(DbgPrint("--=Finish=--"));
    25.     if (j==SizeOfSignature)
    26.         return i-j;
    27.     else /* i==N */
    28.         return -1;
    29.    
    30. }
    31.  
    32.  
    33. DWORD FindDataBySignature (int* Signature/*q[]*/, PBYTE Memory/*s[]*/,DWORD SizeOfSignature,DWORD SizeOfMemory){
    34.     int i;
    35.     for (i = 0; (i < SizeOfSignature) && (Signature[i]>-2); i++);
    36.     return FindSignature(Signature,Memory,SizeOfSignature,SizeOfMemory)+i;
    37.     }
    Собсно поиск по Алгоритму Пратта-Кнутта-Морриса, все смещения считаются динамически.
    Код (Text):
    1. PVOID GetKiDebugRoutine(PVOID KernelBase){
    2.     DWORD offset;
    3.     PVOID KeEnterKernelDebugger,KdInitSystem;//,KiDebugRoutine;
    4.     int sigKdInitSystem[]={0x50,0x50,0xE8,-2};//4 элемента
    5.     int sigKiDebugRoutine[]={0x83,0x3D,-1,-1,-1,-1,-1,
    6.                              0xC7,0x05,-2,-2,-2,-2,-1,-1,-1,-1,
    7.                              0xC6,0x05,-1,-1,-1,-1,-1,
    8.                              0x75,-1};//26 элементов
    9.  
    10.     KeEnterKernelDebugger=GetExportFunctionFromLoadedModule(KernelBase,"KeEnterKernelDebugger");
    11.     offset=FindDataBySignature(sigKdInitSystem,KeEnterKernelDebugger,4,1000);
    12.     KdInitSystem=mkptr(PVOID,KeEnterKernelDebugger,*mkptr(PDWORD,KeEnterKernelDebugger,offset)+offset+4);//тк размер перехода 4 байта
    13.     offset=FindDataBySignature(sigKiDebugRoutine,KdInitSystem,26,1000);
    14.     return  (PVOID)(*mkptr(PDWORD,KdInitSystem,offset));
    15.  
    16. }
    Собственно сам поиск смещений.

    Далее ф-я перехватчик
    Код (Text):
    1. BOOL HookKiDebugRoutine(PKTRAP_FRAME TrapFrame,PVOID Reserved,PEXCEPTION_RECORD ExceptionRecord,
    2.                         PCONTEXT Context,KPROCESSOR_MODE PreviousMode,UCHAR LastChance){       
    3.         PCHAR tmp;
    4.    
    5.         if(TrapFrame->Eax==1){
    6.             tmp=(PCHAR)TrapFrame->Ecx;         
    7.             //RtlCopyMemory(mes,tmp,strlen(tmp));
    8.             ListAdd(&Messages,tmp,strlen(tmp)+1);
    9.         }
    10.         return TrueKiDebugRoutine(TrapFrame,Reserved,ExceptionRecord,Context,PreviousMode,LastChance);     
    11. }
    Установка перехвата

    Код (Text):
    1. int StopInterrupt(){
    2.     int CR0Reg;
    3.     __asm{
    4.             cli
    5.             mov eax, cr0
    6.             mov CR0Reg,eax
    7.             and eax,0xFFFEFFFF
    8.             mov cr0, eax
    9.     }
    10.     return CR0Reg;
    11. }
    12.  
    13. void StartInterrupt(int CR0Reg){
    14.     __asm{
    15.             mov eax, CR0Reg    
    16.             mov cr0, eax            // востановить содержимое CR0
    17.             sti                     // разрешаем прерывания
    18.     }
    19. }
    20.  
    21. void HookProc(void** Dest,void* Src,void** True){
    22.     int CR0=StopInterrupt();
    23.     *True=*Dest;
    24.     *Dest=Src;
    25.     StartInterrupt(CR0);
    26. }
    27.  
    28. ZwQuerySystemInformation=GetKernelProc(L"ZwQuerySystemInformation");
    29.     if(ZwQuerySystemInformation!=NULL){
    30.         KernelBase=GetKernelBase(ZwQuerySystemInformation);
    31.             //GetKernelModuleHandle("ntoskrnl.exe",ZwQuerySystemInformation);
    32.         NtdllBase=GetKernelModuleHandle("ntdll.dll",ZwQuerySystemInformation);
    33.         if(KernelBase!=NULL){
    34.        
    35.             FstKiDebugRoutine=(KIDEBUGROUTINE*)GetKiDebugRoutine(KernelBase);
    36.             HookProc(FstKiDebugRoutine,HookKiDebugRoutine,&TrueKiDebugRoutine);
    37.         }
    38.     }
    39.  
    40.     ListInit(&Messages);
    Собственно в функции поиска есть вывод байт по которым она идёт. Я сверялся с хексом из иды всё байт в байт, даже для 7 и Висты. +в отладчике смотрел че там по этим адресам лежат и опять сверялся, всё совпадает.
     
  17. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    haxorart
    По порядку.
    1. Прототип PKDEBUG_ROUTINE:
    Код (Text):
    1. typedef
    2. BOOLEAN
    3. (*PKDEBUG_ROUTINE) (
    4.     IN PKTRAP_FRAME TrapFrame,
    5.     IN PKEXCEPTION_FRAME ExceptionFrame,
    6.     IN PEXCEPTION_RECORD ExceptionRecord,
    7.     IN PCONTEXT ContextRecord,
    8.     IN KPROCESSOR_MODE PreviousMode,
    9.     IN BOOLEAN SecondChance
    10.     );
    У вас определён последний параметр как:
    Код (Text):
    1. UCHAR LastChance
    Подозреваю что сбивается стек. В NT все параметры размером 4 байта, стек должен быть выравнен на границу 4-х байт.
    2. Собственно хэндлер:
    Код (Text):
    1. if(TrapFrame->Eax==1){
    2.             tmp=(PCHAR)TrapFrame->Ecx;         
    3.             //RtlCopyMemory(mes,tmp,strlen(tmp));
    4.             ListAdd(&Messages,tmp,strlen(tmp)+1);
    5.         }
    Это просто ужас. Ваша ListAdd() будет вызываться при любых исключениях в системе. Обработать следует так:
    - Проверить SecondChance. Для одного исключения хэндлер может вызываться два раза. Должно быть False.
    - Проверить PreviousMode если необходимо, тут видимо не нужно.
    - Проверить код исключения, должен быть DBG_PRINT_EXCEPTION_C или STATUS_BREAKPOINT.
    - (Проверить контекст на включение необходимых полей).
    - Проверить число параметров в инфе об исключении.
    - В зависимости от кода исключения обрабатка идёт разными путями. Для STATUS_BREAKPOINT читать контекст, проверить номер сервиса и валидность ссылки.
    3. Модель возврата на оригинальный обработчик:
    Код (Text):
    1. return TrueKiDebugRoutine
    За собой нужно почистить стек и вернуть управление на предыдущий обработчик, а не вызывать его как процедуру.
    4. Про HookProc() вобще говорить не стоит, налепили там чегото. Единственный момент повторю - используйте инструкцию lock cmpxchg.
     
  18. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Проблема была решена! Спасибо Clerk за помощь, я обязательно учту твои советы по исправлению кода. (!) Проблема лежала обсалютно в другой области, и как я предполагал она была связанна с флагами, а точнее в фильтрами.

    Копаясь в документации наткнулся на странные Note'ы от Microsoft
    Как гласит MSDN всё дело в новой примочке(фильтре), который появился в Висте. Ранее я упоминал, о том, что после запуска DbgView всё начинало работать как часы либо падало. На счёт падения я все же склоняюсь к тому, что 2ой перехват - это не есть хорошо.

    Далее начал реверсить DbgView нашел 2 интересных отрывка кода:

    Код (Text):
    1. [...]
    2.     RtlInitUnicodeString(&DestinationString, L"DbgSetDebugFilterState");
    3.     dword_11D94 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD))MmGetSystemRoutineAddress(&DestinationString);
    4. [...]
    5.  
    6. [...]
    7.   v0 = 0;
    8.   if ( dword_11D94 )
    9.   {
    10.     v1 = &unk_121C0;
    11.     v4 = 130;
    12.     do
    13.     {
    14.       v2 = 0;
    15.       do
    16.       {
    17.         *((_BYTE *)v1 + v2) = dword_11D98(v0, v2);
    18.         DbgSetDebugFilterState(v0, v2++, 1);              (!!!)
    19.       }
    20.       while ( v2 < 0x1E );
    21.       ++v0;
    22.       v1 = (char *)v1 + 30;
    23.     }
    24.     while ( v4-- != 1 );
    25.   }
    26.  
    27. [...]
    В 1ом случае идет бональный поиск функции DbgSetDebugFilterState по имени. Во 2ом как раз перебор всех устройстви установка для них самых широких масок.

    Данный метод без постановки нужных масок не работал, потому что сообщения тупо не доходили до обработчика сепшнов, т.к. фильтры эти применяются достаточно высоко по иерархии( DbgPrintEx, vDbgPrintEx, vDbgPrintExWithPrefix, KdPrintEx(MSDN)). Поэтому-то и работал обработчик, основанный на перехвате отдельных функций.
     
  19. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв