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.