HASP i Visual FoxPro 9

Ostatnio natrafiłem na aplikację stworzoną w środowisku Visual FoxPro 9, zabezpieczona była kluczem sprzętowym HASP Hardlock, a sam plik wykonywalny aplikacji był zaszyfrowany (tzw. envelope).

Struktura aplikacji Visual FoxPro

Z nielicznych kontaktów z oprogramowaniem Visual FoxPro wiedziałem, że każda taka aplikacja składa się z prostego loadera (który uruchamia cały engine VFP znajdujący się w dodatkowych plikach DLL) oraz właściwej aplikacji, która znajduje się na końcu pliku wykonywalnego (zapisana jako overlay), całość jest więc w jednym pliku wykonywalnym. Tak jest w przypadku programów niezabezpieczonych:

W przypadku zabezpieczenia HASP, na końcu zabezpieczonego pliku faktycznie znajdują się jakieś dane, ale nie są to dane aplikacji VFP, gdyż nie można jej zdekompilować oprogramowaniem ReFox.

Zabezpieczenia dla aplikacji z overlayami

Dane overlay (tak zapisane dane są w aplikacjach Flash, Director, Shockwave etc.) nie są mapowane w pamięci procesu i muszą być odczytane bezpośrednio z dysku. Popularną metodą ochrony takich aplikacji jest szyfrowanie tych danych i hookowanie funkcji systemu plików (np. CreateFile, ReadFile etc.) w samej aplikacji, poprzez podstawienie adresów funkcji zastępczych w tabeli importów aplikacji. Jeśli aplikacja otworzy swój własny plik i będzie probowała czytać dane overlaya, nie zostanie odczytane to co jest na dysku, ale to co zostało wczesniej zaszyfrowane.

Takie rozwiązanie przez hookowanie tabeli importów działa jedynie w przypadku samodzielnych plików (bez dodatkowych bibliotek), w przypadku aplikacji VFP, dane z pliku wykonywalnego czytane są przez dodatkowe biblioteki, więc wymagany byłby system wide hook.

Envelope HASP dla aplikacji VFP

System zabezpieczeń HASP dla takich plików, działa w ten sposób, że sterownik systemowy HASP-a podpina się pod funkcje systemowe Windows odpowiedzialne za czytanie plików i obecność oryginalnego overlaya jest emulowana tylko dla zabezpieczonej aplikacji i jej bibliotek. Wygląda to mniej więcej tak:

Odczytanie overlaya

Jak zatem uzyskać obraz całego overlaya? Z przeprowadzonych eksperymentów wynika, że dane nie są nigdzie czytane w całości, więc nie ma takiego punktu w programie (a grzebanie w engine VFP nie należy do najprzyjemniejszych), gdzie by można było zrzucić pamięć całego overlaya.

Jednak jeśli czytanie danych overlaya jest emulowane dla samej zabezpieczonej aplikacji, można wstrzyknąć kod czytający zawartość całego pliku do zabezpieczonej aplikacji i dzięki temu uzyskać dane odszyfrowanego overlaya.

Jest tutaj kilka problemów. Po pierwsze, aby wstrzyknąc kod należy dojść do OEP zabezpieczonej aplikacji, co może sprawić trochę problemów, ja skorzystałem z OllyDbg i pluginu Phant0m, ponieważ envelope HASP-a wykrywa popularne narzędzia do debuggowania, co można zobaczyć na zrzuconym obrazie zabepieczonego pliku:

Znalezienie OEP jest relatywnie proste, wersja dla leniwych — zrzucić działający program z pamięci i wyszukać standardowego entrypoint-a dla aplikacji skompilowanych pod Visual C++ (szukać funkcji GetStartupInfoA). Potem wgrać na nowo plik i ustawić hardware breakpoint na tym adresie.

Będąc na adresie OEP można teraz wstrzyknąć kod odpowiedzialny za czytanie pliku. Aby zaoszczedzić trochę kodu, ja wykorzystałem fakt, że plik aplikacji jest przez nią otwierany w pewnym momencie, dzięki czemu nie trzeba pobierać jego ścieżki i go otwierać, a od razu mamy jego uchwyt, który można wykorzystać do odczytania overlaya:

Następnie skorzystałem ze skryptu ODBScript, żeby pod adresem 4015EE załadować kod, który odczyta zawartość pliku i zapisze go na dysku:

; usun wszystkie breakpointy
        bphwcall
        bc

; ustaw hardware bp na OEP
        bphws   401873,"x"

        run

