Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

Tajemnica SystemFunction040, czyli jeszcze o API Monitorze

W poprzednim wpisie wspomniałem o API Moniotrze, dzięki któremu mogliśmy z bliska podejrzeć zestaw parametrów przekazanych do jednej z funkcji systemowych tworzących nowy proces. Tym razem zrobimy jeszcze jeden krok - pokusimy się o rozszerzenie zbioru API Monitora o funkcje systemu, których jeszcze nie zna.
W tym celu weźrzymy w trzewia klasy SecureString, która wdzięcznie pomagała nam przy naszych poprzednich zabawach, tym razem jednak to ją samą weźmiemy na stół doktora Reflectora. Albo nie - użyję do tego celu małego skalpelka opartego o bibliotekę Cecil.Decompiler, które można szybko skroić do własnych potrzeb, ot jak chociażby zrzutu metody 'ProtectMemory', której SecureString używa do ukrycia danych szyfrowanych. A wewnątrz niej mamy coś takiego:

private void ProtectMemory()
{
       if (this.m_length)
       {
               if (this.m_enrypted)
               {
                       return;
               }
       }
       RuntimeHelpers.PrepareConstrainedRegions();
       try
       {
       }
       finally
       {
               int a = Win32Native.SystemFunction040(this.m_buffer, this.m_buffer.Length * 2, 0);
               if (a < 0)
               {
                       throw new CryptographicException(Win32Native.LsaNtStatusToWinError());
               }
               this.m_enrypted = 1;
       }
}

Uwagę przykuwa wywołanie najprawdopodobniej systemowej funkcji SystemFunction040, do której w buforze m_buffer trafia wszystko to, co zamierzamy zaszyfrować. Szybki rekonensans po zasobach online MSDN doprowadza nas do funkcji RtlEncryptMemory z biblioteki advapi32.dll.
Spróbujmy zatem przyjrzeć się danym przekazywanym do wspomnianej funkcji i oczywiście użyjmy do tego celu API Monitora. Uruchamiamy zatem API Monitor, używamy lornetki (ctrl+f) do wyszukania naszej SystemFunction040 i... nici. Nie ma, API Monitor nic nie wie o tej funkcji. To może RtlEncryptMemory? Znowu lorneta (na meduzach? ;)) i znowu nici :( Czyżbyśmy nie byli w stanie użyć APIMona do naszych celów? Nic bardziej mylnego!

Zaglądamy do podkatalogu API/Windows znajdującego się w folderze instalacyjnym API Monitora i znajdujemy plik advapi32.xml. Po ostatnim zamykającym bloku </api> dopisujemy nową sekcję:

       <Category Name="Zine.net/Security" />
       <Api Name="SystemFunction040">
           <Param Type="PVOID" Name="Memory" />
           <Param Type="ULONG" Name="MemoryLength" />
           <Param Type="ULONG" Name="OptionFlags" />
           <Return Type="NTSTATUS" />
           <Success Return="Equal" Value="0" />
       </Api>
       <Api Name="SystemFunction041">
           <Param Type="PVOID" Name="Memory" />
           <Param Type="ULONG" Name="MemoryLength" />
           <Param Type="ULONG" Name="OptionFlags" />
           <Return Type="NTSTATUS" />
           <Success Return="Equal" Value="0" />
       </Api>
       <Api Name="RtlEncryptMemory">
           <Param Type="PVOID" Name="Memory" />
           <Param Type="ULONG" Name="MemoryLength" />
           <Param Type="ULONG" Name="OptionFlags" />
           <Return Type="NTSTATUS" />
           <Success Return="Equal" Value="0" />
       </Api>
       <Api Name="RtlDecryptMemory">
           <Param Type="PVOID" Name="Memory" />
           <Param Type="ULONG" Name="MemoryLength" />
           <Param Type="ULONG" Name="OptionFlags" />
           <Return Type="NTSTATUS" />
           <Success Return="Equal" Value="0" />
       </Api>

w której umieszczamy oczywiście obie funkcje używane przez SecureString - do szyfrowania i deszyfrowania danych i do tego w wersjach o nazwach SystemFunction04(0|1) oraz odpowiadających im Rtl(En|De)cryptMemory.

Funkcja SystemFunction040 w API Monitorze

Przeładowujemy API Monitor, uruchamiamy przykład z mojego poprzedniego wpisu i patrzymy na to, co się dzieje. W moim przypadku, po wpisaniu 12 znaków z klawiatury dostałem wynik, który widać na rysunku. Zauważmy, że parametr OptionFlags ustawiany jest na 0, co znaczy, że inne procesy nie będą miały dostępu do naszych danych i tylko z naszego procesu będziemy mogli odszyfrować dane. Pierwszy parametr zawiera adres naszego bufora, który obejrzany w czasie sesji z WinDbg oczywiście pokazuje nam swoją zawartość i - co nas nie dziwi - nie zawiera całego ciągu, a wyłącznie ostatni znak, właśnie przekazywany do funkcji szyfrującej.
Zgodnie z dokumentacją, aby skorzystać z funkcji SystemFunction040, należy użyć LoadLibrary + GetProcAddress, czyli nie ma możliwości statycznego zlinkowania. Dodatkowo, Microsoft zaleca użycie funkcji CryptProtectMemory, zamiast wspomnianej RtlEncryptMemory, więc pojawia się pytanie: dlaczego twórcy .NET Framework nie stosują się do tego zalecenia?
Za funkcją RtlEncryptMemory stoi sterownik ksecdd.sys, który wykonuje właściwą pracę szyfrowania i deszyfrowania. Funkcja SystemFunction040 stanowi namiastkę DPAPI, do którego jeszcze wrócę w następnych wpisach :)

