Kilka słów na temat syscalli

Posted in assembler on Grudzień 16th, 2009 by Grzonu

No to chciałem napisać arta o tym jak windows wywołuje funkcje do wywołania których konieczne jest wejście w tryb

jądra do którego program usera który jest w ring3(sterowniki i jądro sa w ring0) nie ma dostepu. Mechanizm ten nosi

nazwe syscall. Przedstawie jak wyglada przykładowe wywołanie np. ZwTerminateProcess.

1
2
3
4
5
6
7
8
9
10
11
PUSH 0
PUSH -1     ;rzucamy na stos parametry funkcji
CALL TERM   ;wywolujemy ZwTerminateProcess
RET
TERM:
MOV EAX,101h ;do eax kopiujemy numer funkcji (tutaj jest to numer ZwTerminateProcess)
CALL SYS     ;wywolujemy syscall (tak samo wyglada funkcja KiFastSystemCall)
SYS:
MOV EDX,ESP
SYSENTER     ;tu nastepuje przejscie do trybu jądra
RET

Widzimy wiec ze nie jest to trudne i wydaje sie bardzo wygodne w roznych shellcodach itp ale jak zawsze jest jakies

ale no wiec w tym przypadku oczywiscie tez jest i jest nim to ze numer funkcji jest różny w kazdej wersji systemu.
Mamy 2 opcje albo probujemy dynamicznie pobrac z ZwTerminateProcess który jest w ntdll ale tu musimy znalesc jej

adres. Metoda ta jest dobra jesli potrzebujemy kompatybilnosci z KAZDYM systemem. jesli potrzebujemy

kompatybilnosci tylko z wybranymi to jest inna metoda na pobranie wersji systemu i dopasowanie odpowiedniego numeru

syscalla bez uzywania jakiegokolwiek wywolania API.

1
2
3
4
MOV EAX,DWORD PTR FS:[18h]	;do eax kopiujemy adres TEB
MOV EDI,DWORD PTR DS:[EAX+30h]	;do edi kopiujemy adres PEB
MOV EBX,DWORD PTR DS:[EDI+A4h]	;do ebx kopiujemy major version number
MOV ECX,DWORD PTR DS:[EDI+A8h]	;do ecx kopiujemy minor version number

przykladowo dla XP wersja to 5.1 wiec major = 5 a minor = 1 wiec ebx = 5 a ecx = 1
numery innych systemow znajdziemy w MSDN

Teraz mozemy juz dopasowac numer zaleznie od wersji jaka chcemy.

Jakie sa inne zalety?
-Omijamy w ten sposob wszystkie hooki zalozone w user-mode.
-Nie musimy szukac adresu potrzebnej nam funkcji.
-Kod dzieki temu jest mniejszy

Teraz mała uwaga dla piszacych sterowniki.
Dodam na koniec ze numer funkcji jest indexem danej funkcji w tabeli SSDT (numery 1-1000) i ShadowSSDT(1000+).
Obie te tablice znajdują się w przestrzeni adresowej jądra więc nie mamy do niej dostepu z User-mode.
W tablicy SSDT znajduja sie funkcji eksportowane przez ntoskrnl.exe(jądro) a w ShadowSSDT funkcje eksportowane

przez win32k.sys gdzie znajduja sie funkcje zwiazane z okienkami,wejsciem,wyjsciem. SSDT jest eksportowane przez

jadro i nie ma wiekszych klopotow z jego znalezieniem za to ShadowSSDT nie jest eksporotwane i metode na jej

znalezienie wymyslil Alexander Volynkin(http://www.volynkin.com/sdts.htm)

Tablice syscalli dla poszczególnych systemów:

http://j00ru.vexillium.org/win32k_syscalls/

http://www.metasploit.com/users/opcode/syscalls.html

Tags: , , , , ,

Niekonwencjonalna metoda zabijania procesu ;)

Posted in C++, assembler on Październik 7th, 2009 by Grzonu

Dziś chcialbym napisac o tym jak mozna zabic proces ktory w jakis sposob sie przed tym broni(nie jest to jeszcze 100% skuteczne metoda ale na 99% aplikacji nie ingerujacych w KM* powinna dzialac)