; ustaw hardware bp na kod po _lopen
        bphws   4015F4,"x"

        run

        bphwcall

; kompiluj kod z pliku pod adres EIP
        asmtxt  eip, "C:\fox_dumper.asm"

; pomocniczy bp
        bphws   401644,"x"

        run

; zrzuc pamiec pod adresem ESI o rozmiarze EBX
        dm esi, ebx, "c:\dump.bin"

        log "VFP dumper zrobione."

        pause

Kod dumpera ładowany jest z zewnętrznego pliku, a nie jest wykorzystywana funkcja ODBScript wykonująca kod assemblera w przestrzeni adresowej aplikacji, ponieważ to po prostu nie działało (jakby ktoś pytał):

; w EAX jest uchwyt pliku
        mov     edi,eax                 ; hFile

; nawiguj na koniec pliku (korzystajac z funkcji
; z tabeli importow aplikacji)
        push    2                       ; FILE_END
        push    0                       ; offset
        push    edi                     ; hFile
        call    [40203C] ;_llseek
        mov     ebx,eax                 ; eax = current offset = size

; alokuj pamiec na rozmiar overlaya
        lea     eax,[ebx-4]             ; na koncu overala zapisany jest DWORD
                                        ; ktory wskazuje offset poczatku overlaya
                                        ; w pliku
        push    0                       ; FILE_BEGIN
        push    eax                     ; offset
        push    edi                     ; hFile
        call    [40203C] ;_llseek

; odczytaj DWORD, ktory okresja polozenie overalaya w pliku
        push    0
        mov     eax,esp

        push    4                       ; size
        push    eax                     ; &memory
        push    edi                     ; hFile
        call    [402040] ;_lread

        pop     eax                     ; esi - raw offset overlaya

        push    0                       ; FILE_BEGIN
        push    0                       ; offset
        push    edi                     ; hFile
        call    [40203C] ;_llseek

; alokuj pamiec na calego overlaya
        push    ebx
        push    40                      ; GMEM_FIXED or GMEM_ZEROINIT
        call    GlobalAlloc
        mov     esi,eax

; odczytaj calego overlaya do nowo zaalokowanej pamieci
        push    ebx                     ; size
        push    esi                     ; &memory
        push    edi                     ; hFile
        call    [402040] ;_lread

; zamknij oryginalny plik
        push    edi
        call    [402038] ;_lclose

; utworz nowy plik
        sub     esp,512
        mov     eax,esp

; "C:\x"
        mov     edx,785C3A43
        mov     [eax],edx
        xor     ecx,ecx
        mov     [eax+4],ecx

; esi - ptr
; ebx - size

        push    0
        call    ExitProcess

Po wykonaniu skryptu, w pliku C:\x otrzymamy zrzut całego pliku (nie tylko overlaya).

Odbudowa oryginalnego pliku wykonywalnego

Posiadając zrzucony overlay (rozpoznamy jego bajty po sygnaturze FE F2 EE), należy go dokleić do odbudowanego loadera VFP. Sam loader po zabezpieczeniu i zrzuceniu wymaga odbudowy importów,  tutaj można trochę oszukać, wszystkie loadery VFP (danej wersji) są takie same, różnią się jedynie wersją językową, ikoną i zasobami z nazwą i numerem wersji aplikacji. Jeśli posiadacie środowisko Visual FoxPro 9, można stworzyć projekt w stylu Hello World, skompilować do pliku wykonywalnego, usunąć oryginalny overlay i wkleić nasz, a aplikacja będzie działała poprawnie (można potem w edytorze zasobów podmienić ikonę i wersję aplikacji).

Wnioski

Było trochę problemów z samą zabezpieczoną aplikacją i jej działaniem w OllyDbg z włączonym pluginem Phant0m (dużo restartów, nie dało się normalnie zamknąć aplikacji), jednak sama metoda jest dosyć szybka i uniwersalna, z późniejszych poszukiwań udało mi się jeszcze znaleźć doskonały artykuł prezentujący szereg innych zabezpieczeń stosowanych w aplikacjach VFP, polecam na koniec:

Security in FoxPro — www.foxpert.com/docs/security.en.htm

PS. Pozdro dla wszystkich burtoniarzy 🙂

Pobieranie adresu EIP

Pobieranie adresu EIP to jedna z ważniejszych czynności w kodzie wirusów, exe-pakerów, exploitów etc., poniższy artykuł prezentuje standardowe metody jak delta offset oraz te mniej znane na uzyskanie własnego adresu EIP.