Wróćmy jednak do API Monitora. Ktoś mógłby spytać: a co w sytuacji, gdy nie ma definicji biblioteki, która nas interesuje, a my chcielibyśmy z niej skorzystać? Nie ma problemu! Wystarczy bowiem dodać w katalogu z plikami .xml definiującymi API nowy plik, a w nim dopisać odpowiednią kategorię oraz API i gotowe. Dla przykładu, wyobraźmy sobie, że mamy bibliotekę zine.dll, w której jest interesujący nas eksport ZineExport ze znanymi nam parametrami. Dodajemy wówczas plik zine.xml do jednego z katalogów, a w nim - podobnie jak zrobiliśmy to wyżej w odniesieniu do funkcji SystemFunction040 - dopisujemy odpowiednią sekcję z kategorią, do której ma wpaść nasze API oraz oczywiście samą funkcję w tagu <Api Name="ZineExport">. Później przeładowujemy API Monitor i cieszymy się naszą nową definicją :)

Na koniec jeszcze dwa słowa odnośnie bezpieczeństwa klasy SecureString i metody ProtectMemory.

Użycie sterownika ksecdd.sys spełniającego wysokie normy bezpieczeństwa pozwala czuć się bezpiecznie. W żadnym momencie nie ma w pamięci procesu wszystkich danych w postaci odszyfrowanej i tylko chwila ‘wyciągania’ danych może stanowić zagrożenie, o czym pisałem już we wcześniejszych wpisach.
Klasa SecureString używa obszarów pamięci niezarządzanej, o której Garbage Collector nic nie wie. Jednak dzięki implementacji IDisposable (użycie using, na które zwróciłem poprzednio uwagę), klasa SecureString grzecznie sprząta po sobie w deterministyczny sposób. Daje nam to jeszcze większą kontrolę nad tym jak długo nasze 'wrażliwe' dane znajdują się w pamięci. Dodatkowo, m_buffer dziedziczy po mieczu po typie CriticalFinalizerObject, co gwarantuje dokładne wyczyszczenie zawartości po odśmieceniu przez GC. Czy możemy czuć się bezpiecznie? Tak! Czy warto korzystać z tej klasy? Zdecydowanie tak.

W przyszłym wpisie rzucimy okiem na to, co się dzieje w powershellu, w którym SecureString znalazł należne sobie miejsce, a także czy zaszyfrowany SecureString to dobry sposób na przechowywanie istotnych danych. :) Jednym słowem grzebnniemy głębiej w DPAPI :)

Opublikowane 7 marca 2011 15:33 przez mgrzeg

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

marca 7, 2011 21:48
 

arkadiusz.wasniewski said:

Michał. Super tekst.

Arek

marca 8, 2011 21:47
 

ucel said:

Czekam na ciag dalszy :)

marca 9, 2011 10:29
 

mgrzeg said:

Dzięki, chłopaki. Takie słowa zachęcają do dalszego wysiłku :)

marca 11, 2011 18:33

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems