Zmniejszanie wykrywalności wskaźnikami w C++

Jeżeli interesujesz się zarówno programowaniem w języku C++ jak i bezpieczeństwem informatycznym to prawdopodobnie interesuje Ciebie tematyka zmniejszania wykrywalności złośliwego oprogramowania. W dzisiejszym wpisie przedstawię Tobie jedną z technik wykorzystującą wskaźniki na funkcje Windows API. Dzięki niej w skuteczny i prosty sposób można zmniejszyć wykrywalność aplikacji.

W dużym uproszczeniu polega to na dynamicznym ładowaniu funkcji WinAPI z bibliotek DLL systemu operacyjnego Windows za pomocą wskaźników na funkcje. Przejdźmy do prostych kodów źródłowych z przykładami.

Wskaźniki na funkcje w C++

W języku programowania C++ istnieje możliwość stworzenia wskaźników funkcyjnych, który wskazuje na konkretne funkcje. Poniżej zaimplementowaliśmy przykładową funkcje wyświetlającą po prostu liczbę wprowadzoną w argumencie funkcji mojaFunkcja.

void mojaFunkcja(int liczba) 
{
	printf("%d", liczba);
} 

void (*wskaznikFun)(int);
wskaznikFun = &mojaFunkcja;
wskaznikFun(2);

W powyższym kodzie źródłowym zadeklarowaliśmy funkcje o nazwie mojaFunkcja przyjmującą jako argument liczbę całkowitą. Funkcja nie zwraca żadnej wartości, tylko wyświetla wprowadzoną przez programistę liczbę.

Kluczowym elementem przykładu jest 6 i 7 linijka kodu źródłowego definiująca najpierw wskaźnik na naszą funkcje, a następnie przypisuje ona za pomocą operatora referencji naszą &mojaFunkcja. Jeśli chcesz się więcej dowiedzieć na temat tego typu wskaźników to polecam Tobie zajrzeć do materiałów wykładowych PJWSTK o wskaźnikach funkcyjnych.

Zmniejszamy wykrywalność wskaźnikami funkcyjnymi

Po tym krótkim wstępie, spróbujmy napisać funkcję działającą analogicznie do funkcji CopyFileA z dokumentacji Microsoftu MSDN z wykorzystaniem wskaźników. Poniżej znajduje się trywialny przykład takiej implementacji w języku programowania C++.

#include <windows.h>
using namespace std;

BOOL myCopyFile(LPCTSTR source, LPCTSTR destination)
{
    typedef BOOL(WINAPI * _CF)
    (
        LPCTSTR lpExistingFileName,
        LPCTSTR lpNewFileName,
        BOOL bFailIfExists
    );

    _CF dynamicCopyFunction = (_CF)GetProcAddress(GetModuleHandle("Kernel32.dll"), "CopyFileA");

    return dynamicCopyFunction(source, destination, true);
}

int main()

{
    myCopyFile("C:\\file.txt", "C:\\newFile.txt");
    return 0;
}

Przykład jest bardzo prosty. Utworzyliśmy sobie własną funkcję o nazwie myCopyFile, która zwraca zgodnie z dokumentacją MSDN wartość typu logicznego BOOL. Jako parametr przyjmuje ścieżkę do pliku do skopiowania source i miejsce docelowe destination.

Struktura tej funkcji w dokumentacji Microsoft Developer Network wygląda następująco:

BOOL WINAPI CopyFile(
  _In_ LPCTSTR lpExistingFileName,
  _In_ LPCTSTR lpNewFileName,
  _In_ BOOL    bFailIfExists
);

W 6 linijce naszego kodu źródłowego zdefiniowaliśmy strukturę zgodną z powyższym fragmentem z  dokumentacji MSDN firmy Microsoft. Warto samemu zajrzeć do dokumentacji WinAPI właśnie dotyczącej prezentowanej funkcji CopyFileA.

Cała magia dzieje się w 13 linii kodu źródłowego. Ładujemy tutaj w sposób dynamiczny za pomocą funkcji GetProcAdress i GetModuleHandle funkcje Windows API o nazwie CopyFileA z biblioteki współdzielonej o nazwie Kernel32.dll Oczywiście informacje dotyczącą faktu, że w niej znajduje się ta funkcja zaczerpnęliśmy z MSDN. Dzięki temu zabiegowi, program antywirusowy nie do końca zdaje sobie sprawę z tego, że korzystamy z tej funkcji WinApi.

Takie funkcje wykorzystywane często są przez cyberprzestępców przy tworzeniu złośliwego oprogramowania. Sztandarowym przykładem może być procedura dodająca program do autostartu systemu z wykorzystaniem rejestru Windows. Wykorzystuje się do tego celu najczęściej funkcje pracujące na rejestrze takie jak RegOpenKeyEx.

Wykrywalność funkcji RegOpenKeyEx i FUD
Wykrywalność dodawania do autostartu za pomocą rejestru systemu Microsoft Windows z wykorzystaniem wskaźników (3/57) i bez wskaźników (10/56)

Na samym już końcu w blokumain po prostu korzystamy z naszej nowo utworzonej funkcji myCopyFile. Jak widzisz wykorzystanie w ten sposób funkcji WinAPI jest bardzo proste i polecam Tobie spróbować przerobić kod dowolnej innej funkcji z dokumentacji MSDN Windows API.

Dlaczego to działa?

Dla osób bardziej ciekawskich wyjaśnię w skrócie, dlaczego zmniejszamy w ten sposób wykrywalność. Dzieje się tak ponieważ ładujemy te funkcje już podczas działania naszego programu w sposób dynamiczny. Programy analizujące takie jak antywirusy komputerowe nie widzą w ten sposób używanych przez nas funkcji w specjalistycznej tablicy importu z bibliotek współdzielonych DLL. Dodatkowo moglibyśmy nazwę funkcji CopyFileA i biblioteki Kernel32.dll zaszyfrować dowolnym algorytmem, przykładowo zaprezentowanym kiedyś już prostym algorytmem XOR. O tym na pewno napiszemy wkrótce.

Zmniejszenie wykrywalnośći funkcji WInAPI
Po lewej stronie tablica importu funkcji z biblioteki KERNEL32.DLL dla programu używającego w standardowy sposób funkcji CopyFileA. Po prawej stronie z wykorzystaniem wskaźników funkcyjnych. Oba programy działają identycznie.

Podsumowanie o metodzie wskaźników

Programiści wykorzystują bardzo wyrafinowane techniki zmniejszania wykrywalności oprogramowania typu malware. Dzisiejszy wpis demonstruje możliwość ukrywania pewnych wywołań funkcji WinAPI. Oczywiście istnieją zupełne inne metody w celu osiągnięcia statusu FUD (fully undetectable) i można skorzystać z oprogramowania typu crypter. Bardzo dawno temu prezentowaliśmy na blogu czym są cryptery.

Jeśli podoboa Ci się nasz blog i chcesz być na bieżąco to zapraszam Ciebie na naszego facebooka #HakerEduPL. Lajkując na pewno dowiesz się pierwszy o naszych nowych publikacjach!

2 thoughts to “Zmniejszanie wykrywalności wskaźnikami w C++”

  1. Nareszcie jakaś większa aktywność na blogu, dobrze,że wróciłeś,Patryku 🙂

    PS. Będzie kontynuacja serii „od zera do hakera”?

  2. Też się Cieszę, że powróciłeś. Zapowiada się Ciekawy okres :D, mam nadzieję że to nie jest krótkotrwałe :D.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *