PEB

Тема в разделе "WASM.NT.KERNEL", создана пользователем at0s, 8 июн 2010.

  1. at0s

    at0s New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2009
    Сообщения:
    91
    Great
    Код (Text):
    1. если иркл меньше 2, то обращаться к выгруженной памяти можно и она сама подгрузится.
    Код (Text):
    1. PLDR_TABLE = pListEntryNext;
    2. oldirql = KeGetCurrentIrql();
    3. DbgPrint(" * * * * * IRQL: %d\n", oldirql);
    4. if (oldirql < 1) // 2
    5.    if(_stricmp((PWCHAR) PLDR_TABLE->FullDllName.Buffer, L"kernel32.dll",12) == 0)
    6.       kernel = (ULONG) PLDR_TABLE->DllBase;
    7.  
    8. --------------------------------------------------------------
    9. WINDBG WATCH
    10. FullDllName | struct _UNICODE_STRING "--- memory read error at address 0x24a48d00 ---"
    11. --------------------------------------------------------------
    12.  
    13. WINDBG
    14.  * * * * * IRQL: 0   <----------
    15. *** Fatal System Error: 0x0000007e
    16.                        (0xC0000005,0x80538F17,0xBAD07B20,0xBAD0781C)
    17. FAULTING_IP:
    18. nt!__ascii_stricmp+17
    19. 80538f17 8a27            mov     ah,byte ptr [edi]   ; edi==24a48d00 адрес  FullDllName
    20.  
    21. EXCEPTION_RECORD:  bad07b20 -- (.exr 0xffffffffbad07b20)
    22. ExceptionAddress: 80538f17 (nt!__ascii_stricmp+0x00000017)
    23.    ExceptionCode: c0000005 (Access violation)
    24.   ExceptionFlags: 00000000
    25. NumberParameters: 2
    26.    Parameter[0]: 00000000
    27.    Parameter[1]: 24a48d00
    28. Attempt to read from address 24a48d00
    29.  
    30. CONTEXT:  bad0781c -- (.cxr 0xffffffffbad0781c)
    31. eax=24a48d6b ebx=00000000 ecx=80526ed0 edx=7c901005 esi=badfb681 edi=24a48d00
    32. eip=80538f17 esp=bad07be8 ebp=bad07bf4 iopl=0         nv up ei ng nz na pe nc
    33. cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010286
    34. nt!__ascii_stricmp+0x17:
    35. 80538f17 8a27            mov     ah,byte ptr [edi]          ds:0023:24a48d00=??
    36.  
    37. SYMBOL_STACK_INDEX:  1
    38. SYMBOL_NAME:  _KEXP03!RunProcess+20f
    39. FOLLOWUP_NAME:  MachineOwner
    40. MODULE_NAME: _KEXP03
    41. IMAGE_NAME:  _KEXP03.sys
    42. DEBUG_FLR_IMAGE_TIMESTAMP:  4c2bc5bb
    43. STACK_COMMAND:  .cxr 0xffffffffbad0781c ; kb
    44. FAILURE_BUCKET_ID:  0x7E_BADMEMREF__KEXP03!RunProcess+20f
    45. BUCKET_ID:  0x7E_BADMEMREF__KEXP03!RunProcess+20f
    пробовал KeLowerIrql, KeRaiseIrql, не помогло, IRQL и так 0
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    at0s
    24a48d00 - по этому адресу не выделена память.
     
  3. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    нельзя понижать irql ниже уровня, на котором был вызван твой код.
    в регистре edi адрес тычет вникуда.
    к тому же, код 7e - KMODE_EXCEPTION_NOT_HANDLED. это обрабатывается в __try/__except.
     
  4. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    at0s
    Некоторое время назад была сделана ссылка на брутфорс – тогда мы хотели намекнуть на непригодность карьерных методов программирования, особенно в ядре. Без аллюзий: если перед началом составления кода воспринять совсем немного первичной теории, то отпадёт огромное множество простейших базовых вопросов. А в твоём случае – когда выполняется итеративная работа над задачами смежных классов – знание основ просто жизненно необходимо. TLDR: rtfm.

    JhanGhuangxi
    >MmProbeForRead не подойдет?
    Great
    >а смысл ее юзать?

    ProbeForRead() подойдёт. Более того, при дереференсах указателей, полученных из юзермода, её использование необходимо. Почему? Да потому что неконтролируемые внешние указатели могут ссылаться на ядерную память. А обычно ведь никто сознательно не добавляет в свои коды скрытый функционал в виде DoS или EoP, не так ли? Или, даже если оставить злые умыслы снаружи – кто гарантирует, что полученные из r3 указатели не указывают на невалидную ядерную память? Ведь блок try\except не отлавливает такие дереференсы – они просто приводят к мгновенному бсоду [и это правильно и логично].

    at0s
    Если всё же необходимо просмотреть базу данных ldr (для сравнения с реальной ситуацией, например), то вот некоторые направляющие (с акцентом на референс указателей и порядок пробирования):

    Код (Text):
    1. UNICODE_STRING ReferenceDllName = L"kernel32.dll";
    2.  
    3. Peb = ...;
    4. if (!Peb)
    5.     return STATUS_BOLT;
    6.  
    7. LDR* Ldr = Peb->Ldr;
    8.  
    9. ENTRY *EntryHead, *CurrentEntry, *PreviousEntry;
    10. EntryHead = CurrentEntry = PreviousEntry = NULL;
    11.  
    12.  
    13. try
    14. {
    15.     ProbeForRead(Ldr);
    16.     CurrentEntry = EntryHead = CONTAINING_RECORD(&Ldr.XxxList, LDR_DATA_TABLE_ENTRY, XxxLinks);
    17.     while (CurrentEntry = CONTAINING_RECORD(CurrentEntry->XxxLinks.Flink, LDR_DATA_TABLE_ENTRY, XxxLinks),
    18.     CurrentEntry != EntryHead)
    19.     {
    20.         ProbeForRead(CurrentEntry);
    21.         PreviousEntry = CurrentEntry;
    22.        
    23.         UNICODE_STRING CapturedDllName = CurrentEntry->DllName;
    24.         ProbeForRead(CapturedDllName.Buffer, CapturedDllName.Length, ALIGN_OF(WCHAR));
    25.        
    26.         if (RtlCompareUnicodeString(&CapturedDllName, &ReferenceDllName) == 0)
    27.         {
    28.             // match
    29.             ...
    30.         }
    31.     }
    32. }
    33. except(EXCEPTION_EXECUTE_HANDLER)
    34. {
    35.     // какие-то указатели невалидны (ссылаются на неаллоцированный регион, ядерную память, etc).
    36.     ...
    37. }
    Также обрати внимание, что для сравнения строк используется RtlCompareUnicodeString(). Сравнивать через wcscmp() [ и тем более через strcmp() ;) ] – неправильно. Да, неправильно даже тогда, когда сравнение происходит со строкой заранее известной длины. При референсе юзермодной памяти по пробированному указателю это не опасно, но в других ситуациях это может быть фатально:
    Код (Text):
    1. PUNICODE_STRING String = GetAbsolutelyValidPointerToAbsolutelyValidUnicodeString();
    2. try
    3. {
    4.     //
    5.     // Мы в try-блоке, а 'String' – валидный пойнтер, указывающий на абсолютно валидную структуру.
    6.     // 'String->Buffer' также валиден. IRQL == Passive.
    7.     // Несмотря на всё это, выполнение следующей строки может привести к мгновенному бсоду:
    8.     //
    9.     if (_wcsnicmp(String->Buffer, L"kernel32.dll", 12) == 0)
    10.     {
    11.         ...
    12.     }
    13. }
    14. except(EXCEPTION_EXECUTE_HANDLER)
    15. {
    16.     ...
    17. }
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Ой а там про юзермодную было? Тогда извиняюсь
     
  6. at0s

    at0s New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2009
    Сообщения:
    91
    Clerk Great
    спасибо, вы правы ошибка была в структуре
    #if (VER_PRODUCTBUILD >= 2600) почему-то не сработал....

    Sol_Ksacap
    Тогда это меня немного задело, но подумав, я понял что ты прав, как и впрочем сейчас. Я отлично понимаю что ты имеешь ввиду.
    [off]
    Как-то, в 1994 году ( кажется вчера ) решил написать прогу под Windows (3.1). К этому времени за спиной было много "подвигов": крякал все что попадалось под руку, на спор ( если за 5 - 10 мин, то ящик пива ), писал защиты, шифровшики, драйвера для драйверов :) (если помнишь хаос, который тогда был), которые шеф удачно продавал ( работал системшиком в програмисткой фирме), вирусы-шутки, во всех дочкиных играх вместо 3-х жизней было где-то 20, набор работаюшей .сом програмы (примитивной) прямо в нортон редакторе ( ящик пива ). и т. д.
    И когда для того чтоб открыть одно окно в Windows и написать "Hello World", мне пришлось набрать 4 страницы кода, я решил что это не мое, благо работы и так было много т.к. мастдай на серьезных системах никто не ставил.
    Последние 9 лет не писал системных прог, когда же решил вернуться, ... имеем то, что имеем.... расплачиваюсь за недальновидность. TLDR
    это в назидание "молодым" - если остановитесь, то начинать придется не с нуля, а с минуса
    [off end]

    спасибо за дельные замечания, уже исправил

    как обычно, в 7 утра, запускаю дебагер :)

    Read This F***ing Manual
    Умирает молодой талантливый программист. Попадает Богу на суд, и плачется: - Господи, почему я умер молодым? Ведь я был хорошим, жене не изменял, вирусов не писал, на порносайты не лазил, за что ты меня лишил жизни?
    Бог поднимает библию, и грозит программисту пальчиком:
    - RTFM, батенька, RTFM...
     
  7. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    at0s, не переживай, все что ты потерял -- так это болезнь "дельфи головного мозга" ;)
     
  8. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    В данном случае (теоретически) может быть проведена "Argument Switch Attack", то есть значение Buffer может быть инвалидировано сторонним кодом после проверки String (защитой является замена типа String на "значение", а не "указатель").

    В то же время, такой атаке подвержена и предполагаемая RtlCompareUnicodeString, поскольку принимает указатели на структуры, которые находятся не известно где. Для сравнения далее приведен исходник и псевдокод упомянутых функций.
    Код (Text):
    1. LONG
    2. RtlCompareUnicodeString(
    3.     IN PUNICODE_STRING String1,
    4.     IN PUNICODE_STRING String2,
    5.     IN BOOLEAN CaseInSensitive
    6.     )
    7.  
    8. /*++
    9.  
    10. Routine Description:
    11.  
    12.     The RtlCompareUnicodeString function compares two counted strings.  The
    13.     return value indicates if the strings are equal or String1 is less than
    14.     String2 or String1 is greater than String2.
    15.  
    16.     The CaseInSensitive parameter specifies if case is to be ignored when
    17.     doing the comparison.
    18.  
    19. Arguments:
    20.  
    21.     String1 - Pointer to the first string.
    22.  
    23.     String2 - Pointer to the second string.
    24.  
    25.     CaseInsensitive - TRUE if case should be ignored when doing the
    26.         comparison.
    27.  
    28. Return Value:
    29.  
    30.     Signed value that gives the results of the comparison:
    31.  
    32.         Zero - String1 equals String2
    33.  
    34.         < Zero - String1 less than String2
    35.  
    36.         > Zero - String1 greater than String2
    37.  
    38.  
    39. --*/
    40.  
    41. {
    42.  
    43.     PWCHAR s1, s2, Limit;
    44.     LONG n1, n2;
    45.     WCHAR c1, c2;
    46.  
    47.     s1 = String1->Buffer;
    48.     s2 = String2->Buffer;
    49.     n1 = String1->Length;
    50.     n2 = String2->Length;
    51.  
    52.     ASSERT((n1 & 1) == 0);
    53.     ASSERT((n2 & 1) == 0);
    54.     ASSERT(!(((((ULONG_PTR)s1 & 1) != 0) || (((ULONG_PTR)s2 & 1) != 0)) && (n1 != 0) && (n2 != 0)));
    55.  
    56.     Limit = (PWCHAR)((PCHAR)s1 + (n1 <= n2 ? n1 : n2));
    57.     if (CaseInSensitive) {
    58.         while (s1 < Limit) {
    59.             c1 = *s1++;
    60.             c2 = *s2++;
    61.             if (c1 != c2) {
    62.  
    63.                 //
    64.                 // Note that this needs to reference the translation table!
    65.                 //
    66.  
    67.                 c1 = NLS_UPCASE(c1);
    68.                 c2 = NLS_UPCASE(c2);
    69.                 if (c1 != c2) {
    70.                     return (LONG)(c1) - (LONG)(c2);
    71.                 }
    72.             }
    73.         }
    74.  
    75.     } else {
    76.         while (s1 < Limit) {
    77.             c1 = *s1++;
    78.             c2 = *s2++;
    79.             if (c1 != c2) {
    80.                 return (LONG)(c1) - (LONG)(c2);
    81.             }
    82.         }
    83.     }
    84.  
    85.     return n1 - n2;
    86. }
    Код (Text):
    1. int _wcsnicmp(const wchar_t *String1, const wchar_t *String2, size_t count)
    2. {
    3.   int result;
    4.   const wchar_t *s1;
    5.   const wchar_t *s2;
    6.   wchar_t c1;
    7.   wchar_t c2;
    8.  
    9.   result = 0;
    10.   if ( count )
    11.   {
    12.     s1 = String1;
    13.     s2 = String2;
    14.     do
    15.     {
    16.       c1 = *s1++;
    17.       if ( c1 >= 0x41 && c1 <= 0x5A )
    18.         c1 += 32;
    19.       c2 = *s2++;
    20.       if ( c2 >= 0x41 && c2 <= 0x5A )
    21.           c2 += 32;
    22.       }
    23.     }
    24.     while ( !(--count) && c1 && c1 == c2 );
    25.     result = c1 - c2;
    26.   }
    27.   return result;
    28. }
    Во втором случае злоумышленник может исказить длину String1 и добиться неверного результата, но подобное можно получить и в первой функции, если строки не за-capture-ны. Что еще, не считая NLS в первом варианте?