Computer Emergency Response Center

Windows ToolHelp32 kitabxanasından yayınma 1-ci hissə

TlHelp32 icra oluna bilən proqram təminatları haqqında informasiya əldə edilməsi üçün istifadə edilən funksiyalardan ibarət kitabxanadır. Bu kitabxana köməkliyi ilə sistemdə fəaliyyət göstərən proseslər, onları istifadə etdikləri modullar (dll), aktiv heap adresləri, icra olunan threadlar və s. haqqında məlumat əldə etmək mümkündür. Daha ətraflı: https://learn.microsoft.com/en-us/windows/win32/api/_toolhelp/

Məqalədə göstərilən metod “proof of concept” olaraq qəbul edilməlidir. Müdaxilə edilən prosesin düzgüz işləyib işləməyəcəyi haqqında tam olaraq test fəaliyyəti həyata keçirilməmişdir.  TlHelp32 kitabxanası ilə prosesə aid modulları enumerate etmək üçün 3 funksiya istifadə edilir. CreateToolhelp32Snapshot, Module32FirstModule32Next. Sırası ilə ilk öncə CreateToolhelp32Snapshot ilə proses məlumatlarının funksiya çağrıldığı an snapshotu alınır. (snapshot əməliyyatı o deməkdir ki, funksiya çağrıldıqdan sonra proses üzərində hər hansı dəyişiklik qeydə alınmır). Daha sonra Module32FirstModule32Next funksiyalarının köməkliyi ilə snapshot zamanı alınan buffer üzərində lazımı modul məlumatları götürülür və MODULEENTR32 strukturuna yazılır.

HANDLE CreateToolhelp32Snapshot(
[in] unsigned long dwFlags,
[in] unsigned long th32ProcessID
);

Funksiya snapshot götürüləcək proses id dəyərini və hansı məlumatı götürəcəyini bildirən flag qəbul edir.

Flaglardan bəziləri:

Flag

Value

Description

TH32CS_SNAPHEAPLIST

0x00000001

Heap

TH32CS_SNAPMODULE

0x00000008

Module

TH32CS_SNAPMODULE32

0x00000010

Module32 (x64)

TH32CS_SNAPPROCESS

0x00000002

Process

TH32CS_SNAPTHREAD

0x00000004

Threads

Bu məlumatların haqqında informasiya götürüldükdən sonra isə Module32 funksiyaları ilə modulların siyahısı götürülür. Bu əməliyyatları həyata keçirən 2 ədad kiçik alət yazırıq.

  • testapp
  • modulelist

modulelist ilə testapp-ya aid modulların siyahısı alınacaqdır. Daha sonra isə testapp yaddaşında bəzi əməliyyatlar ilə modulelist-dən necə yayınmaq olar buna baxacağıq.

testapp.c

#include 
#include 

int main(void)
{
    printf("my pid: %d\n", GetCurrentProcessId());
    getchar();
}

modulelist.c

#include 
#include 
#include 
#include 

int main(int argc, char* argv[])
{
    HANDLE hModuleSnapshot = INVALID_HANDLE_VALUE;
    MODULEENTRY32W mod32 = {0};
    hModuleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, atol(argv[1]));
    if ( hModuleSnapshot == INVALID_HANDLE_VALUE)
    {
        printf("Error CreateToolhelp32Snapshot");
        return -1;
    }
    mod32.dwSize = sizeof(MODULEENTRY32W);
    
    if (!Module32FirstW(hModuleSnapshot, &mod32))
    {
        printf("Error Module32FirstW");
        return -1;
    }
    do
    {
        wprintf(L"%s\n", mod32.szModule);
    }while(Module32NextW(hModuleSnapshot, &mod32));
    return 1;
}

TestApp istifadəçidən (stdin) hər hansı key daxil edilməsini istəyir və o zamana qədər gözləmə rejimində qalır. Modulelist isə özünə commandline üzərindən gələn proses modullarını list edir. Burada önəmli olan snapshot məlumatlarının haradan götürüldüyüdür. Çünki Module32 funksiyaları snapshot (local buffer) üzərindən bu məlumatları oxuyur. CreateToolhelp32Snapshot funksiyası prosesə aid modulların informasiyasını prosesə aid bir başqa data struktur (PROCESS_ENVIRONMENT_BLOCK) üzərindən götürür.

Burada bizə lazım olan növbəti struktur isə PEB_LDR_DATA strukturudur.

0:001> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY
   +0x024 EntryInProgress  : Ptr32 Void
   +0x028 ShutdownInProgress : UChar
   +0x02c ShutdownThreadId : Ptr32 Void


Göy rəng ilə işarə etdiyim offsetlər modulların saxlanıldığı data strukturlardır. Bu data struktur isə LIST_ENTRY adı verilən və microsoft tərəfindən istifadə edilən doubly-linked data strukturudur.

0:001> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
   +0x000 Flink  : Ptr32 _LIST_ENTRY
   +0x004 Blink  : Ptr32 _LIST_ENTRY

Flink (forward) bir növbəti listin adresini , Blink (backward) isə öncəki listin adresini özündə saxlayır. Buna gorə doubly-linked adlandırılır. Tlhelp32 modul siyahısı alarkın məhz bu listi gəzir və modullar haqqında məlumat toplayır. LIST_ENTRY tək başına istifadə edilən bir data struktur deyil. LIST_ENTRY bir neçə data strukturu bir-birinə bağlamaq üçün istifadə edilən bir strukturdur. Burada bir birinə bağlanılan məlumatlar haqqında tam olmasada Microsoft bizə kiçik bir hint verir. The head of a doubly-linked list that contains the loaded modules for the process. Each item in the list is a pointer to an LDR_DATA_TABLE_ENTRY structure.

Testapp üzərindən istifadə edilən modullara göz gəzdirək. İlk öncə r $peb əmri ilə prosesin peb adresini öyrənirik.

0:001> r $peb
$peb=00595000

Daha sonra dt _PEB @$peb əmri ilə prosesin environment block strukturuna baxırıq:

Bundan sonra isə dt _PEB_LDR_DATA 0x77815d80 əmri ilə LDR_DATA struktuna keçid edirik:

Burada 2 ədəd LIST_ENTRY strukturu mövcuddur. Hərbiri eyni data struktura işərə etsədə load order filter tətbiq edilib. Mənim analizlərimdə CreateToolhelp32Snapshot InLoadOrderModuleList üzərindən məlumatları toplayır. Buna görə məhz bu list üzərində əməliyyat aparacam. LIST_ENTRY strukturunun əslində tək başına olmadığını qeyd etmişdim. Bu strukt LDR_DATA_TABLE_ENTRY strukturnun bir hissəsidir.

Modulelist alətinin verdiyi məlumatı xatırlayaq:

  1. testapp.exe
  2. ntdll.dll
  3. kernel32.dll
  4. kernelbase.dll

PEB_LDR_DATA strukturuna müdaxilə

Modul məlumatlarını saxlayan LDR_DATA_TABLE_ENTRY strukturnun LIST_ENTRY strukturu ilə bir-birinə necə zəncir şəklində bağlandığını anladınız (ümid edirəm). Bizə lazım olan modulu gizlətmək üçün belə bir yol izləyəcəm. A-B-C bir-birinə bağlıdırsa, buradan B-nı aradan götürə bilsək (yəni A-nı birbaşa C-yə bağlaya bilsək) modulumuzu gizlətmək mümkündür. Aşağıdakı diaqrama diqqət yetirin:

Keçirik yeniden PEB_LDR_DATA strukturuna. Burada LIST_ENTRY data strukturu üzərində dəyişikliyimizi edirik. İlk LDR_DATA_TABLE_ENTRY testapp modulunu işarə edirdi bildiyiniz kimi. Bunu özündən sonra gələn modulun Flink adresi ilə dəyişirik.

Burada 0x77ba5d8c adresində olan 32 bitlik adresi 0xe23198 adresi ilə dəyişirik.

.g(go) əmri ilə proqram qaldığı yerdən işinə davam edir və modulelist alətinin nəticəsi:

 

 

İstinadlar

[1] https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
[2] https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/singly-and-doubly-linked-lists
[3] https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm
[4] https://www.nirsoft.net/kernel_struct/vista/PEB_LDR_DATA.html

Press ESC to close