http://corkami.blogspot.com/2010/03/si-cest-ton-corps-qui-bouge-cest-ton.html

Jeśli ktoś zna inne metody — dopiszcie się do komentarzy.

Crackme .NET – Rozwiązania

Jakiś czas temu napisałem o konkursie na crackme dla .net zorganizowanym przez Krakowska Grupa Developerów .NET, dzisiaj z ciekawości tam zajrzałem i zobaczyłem, że jest tylko 1 rozwiązanie, więc postanowiłem zadziałać.

1. NemCrackMe # 1 by Pawlos

Standardowy schemat name/serial:

NemCrackMe # 1 by Pawlos

Szybka wrzutka do Reflectora i dekompilacja kodu po kliknięciu „Please Crack Me”:

cm1_check_serial

Mówiąc w skrócie, kod na powyższym obrazku, wywołuje jakąś magiczną funkcję (która jest dynamicznie tworzona) z 2 parametrami, którymi są teksty z kontrolek edycyjnych:

private void button1_Click(object sender, EventArgs e)
{
    Type type = this.tBuilder.CreateType();
    type.GetMethod("o").Invoke(Activator.CreateInstance(type), new object[] { this.textBox1.Text, this.textBox2.Text });
}

Jak nietrudno zgadnąć jest to właściwa metoda sprawdzająca poprawność hasła. Ok, tylko gdzie ona jest hmm? Bez większego doświadczenia w programowaniu w C#, po prostu kliknąłem w inne metody crackme, w tym w konstruktor formy i wszystko stało się jasne:

cm1_dynamic_code

Crackme generuje funkcję sprawdzającą poprawność wpisanego hasła w konstruktorze formy, funkcja generowana jest dynamicznie w pamięci wykorzystując składnię języka IL.

Składnia języka IL może nie jest jakaś szczególnie zawiła, ale zrozumienie tego kodu bez jakiegokolwiek doświadczenia może być trudne, idealnie byłoby gdybyśmy mogli skorzystać z Reflectora do zdekompilowania kodu.

Aby to zrobić, utworzyłem pomocniczy program, który tworzy assembly .net-we z powyższą procedurą i zapisuje je do pliku DLL, dzięki czemu można go wgrać do Reflectora i pokaże nam wszystko ładnie na tacy. Po dekompilacji otrzymałem taki algorytm:

public class MyDynamicType
{
    // Fields
    private int[] coes;
    private int num1;
    private int num2;

    // Methods
    public MyDynamicType();
    public void o(string, string);

// konstruktor
public MyDynamicType()
{
    this.num1 = 0x654321;
    this.num2 = 0x123456;
    this.coes = new int[] { 0x21, 12, 0x5d3, 110, 5, 0x610, 8 };
}

public void o(string text1, string text2)
{
    long num = 0x73L;
    int num2 = 1;
    foreach (char ch in text1)
    {
        num += Math.Abs((int) (ch * this.coes[num2++ % this.coes.Length]));
    }
    num += this.num1;
    num -= this.num2;
    if (text2 == num.ToString())
    {
        MessageBox.Show("Udało się", "NemCrackMe");
    }
}

}

Name: bartosz Password: 5779531

No i to by było na tyle. Użyte zabezpieczenie raczej nie jest dobrym przykładem, gdyż nawet bez większego doświadczenia można zrozumieć zasadę jego działania, myślę, że dodatkowym utrudnieniem byłaby obfuskacja instrukcji sprawdzających numer seryjny, choćby seria zmieniająca codeflow (swoją drogą to niezła baza do takich eksperymentów).

Generator kluczy oraz pomocniczy program tworzący assembly dostępny jest na końcu artykułu wraz z innymi rozwiązaniami.

Dzisiaj przeglądałem kolekcję wtyczek do Reflectora i jest tam plugin o nazwie ReflectionEmitLanguage, który z kodu IL generuje kod w HLL generujący dynamiczny kod IL wskazanej metody i chyba (głowy nie dam) właśnie to zostało użyte do wygenerowania funkcji sprawdzającej hasło w tym crackme.

2. PIJA CrackMe 1

Chyba najciekawsze crackme i zarazem stosujące dobre zabezpieczenia. Ale od początku:

PIJA CrackMe 1

Crackme wymaga wpisania numeru seryjnego i kodu aktywacyjnego, jednocześnie wyświetla kod identyfikacyjny (jak nietrudno zgadnąć, będzie to mieć coś wspólnego z identyfikatorem naszego sprzętu).