Co bedziemy chcieli zrobic:
-Otworzyc proces
-Zaalokowac kawałek pamieci w tym procesie
-Wrzucic do tej pamieci shellcode
-Utworzyc wątek w tym procesie ktory uruchomi shellcode

Co będzie robil shellcode?
Będzie wywolywal funkcje ZwTerminateProcess za pomoca syscall`a ;)

Czemu akurat tak? A po to aby uminać wszystkie hooki UM*
Najpierw o tym jak wywolywana jest funkcja przez syscall:
-na stos rzucamy parametry funkcji
-do rejestru eax wrzucamy numer funkcji
-do rejestru edx wrzucamy esp
-uzywamy mnemonika SYSENTER aby wywolac funkcje

wyglada to mniej wiecej tak:

1
2
3
4
5
6
7
8
9
10
11
PUSH 0
PUSH -1
CALL TERM
RET
TERM:
MOV EAX,101h ;101h <- ZwTerminateProcess dla XP
CALL SYS
SYS:
MOV EDX,ESP
SYSENTER
RET

Pozostaje jeszcze jedna kwestia zwiazana z wywolaniem syscall`a
W kazdej wersji systemu numery syscall`i sie zmieniaja. Aby nie wpisywac ich „na sztywno” pobierzemy je dynamicznie z kodu funkcji ZwTerminateProcess ;) (tu naleza sie podziekowania dla j00ru ktory mnie na to naprowadzil)
debugujemy tą funkcje i widzimy instrukcje mov eax,101h (B8 01010000)
widzimy więc ze aby uzyskac numer funkcji poprostu odczytamy bajty 2-5 tejze funkcji

Jeszcze jedna uwaga i przejdziemy do kodu ;)
Aby otworzyc niektore procesy potrzebujemy pewnych uprawnien …nadajmy je sobie ;)

1
2
3
4
5
6
7
8
9
10
11
12
void DebugPriv()
{
HANDLE hToken;
    TOKEN_PRIVILEGES tkp;
    if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
        tkp.PrivilegeCount = 1;
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(hToken, 0, &tkp, sizeof(tkp), NULL, NULL);
    }
}

No dobrze teraz troche innego kodu ;)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <windows.h>
char shellcode[]={
"\x6A\x00\x6A\xFF\xE8\x01\x00\x00\x00\xC3\xB8\x01\x01\x00\x00\xE8\x01\x00\x00\x00\xC3\x8B\xD4\x0F\x34\xC3"}; //nasz shellcode
DWORD shellcode_size=0x20;//rozmiar shellcodu
 
void KillApp(DWORD pid)//pid - process id
{
char code[0x20];
memcpy(code,shellcode,0x20);//kopiujemy shellcode do innego bufora
HMODULE h=GetModuleHandle("NTDLL.DLL");
FARPROC p=GetProcAddress(h,"ZwTerminateProcess");//pobieramy adres ZwTerminateProcess
memcpy((char*)(code+0x0B),(char*)((char*)p+1),4);//kopiujemy numer funkcji tak jak mówiłem wczesniej
HANDLE hProc=0;
	hProc=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_CREATE_THREAD, FALSE, pid);//otwieramy proces
LPVOID hRemoteMem = VirtualAllocEx(hProc, NULL, 0x20, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//alokujemy pamiec w procesie
DWORD numBytesWritten;
    WriteProcessMemory(hProc, hRemoteMem, code, 0x20, &numBytesWritten);//zapisujemy nasz shellcode do procesu
HANDLE hRemoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)hRemoteMem, 0, 0, NULL);//tworzymy zdalny watek ktory wykona kod zapisany w procesie
CloseHandle(hProc);//mozemy juz zamknac uchwyt ...i tak wskazuje na nieistniejacy proces :D

No dobrze wszystko fajnie …ale co jesli perfidna aplikacja zablokuje nam mozliwosc otwarcia procesu
mozemy probowac 2 metod
1. Debugowac aplikacje
2. Wyciągnąć ten uchwyt z procesu csrss.exe ktory przechowuje uchwyty wszystkich aplikacji(no prawie wszystkich)

Sposob 1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HANDLE OpenProcessFromDebug(DWORD pid)
{
if(DebugActiveProcess(pid)!=0)//Zaczynamy Debuggowanie
{
DEBUG_EVENT DebugEvent;
while(1)
{
if(WaitForDebugEvent(&DebugEvent, INFINITE))//czekamy na event o jakims zdarzeniu
{
DebugActiveProcessStop(pid);//konczymy debuggowanie
return DebugEvent.u.CreateProcessInfo.hProcess;//zwracamy uchwyt procesu ktory dostajemy w evencie ;)
}
}
}
return 0;
}

Sposob 2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
 
typedef DWORD (__stdcall *CSRPID)(void);
typedef DWORD (__stdcall *NTQSI)(ULONG SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength, ULONG* ReturnLength);
#define SystemHandleInformation 16
 
typedef struct {
    ULONG OwnerPid;
    BYTE ObjectType;
    BYTE HandleFlags;
    USHORT HandleValue;
    PVOID ObjectPointer;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_ENTRY;
 
typedef struct {
    ULONG HandleCount;
    SYSTEM_HANDLE_ENTRY Handles[1];
} SYSTEM_HANDLE_INFORMATION;
 
 
HANDLE OpenProcessFromCsrss(DWORD pid)
{
 
        HMODULE ntdll = GetModuleHandle("ntdll");
 
                CSRPID CsrGetProcessId = (CSRPID)GetProcAddress(ntdll, "CsrGetProcessId");
                NTQSI NtQuerySystemInformation = (NTQSI)GetProcAddress(ntdll, "NtQuerySystemInformation");
 
 
                DWORD csrss_pid = CsrGetProcessId();//pid procesu csrss
                HANDLE csrss = OpenProcess(PROCESS_DUP_HANDLE, FALSE, csrss_pid);//otwieramy proces csrss
                if (!csrss)
                {
                        return 0;
                }
 
                ULONG size = 0x1000;
                SYSTEM_HANDLE_INFORMATION *handles = (SYSTEM_HANDLE_INFORMATION*)malloc(size);
//alokujemy miejsce na strukture zwracana przez NtQuerySystemInformation
                DWORD status;
 
                        ULONG need;
                        status = NtQuerySystemInformation(SystemHandleInformation, handles, size, &need);
                       if(status!=0)//jesli mamy przeznaczylismy za malo miejsca to dodajmy wiecej ;)
					   {
                        free(handles);
                        size =need;
                        handles = (SYSTEM_HANDLE_INFORMATION*)malloc(size);
						NtQuerySystemInformation(SystemHandleInformation, handles, size, &need);
						}
 
 
                        int i;
		HANDLE h_proc;
//mamy juz nasza strukture ...teraz poszukajmy naszego uchwytu
                        for (i=0; i<handles->HandleCount; i++)
                        {
                                if (handles->Handles[i].OwnerPid == csrss_pid)//wlascicielem uchwytu jest csrss
                                {
                                        if (DuplicateHandle(csrss, (HANDLE)handles->Handles[i].HandleValue, GetCurrentProcess(), &h_proc, 0, FALSE, DUPLICATE_SAME_ACCESS))//duplikujemy uchwyt
                                        {
                                                if (GetProcessId(h_proc) == pid) //pobieramy PID z uchwytu i jesli sie zgadza to zwracamy ten uchwyt
                                                {
                                                        return h_proc;
                                                }
                                                CloseHandle(h_proc);
                                        }
                                }
                        }
 
                if (handles) free(handles);
                CloseHandle(csrss);
 
 
        return 0;
}

No to chyba na tyle w tym temacie, jesli cos mi jeszcze wpadnie do glowy to dopisze ;)

Jeszcze jedna mała uwaga …za pomoca szukania uchwytow w csrss mozna odnalesc ukryte procesy ;)

Cały kod pełnosprawnej aplikacji znajduje sie ponizej :)
CLICK

Bede wdzieczny za wszelkie komentarze i uwagi

*UM-User Mode(ring3)
*KM-Kernel-Mode(ring0)

Tags: , , , , , ,