восстановление адресов в SSDT..

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

  1. nof

    nof New Member

    Публикаций:
    0
    Регистрация:
    22 июн 2010
    Сообщения:
    5
    Всем добрый день. Я начинающий в программирования ядра, поэтому просьба сильно не пинать )

    Есть некое приложение (драйвер), который хукает ряд функций. RKU его палит:
    [​IMG]
    И успешно снимает эти хуки при необходимости.
    У меня стоит задача самому вручную снимать эти хуки.. драйвер для чтения SST уже написал, но не могу понять где мне брать оригинальные адреса функций. То есть на что мне менять хукнутые адреса. Из прочтённой литературы понял, что настоящие адреса можно получить импортирую их из ядра. Более детальной информации не нашёл, поэтому просьба подсказать.. желательно разумеется с примерами)
    Заранее, спасибо!
     
  2. JhanGhuangxi

    JhanGhuangxi New Member

    Публикаций:
    0
    Регистрация:
    15 апр 2010
    Сообщения:
    31
    Ищи по форуму, тема поднималась не один десяток раз... Даже код есть
     
  3. nof

    nof New Member

    Публикаций:
    0
    Регистрация:
    22 июн 2010
    Сообщения:
    5
    нашёл только это - http://wasm.ru/forum/viewtopic.php?id=24061 - там вопрос не решён
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    В двух словах:
    1) загружаем копию ядра с диска. (имя возьмешь в ZwQuerySystemInformation(SystemModuleInformation), первый модуль).
    для этого потребуется свой пе-загрузчик (ZwCreateFile + ZwCreateSection + ZwMapViewOfSection + парсинг релоков; импорты не обрабатываем - не нужно).
    2) вручную парсим экспорт и находим KeServiceDescriptorTable. Там уже в первом элементе массива (таблиц 4 штуки, не забываем - ядро, свободная (win32k в шадове), и две свободные для пользователя) уже прописан адрес KiServiceTable. Вынимаем этот адрес - это и есть массив адресов SDT. (чтобы адреса были валидными, нужно в пункте 1 при парсинге релоков перебазировать загруженную копию ядра на базу оригинального ядра в памяти).
    3) заменяем настоящий массив KeServiceDescriptorTable[0].ServiceTable на массив, прочитанный с копии ядра, поэлементно.
    4) ???
    5) PROFIT!

    Как загружать образ и парсить релоки - смотри сорцы ReactOS, например. Или винды (рипнутые).
    Как парсить экспорт - смотри опять же реактос.
    Как найти базу ядра и его имя - смотри примеры ZwQuerySystemInformation(SystemModuleInformation).
    Как скопировать массив - memcpy =)
     
  5. nof

    nof New Member

    Публикаций:
    0
    Регистрация:
    22 июн 2010
    Сообщения:
    5
    Да, спасибо, именно такой пример я нашёл в этой теме: http://wasm.ru/forum/viewtopic.php?id=24061

    Привожу сюда:
    Код (Text):
    1. #include <ntddk.h>
    2.  
    3. #define DEBUG
    4.  
    5. #ifdef DEBUG
    6. #define DBGPRINT DbgPrint
    7. #else
    8. #define DBGPRINT
    9. #endif
    10.  
    11. typedef BOOLEAN        BOOL;
    12. typedef ULONG        DWORD;
    13. typedef USHORT        WORD;
    14. typedef UCHAR        BYTE;
    15.  
    16.  
    17.  
    18. typedef NTSTATUS (NTAPI *NTPROC)();
    19. typedef NTPROC *PNTPROC;
    20.  
    21. typedef struct _SYSTEM_SERVICE_TABLE{
    22.     PNTPROC ServiceTable;
    23.     PULONG CounterTable;
    24.     ULONG ServiceLimit;
    25.     PUCHAR ArgumentTable;
    26. } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;
    27.  
    28. typedef struct _SERVICE_DESCRIPTOR_TABLE{
    29.     SYSTEM_SERVICE_TABLE NtoskrnlTable;
    30.     SYSTEM_SERVICE_TABLE Table2;
    31.     SYSTEM_SERVICE_TABLE Table3;
    32.     SYSTEM_SERVICE_TABLE Table4;
    33. } SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
    34.  
    35. typedef enum _SYSTEM_INFORMATION_CLASS {
    36.     SystemBasicInformation,
    37.     SystemProcessorInformation,
    38.     SystemPerformanceInformation,
    39.     SystemTimeOfDayInformation,
    40.     SystemNotImplemented1,
    41.     SystemProcessesAndThreadsInformation,
    42.     SystemCallCounts,
    43.     SystemConfigurationInformation,
    44.     SystemProcessorTimes,
    45.     SystemGlobalFlag,
    46.     SystemNotImplemented2,
    47.     SystemModuleInformation,
    48.     SystemLockInformation,
    49.     SystemNotImplemented3,
    50.     SystemNotImplemented4,
    51.     SystemNotImplemented5,
    52.     SystemHandleInformation,
    53.     SystemObjectInformation,
    54.     SystemPagefileInformation,
    55.     SystemInstructionEmulationCounts,
    56.     SystemInvalidInfoClass1,
    57.     SystemCacheInformation,
    58.     SystemPoolTagInformation,
    59.     SystemProcessorStatistics,
    60.     SystemDpcInformation,
    61.     SystemNotImplemented6,
    62.     SystemLoadImage,
    63.     SystemUnloadImage,
    64.     SystemTimeAdjustment,
    65.     SystemNotImplemented7,
    66.     SystemNotImplemented8,
    67.     SystemNotImplemented9,
    68.     SystemCrashDumpInformation,
    69.     SystemExceptionInformation,
    70.     SystemCrashDumpStateInformation,
    71.     SystemKernelDebuggerInformation,
    72.     SystemContextSwitchInformation,
    73.     SystemRegistryQuotaInformation,
    74.     SystemLoadAndCallImage,
    75.     SystemPrioritySeparation,
    76.     SystemNotImplemented10,
    77.     SystemNotImplemented11,
    78.     SystemInvalidInfoClass2,
    79.     SystemInvalidInfoClass3,
    80.     SystemTimeZoneInformation,
    81.     SystemLookasideInformation,
    82.     SystemSetTimeSlipEvent,
    83.     SystemCreateSession,
    84.     SystemDeleteSession,
    85.     SystemInvalidInfoClass4,
    86.     SystemRangeStartInformation,
    87.     SystemVerifierInformation,
    88.     SystemAddVerifier,
    89.     SystemSessionProcessesInformation
    90. } SYSTEM_INFORMATION_CLASS;
    91.  
    92. typedef struct _SYSTEM_MODULE{
    93.     ULONG   Reserved[2];
    94.     PVOID   Base;
    95.     ULONG   Size;
    96.     ULONG   Flags;
    97.     USHORT  Index;
    98.     USHORT  Unknown;
    99.     USHORT  LoadCount;
    100.     USHORT  ModuleNameOffset;
    101.     CHAR    ImageName[256];
    102. } SYSTEM_MODULE, *PSYSTEM_MODULE;
    103.  
    104. typedef struct _SYSTEM_MODULE_INFORMATION {
    105.     ULONG dwCount;
    106.     SYSTEM_MODULE Modules[1];
    107. } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
    108.  
    109.  
    110. #ifdef __cplusplus
    111. extern "C" {
    112. #endif
    113.     extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
    114.     extern
    115.         NTSYSAPI
    116.         NTSTATUS
    117.         NTAPI
    118.         ZwCreateSection(
    119.         OUT PHANDLE SectionHandle,
    120.         IN ACCESS_MASK DesiredAccess,
    121.         IN POBJECT_ATTRIBUTES ObjectAttributes,
    122.         IN PLARGE_INTEGER SectionSize OPTIONAL,
    123.         IN ULONG Protect,
    124.         IN ULONG Attributes,
    125.         IN HANDLE FileHandle
    126.         );
    127.     extern
    128.         NTSYSAPI
    129.         NTSTATUS
    130.         NTAPI
    131.         ZwQuerySystemInformation (
    132.         IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    133.         OUT PVOID                   SystemInformation,
    134.         IN ULONG                    Length,
    135.         OUT PULONG                  ReturnLength
    136.         );
    137. #ifdef __cplusplus
    138. }
    139. #endif
    140.  
    141. #define RESTORE_SYSCALL 0xAD
    142.  
    143. void DriverUnload(PDRIVER_OBJECT DriverObject){
    144. }
    145.  
    146. NTSTATUS DrvUnhookQST(ULONG dwSysCall, ULONG dwOriginal){
    147.     ULONG CR0Reg;
    148.         DBGPRINT("fun addr - %x", dwOriginal);
    149.     _asm mov eax,CR0
    150.         _asm mov CR0Reg,eax
    151.         _asm and eax,0xFFFEFFFF
    152.         _asm mov cr0, eax
    153.  
    154.         KeServiceDescriptorTable->NtoskrnlTable.ServiceTable[dwSysCall] = (NTPROC)dwOriginal;
    155.     _asm mov eax,CR0Reg
    156.         _asm mov cr0,eax
    157. }
    158.  
    159. HANDLE hKernelFile = 0, hKernelSection = 0;
    160. PVOID pKernel = NULL;
    161.  
    162. void UnMapKernel(){
    163.     if (pKernel)
    164.         ZwUnmapViewOfSection(pKernel, NtCurrentProcess());
    165.     if (hKernelSection)
    166.         ZwClose(hKernelSection);
    167.     if (hKernelFile)
    168.         ZwClose(hKernelFile);
    169. }
    170.  
    171. NTSTATUS MapKernel(){
    172.     UNICODE_STRING usKernelFile;
    173.     OBJECT_ATTRIBUTES oaKernelFile;
    174.     NTSTATUS ns = STATUS_SUCCESS;
    175.     IO_STATUS_BLOCK iosb;
    176.     ULONG dwViewSize = 0;
    177.  
    178.  //   RtlInitUnicodeString(&usKernelFile, L"\\SystemRoot\\System32\\ntoskrnl.exe");
    179.     RtlInitUnicodeString(&usKernelFile, L"\\SystemRoot\\System32\\ntkrnlpa.exe");
    180.     InitializeObjectAttributes(&oaKernelFile, &usKernelFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    181.         NULL, NULL);
    182.     ns = ZwOpenFile(&hKernelFile, FILE_READ_DATA | SYNCHRONIZE, &oaKernelFile, &iosb,
    183.         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT);
    184.     if (ns != STATUS_SUCCESS)
    185.         return ns;
    186.  
    187.     ns = ZwCreateSection(&hKernelSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, 0x8000000,
    188.         hKernelFile);
    189.     if (ns != STATUS_SUCCESS){
    190.         UnMapKernel();
    191.         return ns;
    192.     }
    193.  
    194.     ns = ZwMapViewOfSection(hKernelSection, NtCurrentProcess(), &pKernel, 0, 0, NULL, &dwViewSize,
    195.         ViewShare, 0, PAGE_READONLY);
    196.     if (ns != STATUS_SUCCESS)
    197.     {
    198.         UnMapKernel();
    199.     }
    200.     return ns;
    201. }
    202.  
    203. ULONG mystrcmp(char *str1, char *str2){
    204.     while(*str1 && *str2)
    205.         if(*str1 == *str2){
    206.             str1++;
    207.             str2++;
    208.         }else
    209.             return 0;
    210.         if(*str1 && !*str2)
    211.             return 0;
    212.         else
    213.             if (*str2 && !*str1)
    214.                 return 0;
    215.         return 1;
    216. }
    217.  
    218. ULONG xGetProcAddress(ULONG hModule, char *pszName){
    219.     ULONG dwNameCount, *AddressOfNames, *AddressOfFunctions;
    220.     USHORT *AddressOfOrdinals;
    221.     ULONG i;
    222.     char *pszExportName;    
    223.  
    224. [b]    char *ptr = (char *)hModule;
    225.     ptr += 0x3c;
    226.     ptr = (char *)(*(ULONG *)ptr) + hModule + 0x78;
    227.     ptr = (char *)(*(ULONG *)ptr) + hModule;
    228.     dwNameCount = *(ULONG *)(ptr + 24);[/b]
    229.     AddressOfNames = (ULONG *)(*(ULONG *)(ptr + 32) + hModule);
    230.     AddressOfOrdinals = (USHORT *)((*(ULONG *)(ptr + 36)) + hModule);
    231.     AddressOfFunctions = (ULONG *)((*(ULONG *)(ptr + 28)) + hModule);
    232.  
    233.     DBGPRINT("%d", dwNameCount);
    234.     for(i = 0; i < dwNameCount; i++){
    235.         pszExportName = (char *)(AddressOfNames[i] + hModule);
    236.         DBGPRINT("%s", pszExportName);
    237.         if(mystrcmp(pszExportName, pszName) == 1)
    238.             return (ULONG)(AddressOfFunctions[AddressOfOrdinals[i]]);
    239.     }
    240.     return 0;
    241.  
    242.  
    243. }
    244.  
    245. typedef struct _IMAGE_BASE_RELOCATION{
    246.     ULONG VirtualAddress;
    247.     ULONG SizeOfBlock;
    248. } IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
    249.  
    250. typedef struct {
    251.     USHORT offset: 12;
    252.     USHORT type: 4;
    253. } IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;
    254.  
    255. ULONG GetKiServiceTable(ULONG SDT, ULONG hModule){
    256.     char *ptr;
    257.     PIMAGE_BASE_RELOCATION pbr;
    258.     PIMAGE_FIXUP_ENTRY pfe;
    259.     ULONG bFirst = 1, dwPointerRVA, i;
    260.  
    261.     ptr = (char *)hModule;
    262.     ptr += 0x3c;
    263.     ptr = (char *)(*(ULONG *)ptr) + hModule + 0xA0;
    264.     pbr = (PIMAGE_BASE_RELOCATION)((char *)(*(ULONG *)ptr) + hModule);
    265.     while ((bFirst) || (pbr->VirtualAddress)){
    266.         bFirst = 0;
    267.         pfe = (PIMAGE_FIXUP_ENTRY)((ULONG)pbr + 8);
    268.         for (i = 0; i < (pbr->SizeOfBlock - 8) >> 1; i++, pfe++)
    269.             if (pfe->type == 3){
    270.                 dwPointerRVA = pbr->VirtualAddress + pfe->offset;
    271.                 if (*(PULONG)(hModule + dwPointerRVA) - 0x400000 == SDT)
    272.                     if (*(PUSHORT)(hModule + dwPointerRVA - 2) == 0x05c7)
    273.                         return (*(PULONG)(hModule + dwPointerRVA + 4) - 0x400000 + hModule);
    274.             }
    275.             *(PULONG)&pbr += pbr->SizeOfBlock;
    276.     }
    277.     return 0;
    278. }
    279.  
    280. ULONG GetKernelBase(){
    281.     ULONG cb = 0, i;
    282.     PSYSTEM_MODULE_INFORMATION p = NULL;
    283.     NTSTATUS ns;
    284.  
    285.     ZwQuerySystemInformation(SystemModuleInformation, &p, 0, &cb);
    286.     if (!cb)
    287.         return 0;
    288.     p = ExAllocatePool(PagedPool, cb);
    289.     if (!p)
    290.         return 0;
    291.     ns = ZwQuerySystemInformation(SystemModuleInformation, p, cb, &cb);
    292.     if (ns != STATUS_SUCCESS)
    293.         goto exit;
    294.     for (i = 0; i < p->dwCount; i++) {
    295.          if (mystrcmp("ntkrnlpa.exe", &(p->Modules[i].ImageName[p->Modules[i].ModuleNameOffset])) == 1)
    296.                  return (ULONG)p->Modules[i].Base;
    297.     }
    298. exit:
    299.     ExFreePool(p);
    300.     return 0;
    301. }
    302.  
    303. NTSTATUS RestoreSDT(){
    304.     ULONG SDT, KernelBase;
    305.     PNTPROC KiServiceTable;
    306.  
    307.     if (MapKernel() != STATUS_SUCCESS)
    308.         return STATUS_SUCCESS;
    309.     SDT = xGetProcAddress((ULONG)pKernel, "KeServiceDescriptorTable");
    310.     if (!SDT)
    311.         goto exit;
    312.     KiServiceTable = (PNTPROC)GetKiServiceTable(SDT, (ULONG)pKernel);
    313.     if (!KiServiceTable)
    314.         goto exit;
    315.     KernelBase = GetKernelBase();
    316.     if (!KernelBase)
    317.         goto exit;
    318.     //DrvUnhookQST(RESTORE_SYSCALL, (ULONG)(KiServiceTable[RESTORE_SYSCALL]) - 0x400000 + GetKernelBase());
    319. exit:
    320.     UnMapKernel();
    321.     return STATUS_SUCCESS;
    322. }
    323.  
    324. NTSTATUS DriverEntry(PDRIVER_OBJECT  DriverObject, PUNICODE_STRING RegistryPath){
    325.     DBGPRINT("Loading driver");
    326.     DriverObject->DriverUnload = DriverUnload;
    327.     RestoreSDT();
    328.     return STATUS_SUCCESS;
    329. }
    И точно также как в той теме, у меня идёт неверное чтение PE заголовка, а точнее адрес секции экспорта..
    Иными словами:
    Код (Text):
    1. dwNameCount = *(ULONG *)(ptr + 24)
    dwNameCount = 0
     
  6. nof

    nof New Member

    Публикаций:
    0
    Регистрация:
    22 июн 2010
    Сообщения:
    5
    Ребят, не могу понять...
    Код (Text):
    1. char *ptr = (char *)hModule;
    2.     ptr += 0x3c; // смещаемся до PE NT заголовка
    3.     ptr = (char *)(*(ULONG *)ptr) + hModule + 0x78; // смещаемся до адреса секции экспорта. Адрес, если смотреть через winhex: 0x00199000
    4.     ptr = (char *)(*(ULONG *)ptr) + hModule; // прибавляем к нему адрес по которому загружен модуль
    Всё верно? Так вот что я вижу (если рассматривать бинарник с диска при помощи winhex):

    [​IMG]
    Выделенные байты: 0x00199000
    Их же мне показывает PEID при анализе этого же бинарника:
    [​IMG]
    Но как видно по этому скриншоту, по этому адресу в реале ничего нет.. поэтому и код не работает, как я понимаю.
    Если в peid посмотреть дамп секции экспорта, там откуда-то всплывают вообще другие адреса: 0x00181200 - 0x0018c7a1
    Откуда он их взял? :/
     
  7. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    RVA это Relative Virtual Address - адрес относительно базы образа.
    А ты смотришь смещение. Тебе нужно узнать по таблице секций, какой секции соответствует твой RVA, добавить PointerToRawData и вычесть VirtualAddress.
    Получишь смещение в файле.

    Но все это делать не нужно будет в коде - ZwMapViewOfSection секцию с SEC_IMAGE отмапит согласно таблице секций.

    Прочитай про пе формат
     
  8. artyom

    artyom New Member

    Публикаций:
    0
    Регистрация:
    17 дек 2008
    Сообщения:
    2
    Еще информация от 90210, http://rootkit.com/newsread.php?newsid=176
     
  9. nof

    nof New Member

    Публикаций:
    0
    Регистрация:
    22 июн 2010
    Сообщения:
    5
    Во всех статьях, которые я читаю, VirtualAddress'ом называют как раз RVA. Получается, что мне надо к RVA прибавить PointerToRawData и вычесть этот же RVA? :) не понял.. что вы подразумеваете под VirtualAddress?
    Секцию моего RVA (00199000) я определил - .edata
    Что с этой секцией делать не понял :/
     
  10. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Почитай "от зелёного к красному" http://www.wasm.ru/article.php?article=green2red02, там подробно объясняется что к чему. Вычисление offset'a по RVA оттуда
    Код (Text):
    1. //Base - файл проецируется в память, это его база
    2. //RVA - значение, которое нужно преобразовать в Offset
    3. DWORD RVAtoOffset(DWORD Base,DWORD RVA)
    4. {
    5.     PIMAGE_NT_HEADERS pPE=(PIMAGE_NT_HEADERS)((long)Base+((PIMAGE_DOS_HEADER)Base)-»e_lfanew);
    6.     short NumberOfSection=pPE-»FileHeader.NumberOfSections;
    7.     long SectionAlign=pPE-»OptionalHeader.SectionAlignment;
    8.     PIMAGE_SECTION_HEADER Section=(PIMAGE_SECTION_HEADER)
    9.        (pPE-»FileHeader.SizeOfOptionalHeader+(long)&
    10.        (pPE-»FileHeader)+sizeof(IMAGE_FILE_HEADER));
    11.     long VirtualAddress,PointerToRawData;
    12.     bool flag=false;
    13.     for (int i=0;i«NumberOfSection;i++)
    14.     {
    15.         if ((RVA>=(Section-»VirtualAddress))&&
    16.            (RVA«Section-»VirtualAddress+
    17.            ALIGN_UP((Section-»Misc.VirtualSize),SectionAlign) ))
    18.         {
    19.             VirtualAddress=Section-»VirtualAddress;
    20.             PointerToRawData=Section-»PointerToRawData;
    21.             flag=true;
    22.             break;
    23.         }
    24.         Section++;
    25.     }
    26.     if (flag) return RVA-VirtualAddress+PointerToRawData;
    27.     else return RVA;
    28. }
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Да, проблема в том, что поле, содержащее RVA, называется IMAGE_SECTION_HEADER::VirtualAddress. Но там лежит относительный виртуальный адрес.
    Определимся с терминологией:
    Virtual address - адрес байта в памяти после загрузки модуля.
    Relative Virtual Address - адрес байта в памяти относительно базы модуля.
    File offset (в структуре IMAGE_SECTION_HEADER: PointerToRawData) - смещение байта внутри файла.

    Простые правила:
    1) VA = ImageBase + RVA
    2) Везде, кроме таблицы секций, в PE заголовках лежат RVA.
    3) Чтобы вычислить RVA по смещению Offset в файле, нужно пройтись по таблице секций, найти секцию, у которой (PointerToRawData >= Offset && (PointerToRawData + SizeOfRawData) < Offset). Как только нашли, берем поля VirtualAddress (это RVA!) и PointerToRawData секции и считаем: RVA = VirtualAddress + (Offset - PointerToRawData).
    (То бишь мы отнимаем от нашего смещения смещение начала секции и добавляем RVA начала секции, получаем RVA байта)
    4) Чтобы вычислить смещение по RVA, нужно найти секцию, у которой (VirtualAddress >= RVA && (VirtualAddress + SizeOfRawData, VirtualSize)) < RVA). Как только нашли такую секцию, можно узнать смещение байта: Offset = (RVA - VirtualAddress) + PointerToRawData. (Вычитаем из RVA байта RVA начала секции, получаем смещение внутри секции. Добавляем смещение начала секции, получаем смещение байта в файле).
     
  12. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    haxorart
    Странно кстати, авторы там пишут, цитирую
    а сами полагаются на VirtualSize во всех случаях в RVAToOffset()

    На самом деле, достаточно прибавить SizeOfRawData - все равно, если виртуальный размер больше, то оставшиеся байты не имеют эквивалента на диске (выравнивание или неинициализированные данные).
     
  13. haxorart

    haxorart New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    123
    Даже на солнце есть пятна. Но это самое понятное объяснение формата РЕ из тех, которые я читал.
     
  14. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    o Побить ядро.
    o Диспатч багчека, восстановление состояния задачи, откат ошибки и трейс.
    o Профит..