Szybka dekompilacja pod Reflectorem metody na naciśniecie buttona sprawdzającego:

private void enter_Click(object sender, EventArgs e)
{
    try
    {
        byte[] buffer3;
        string text = this.txtSerial.Text;

        // generuj klucz deszyfrujacy
        byte[] key = Utils.GetKey(this.regKey, text);

        // generuj wektor inicjujacy
        byte[] iV = Utils.GetIV();

        Rijndael rijndael = Rijndael.Create();
        rijndael.Padding = PaddingMode.Zeros;

        // otworz plik crackmelib.dll
        using (FileStream stream = File.Open("crackmelib.dll", FileMode.Open))
        {
            buffer3 = new byte[stream.Length];
            new CryptoStream(stream, rijndael.CreateDecryptor(key, iV), CryptoStreamMode.Read).Read(buffer3, 0, (int) stream.Length);
        }

        // wywolaj funkcje A() z odszyfrowanej (w pamieci) biblioteki crackmelib.dll
        ((IA) Assembly.Load(buffer3).CreateInstance("crackmelib.B")).A(this.txtActivationCode.Text);
    }
    catch
    {
        MessageBox.Show("Uuuups! Try again");
    }
}

Dla rozjaśnienia kolejne kroki:

  1. Generowany jest klucz deszyfrujący AES na podstawie „Activation request” i „Serial”
  2. Generowany jest wektor inicjujący dla AES
  3. Plik „crackmelib.dll” zostaje odszyfrowany w pamięci według ustawionych parametrów
  4. Odszyfrowana biblioteka „crackmelib.dll” zostaje załadowana do pamięci
  5. Z biblioteki „crackmelib.dll” zostaje wywołana funkcja A() z parametrem jakim jest kod kod aktywacyjny

Użycie wychwytywania wyjątków z użyciem try{} catch{} sugeruje, że albo klucz deszyfrujący będzie poprawny i nastąpi poprawne odszyfrowanie i załadowanie assembly z pliku „crackmelib.dll”, albo nastąpi wyjątek.

Activation request

Wyświetlany po uruchomieniu crackme identyfikator generowany jest w konstruktorze głównej formy crackme:

cm2_request_code

Za jego generację odpowiada biblioteka „CommonInterface.dll”:

public static string GetRequestCode()
{
    return (GetProcessorID() + GetBIOSSerialNumber());
}

private static string GetProcessorID()
{
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_Processor");
    foreach (ManagementObject obj2 in searcher.Get())
    {
        foreach (PropertyData data in obj2.Properties)
        {
            if (data.Name.ToUpper() == "PROCESSORID")
            {
                return data.Value.ToString();
            }
        }
    }
    return null;
}

private static string GetBIOSSerialNumber()
{
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_BIOS");
    foreach (ManagementObject obj2 in searcher.Get())
    {
        foreach (PropertyData data in obj2.Properties)
        {
            if (data.Name.ToUpper() == "SERIALNUMBER")
            {
                return data.Value.ToString();
            }
        }
    }
    return null;
}

Odczytywanie danych o procesorze i danych BIOS odbywa się poprzez klasy interfejsu WMI (przykłady użycia na CodeProject).

Wektor inicjujący AES

Wektor generowany jest w kodzie biblioteki „CommonInterface.dll”:

public static byte[] GetIV()
{
    foreach (object obj2 in Assembly.GetExecutingAssembly().Evidence)
    {
        if (obj2 is Hash)
        {
            return ((Hash) obj2).MD5;
        }
    }
    return new byte[0];
}

Kilka słów wyjaśnienia, metoda GetExecutingAssembly() zwraca assembly, w którym znajduje się aktualnie wykonywany kod, czyli w tym przypadku będzie to assembly „CommonInterface.dll”.

Następnie enumerowane są wszystkie elementy assembly określone jako Evidence, które są obiektem typu Hash. Może to troche skomplikowane, bo dokumentacja też tego jakoś prosto nie wyjaśnia, ale debugging wykazał, że obliczana jest po prostu suma MD5 z pliku „CommonInterface.dll”.

Czyli wektor inicjujący stanowi sumę kontrolną pliku crackme, jest to zapewne zabezpieczenie przed patchowaniem pliku, bo jakakolwiek zmiana, spowoduje wygenerowanie błędnego wektora inicjującego.

Klucz deszyfrujący AES

