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
.
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.
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!
Nareszcie jakaś większa aktywność na blogu, dobrze,że wróciłeś,Patryku 🙂
PS. Będzie kontynuacja serii „od zera do hakera”?
Też się Cieszę, że powróciłeś. Zapowiada się Ciekawy okres :D, mam nadzieję że to nie jest krótkotrwałe :D.