Klucz deszyfrujący generowany jest w funkcji GetKey() znajdującej się w tej samej bibliotece „CommonInterface.dll”:

public static byte[] GetKey(string reques, string serial)
{
    byte[] buffer = new byte[] {
        0, 5, 7, 0x59, 0x38, 0x22, 0x4e, 0x22, 0x38, 0x4e, 0x17, 0x2d, 9, 12, 0x38, 0x4e,
        0x22, 0x57, 90, 0x6f, 0x17, 0x4e, 0x62, 0x17, 0x38, 12, 0x5f, 0x2c, 0x43, 0x43, 12, 0x17
     };

    // pobierz 1 czesc identyfikatora sprzetowego (o dlugosci polowy buffer)
    byte[] bytes = Encoding.ASCII.GetBytes(reques.Substring(0, buffer.Length / 2));

    // numer seryjny z base64 zamien na bajty
    byte[] buffer3 = Convert.FromBase64String(serial);

    // klucz deszyfrujacy = pierwsza_czesc_hardware_id[] + numer_seryjny[]
    for (int i = 0; i < (buffer.Length / 2); i++)
    {
        buffer[i] = (byte) (bytes[i] + buffer3[i]);
    }

    return buffer;
}

O sile tego crackme świadczy właśnie ta powyższa procka, otóż cała reszta kodu znajdująca się w crackmelib.dll jest zaszyfrowana algorytmem AES z kluczem o długości 32 bajtów (256 bitów), klucz deszyfrujący tworzony jest z pary identyfikatora sprzętowego i numeru seryjnego (+reszta tablicy buffer) i musi być poprawny, inaczej odszfrowany kod będzie nieprawidłowy i nie będzie można załadować tego assembly.

Krótko mówiąć bez znajomości poprawnej pary identyfikator + serial nie można odszyfrować reszy kodu potrzebnego do pracy. Można próbować brute forcem znaleźć poprawną kombinację pierwszych 16 bajtów tablicy buffer (128 bitów), jednak jak przypuszczam taka próba zajęłaby więcej czasu niż czekanie przeze mnie na prezenty od świętego mikołaja za dobre zachowanie 😉

Schemat ten przypomina stosowane w exe-protektorach makra szyfrujące kod według danych licencyjnych, ma to swoje zalety i wady, ale z pewnością można powiedzieć, że to dobre zabezpieczenie z punktu widzenia łamiącego.

Po tej szybkiej analizie miałem już sobie odpuścić, jednak z ciekawości zajrzałem jeszcze raz na stronę konkursową i mój wzrok przykuło rozwiązanie własnie do tego crackme i temat na konkursowym forum, gdzie było zamieszczone ogłoszenie o umieszczeniu crackme oraz HA! autor podaje poprawną serię kodu identyfikatora sprzętowego, numeru seryjnego oraz kodu aktywacyjnego. No to teraz z górki :).

Mając te dane można stworzyć dekoder (w samym crackme nie można wpisać tego podanego identyfikatora sprzętowego), który wykorzystując podane parametry odszyfruje plik crackmelib.dll:

        public static void DecodeAssembly()
        {
            Assembly asmCrackme;
            string asmFile = "CommonInterface.dll";

            // sciezka, gdzie znajduje sie crackme
            string asmPath = Path.Combine(Directory.GetCurrentDirectory(), asmFile);

            Console.WriteLine("[i] Loading {0} assembly.", asmFile);

            // zaladuj assembly z pliku
            asmCrackme = Assembly.LoadFile(asmPath);

            if (asmCrackme == null)
            {
                Console.WriteLine("[!] Cannot load assembly!");
                return;
            }

            // dekoduj tak jak to robi crackme
            try
            {
                // zestaw poprawnych seriali podanych przez autora na forum konkursowym:
                // http://ms-groups.pl/kgd.net/Konkurs_CrackMe/Lists/Forum%20konkursowe/Flat.aspx?RootFolder=%2Fkgd%2Enet%2FKonkurs%5FCrackMe%2FLists%2FForum%20konkursowe%2FPIJA%20CrackMe%201&FolderCTID=0x0120020070660992493BC44DA19C94BED291A957
                // (inaczej pozostaje brute force 128 bitowej wartosci)
                string validRequest = "BFEBFBFF00010676BPSHY3J";
                string validSerial = "9gfHIoPHxMVLJzPk+QKHeg==";
                string validActivation = "+1XY8HQBb8KmSaMS/X8RUDafoZOIhHZb9bpgiYwW78Cpnz1j9WfdwpQ+5Yq8RU9l";

                byte[] buffer3;
                byte[] key = GetKey(validRequest, validSerial);
                byte[] iV = GetIV(asmCrackme);

                Console.WriteLine("[i] Decryption key:\r\n\r\n{0}", HexDump(key));
                Console.WriteLine("[i] Initialization vector:\r\n\r\n{0}", HexDump(iV));

                Rijndael rijndael = Rijndael.Create();
                rijndael.Padding = PaddingMode.Zeros;

                using (FileStream stream = File.Open("crackmelib.dll", FileMode.Open))
                {
                    buffer3 = new byte[stream.Length];
                    new CryptoStream(stream, rijndael.CreateDecryptor(key, iV), CryptoStreamMode.Read).Read(buffer3, 0, (int)stream.Length);
                }

                // zapisz zdekodowany plik
                File.WriteAllBytes("crackmelib.decoded.dll", buffer3);

                Console.WriteLine("[i] Assembly decoded successfully.");
            }
            catch
            {
                Console.WriteLine("[!] Cannot decode file (invalid keys?)!");
                return;
            }
        }

Po odpaleniu tego kodu, otrzymamy ładne assembly z ostatnim fragmentem crackme.

Analiza crackmelib.dll

Pamiętacie, po odszyfrowaniu tej biblioteki, wywoływana była metoda A() z parametrem, jakim był kod aktywacyjny:

cm2_crackmelib

Metoda na początku porównuje hash pliku „PIJA_Crackme.exe” z zakodowaną wartością i jeśli się nie pokrywają, kasowany jest plik crackmelib.dll, a następnie tworzona jest jego kopia z losową zawartością:

private void MessFile()
{
    int count = 0x1800;
    File.Delete("crackmelib.dll");
    FileStream stream = File.Create("crackmelib.dll");
    byte[] buffer = new byte[count];
    Random random = new Random();
    for (int i = 0; i < count; i++)
    {
        buffer[i] = (byte) random.Next(0xff);
    }
    stream.Write(buffer, 0, count);
    stream.Flush();
    stream.Close();
}

Jeśli ktoś zpatchuje główny plik crackme, nadpisana zostanie zaszyfrowana biblioteka sprawdzająca poprawność kodu aktywacyjnego i wszelkie następne próby rejestracji skończą się niepowodzeniem.

Keygen i pomocnicza aplikacja dekodująca znajduje się na końcu artykułu, jeszcze dla jasności:

cm2_cracked

Było to najlepsze crackme ze wszystkich, a użyte zabezpieczenia znakomicie sprawdzają się w warunkach bojowych,  wzajemnie sprawdzane sumy kontrolne są doskonałą metodą na ochrone przed patchowaniem (oczywiście bardziej ukryte), a zastosowany algorytm szyfrujący sprawia, że złamanie tak zabezpieczonej aplikacji bez poprawnego klucza jest praktycznie niemożliwe.

Na koniec jeszcze małe TheDailyWTF, które wyświetliło się podczas debugowania crackme pluginem Deblector (który i tak się do niczego raczej nie przydał):

cm2_zombie

Zombies ahead?

3. MKS CrackMe 1

Bardzo proste crackme, dodatkowo opublikowane razem z rozwiązaniem (WTF?) .

MKS CrackMe 1

W zasadzie, jedynym problemem jest to, że domyślnie wyłączony jest button do sprawdzania hasła:

private void Form1_Load(object sender, EventArgs e)
{
    this.button1.Enabled = false;
}

W rozwiązaniu zaproponowanym przez autora crackme (hehe nie moge ;)) użyty jest plugin Reflectora o nazwie Reflexil do spatchowania tego kodu, my jednak będziemy oryginalni i użyjemy IDA do znalezienia kodu wyłączającego button:

cm3_patch

Jak widać na obrazku powyżej, mamy tam instrukcje „ldc.i4.0” (flaga FALSE), która sprawia, że button jest wyłączany, trzeba tam wrzucić instrukcję „ldc.i4.1”, problemem może być znalezienie jej hexadecymalnego odpowiednika, ale wystarczy przewinąć deadlisting w IDA (albo zajrzeć na stronę MSDN z listą opcodów albo tutaj), żeby znaleźć jakiś inny kod, w którym ją znajdziemy, potem tylko szybki patch i button do sprawdzania hasła będzie włączony.

Algorytm sprawdzania hasła jest banalnie prosty:

private void button1_Click(object sender, EventArgs e)
{
    if ((this.textBox1.Text.Length > 0) && (Convert.ToBase64String(Encoding.Default.GetBytes(this.textBox1.Text)) == this.textBox2.Text))
    {
        MessageBox.Show("You are great", "Registration");
    }
    else
    {
        MessageBox.Show("You failed. Try again.", "Registration");
    }
}

Name: bartosz Password: YmFydG9zeg==

Crackme może nie jest oryginalne, ale naprowadziło mnie na plugin Reflexil, który przyda się w przyszłości (pozwala na manipulację .net-owych assemblies i zapisywanie zmian do plików), tak więc zawsze jakis plus 🙂

4. SimpleCrackMe

Chyba najprostsze crackme z całej listy (w wersji widnieje nazwa firmy Comarch).

SimpleCrackMe

Do złamania wystarczy zmusić crackme do włączenia buttona. Jak to zrobić? Posłużymy się opisanym wyżej pluginem Reflexil. Kod wyłączający buttona zwykle znajduje się w procedurach ładowania okna lub inicjowania komponentów.

Po jego znalezieniu, z menu Tools, wybieramy Reflexil i w prawym panelu, na dole, pojawia się okno Reflexila z kodem IL bieżącej procedury:

cm4_patch

Mamy podobną sytuację jak w przypadku poprzedniego crackme. Należy zmodyfikować flagę FALSE, która wykorzystana jest do wyłączenia buttona. Aby to zrobić, klikamy prawym przyciskiem myszki na zaznaczony opcode i wybieramy EDIT, wyświetli się nam okno z opcją podmiany instrukcji:

cm4_edit_op

Na liście combo znajdują się wszystkie dostępne instrukcje, wybieramy więc „ldc.i4.1” (dokładnie tak jak w poprzednim crackme). Teraz, aby zapisać zmiany, należy kliknąć w drzewku Reflectora ikonkę modułu SimpleCrackme.exe i w oknie Reflexila pojawi się opcja zapisu:

cm4_save

Tutaj wystąpuje jeden problem. Otóż crackme jest podpisane cyfrowo (tzw. StrongNamed Assemblies) i zapisanie zmodyfikowanej wersji crackme spowoduje jego zawieszenie podczas uruchamiania.

Przy próbie zapisu zmodyfikowanego crackme, Reflexil pozwala wybrać co z tym fantem zrobić:

cm4_signed

Z dostępnych opcji należy wybrać opcję usunięcia sygnatury strong name a następnie wybrać „Cancel and keep it delay signed”, albo jak komuś się chce, to można się pobawić w ponowne podpisywanie, własnym kluczem. Nie jestem do końca obeznany z tematem podpisywania assemblies, więc jeśli popełniłem jakiś błąd, skorygujcie mnie.

W każdym razie, po tych zmianach crackme się ładnie uruchamia i można klikać w button:

cm4_cracked

Finał

Na tle wszystkich crackme, wyróżniło się crackme PIJA CrackMe 1. Wniosek jaki mi się jednak nasuwa jest taki, że jakiekolwiek zabezpieczenia .net nie mają sensu bez zastosowania obfuscatorów (i to dobrych).

Keygeny

Paczka z kodami dodatkowych narzędzi i generatorów kluczy:

konkurs_dotnet_crackme.zip (4.51 MB)

Top 10 narzędzi do reversingu

Moja lista ulubionych i najczęściej wykorzystywanych narzędzi w reverse engineeringu.

1. HIEW

Bezapelacyjnie pierwsze miejsce. Narzędzie (w tym wypadku nie można mówić o nim jak o zwykłym hexedytorze), z którego najczęściej korzystam do edycji plików, kodu, do szybkiego odnajdywania tego co potrzeba.

Mimo konsolowego GUI jest to prawdziwy kombajn, bez którego nie wyobrażam sobie pracy w reversingu.

HIEW

http://www.hiew.ru/

2. OllyDbg

Kiedyś wpisałbym tutaj SoftICE, jednak jego lata panowania już minęły. OllyDbg mimo pewnych niedogodności względem SoftICE, godnie go zastąpił, głównie ze względu na system wtyczek, które bez wątpienia stanowią dzisiaj o sile tego debuggera (np. ODBScript, dzięki któremu można robić cuda i dzięki któremu na nowo odnalazłem radochę z pracy).

Również duża liczba zmodyfikowanych wersji OllyDbg świadczy o ogromnej rzeszy fanów tego debuggera.

OllyDbg

http://www.ollydbg.de/

3. IDA

Najlepszy deasembler na świecie, bez niego nie ma mowy o analizie plików binarnych ani prowadzenia projektu, który wymaga kilku miesięcy pracy przy modyfikacji jakiegoś oprogramowania.

Posiada wbudowany system wtyczek, język skryptowy, rozpoznawanie znanych funkcji z całej rzeszy kompilatorów i ich bibliotek oraz masę zaawansowanych funkcji ułatwiających analizę statyczną oraz dynamiczną.

IDA Pro

https://www.hex-rays.com/idapro/

4. FileMon i RegMon

Ex aequo na tym samym miejscu. Narzędzia, dzięki którym zaoszczedziłem kupę pracy przy śledzeniu działania aplikacji. Wystarczy odpalić, ustawić filtry i mamy wszystko co potrzeba, potem wystarczy znaleźć odpowiedni ciąg znaków w aplikacji, xref (you know the drill) itd.

Przy okazji nie wiem jak wy, ale do mnie zupełnie nie przemówiło zintegrowane narzędzie ProcMon.

RegMon

Linki — FileMon RegMon ProcMon

5. Hex Workshop

To czego nie ma HIEW ma Hex Workshop, główną zaletą Hex Workshop jest praca na wielu plikach, co znacznie ułatwia kopiowanie danych z jednego pliku do drugiego, porównywanie binarne plików.

Dodatkowe funkcje umilające życie to m.in. wbudowane hashowanie, przeglądanie zdefiniowanych struktur danych. Bardziej rozbudowanym hexedytorem, który mógłby zastąpić Hex Workshop w mojej bazie narzędzi jest WinHex, jednak zawsze był dla mnie zbyt toporny, aby stał się narzędziem, które klikam w pierwszej kolejności.

Hex Workshop

http://www.hexworkshop.com/

6. LordPE

Process viewer i dumper, który mimo już swojego zaawansowanego wieku, nadal jest przydatnym narzędziem, które umożliwia szybki zrzut pamięci wybranego procesu (oraz jego pamięci wirtualnej), odbudowę uszkodzonych struktur plików PE czy też szybką edycję flag sekcji exeków.

LordPE

LordPE

7. Universal Extractor i MultiExtractor

Czasami zdarza się, że trzeba rozpakować pliki instalatora, albo z binarki wyciągnąć wszystkie jego zasoby, te 2 narzędzia pozwalają sprawnie i szybko rozpakować wszystkie rodzaje instalatorów stosowane w dystrybucji oprogramowania oraz wyciągnąć chyba wszystkie multimedialne formaty plików ukryte w innych plikach binarnych.

Universal Extractor

Universal Extractor — http://legroom.net/software/uniextract

MultiExtractor v2.80a

MultiExtractor — http://www.multiextractor.com

8. .NET Reflector i JAD

Rozwój techniki sprawił, że nie da się już przejść obojętnie obok aplikacji .NET oraz tych w Javie, dlatego te 2 narzędzia prędzej czy później wylądują u każdego, kto zajmuje się reversingiem.

.NET Reflector

.NET Reflector — http://www.red-gate.com/products/reflector/
JAD — http://www.varaneckas.com/jad

9. eXeScope

Prosty i szybki viewer zasobów plików wykonywalnych, mimo, że są już zdecydowanie lepsze narzędzia (jak np. te wbudowane w Visual Studio), eXeScope przydaje się gdy trzeba szybko znaleźć coś w zasobach.

eXeScope

eXeScope

10. ImpREC

Narzędzie, bez którego nie obejdzie się żaden unpacking, w pewnym momencie i tak się przyda 🙂

ImpREC

ImpREC

Gdzie szukać innych narzędzi?

Jeśli szukasz narzędzi, odwiedź te strony:

Wykop wpis Zagłosuj na ten wpis na Wykopie

j00ru//vx tech blog

infoNie wiem w sumie czemu, ale jakiś czas temu j00ru otworzył swojego bloga i mimo, że jako pierwszy wpisałem się do komentarzy (sorry Gyn :P) zapomniałem jakoś o nim wspomnieć tutaj na Security News.

Blog jest po angielsku, ale to raczej nie powinno być dla nikogo problemem, artykułów jest na razie mało, ale za to są bardzo techniczne i myślę, że warto zajrzeć i zachęcić autora do kolejnych wpisów. Obecnie dostępne artykuły:

Strona bloga — https://j00ru.wordpress.com/