Hoe te Maken van een antivirus-engine

Verzenden

Gebruiker Rating4.54(13 stemmen)

Inleiding

Bij roaming rond de techneuten forums, dan zie ik vaak dat sommige mensen (en veel niet erg ervaren) vragen voor: “Hoe maak ik een antivirus”, soms met niet zeer geschikt talen (bat, PHP, …) en het hebben van een verkeerd idee van wat een antivirus is, en hoe het gebouwd moet worden.

ik heb ook veel “Antivirus software” gemaakt door kiddies, met zeer weinig nog-op-school mensen en ongeveer 4 uur per dag van het coderen op enkele weken. Ik ben het niet vertellen kiddies zijn niet bekwaam, maar ik vertel het bouwen van een antivirus engine moet ofwel veel ervaren mensen met fulltime baan plus veel tijd om een fatsoenlijke software of veel geld vrij te geven om hen te betalen 🙂 (in het geval dat ze niet vrijwilliger).

dus, Ik zal hier de richtlijnen voor een basis antivirus codering, voor Windows en in C/C++behandelen. Men kan hier vinden de aanwijzingen voor het ontwerpen van een antivirus engine, of gewoon leren hoe de meeste van hen zijn gebouwd.

bescherming

voor een goede bescherming moet een Antivirus ten minste één driver hebben om code in de kernel te kunnen uitvoeren en over het algemeen toegang hebben tot kernel API ‘ s. Beginnend met Vista begreep Microsoft dat de Antivirus-industrie sleutels nodig had om de kernel in te voeren en filters te activeren op strategische plaatsen, zoals bestandssysteem, register en netwerk. Wees niet verbaasd als het bouwen van een antivirus voor pre-Vista-systemen kan een echte pijn, omdat het niet is ontworpen voor deze.

  • echter, op Pre-Vista-systemen gebruikten antivirusbedrijven rootkit-achtige functies om de deuren te bewaken (zelfs als het helemaal niet werd aanbevolen door Microsoft) en om uw systeem te beschermen. Ze gebruikten wat we noemen “haken” (API omleidingen voor filtering doel).
  • op Vista+ leverde Microsoft API ’s om onze low level driver tussen userland calls en kernel API’ s in te voegen. Op die manier is het eenvoudig om een antivirus product in de kernel te registreren. Meer, dat soort registratie gebaseerd systeem stelt ons in staat om onze systeembeveiliging te verzenden in lagen, waar verschillende producten met verschillende doelen kunnen samenleven. Dit was niet het geval voor haken, omdat de uitvoering volledig productafhankelijk was.

noot: Ik zal niet betrekking hebben op de workarounds met hooks voor pre-Vista systemen, want het is gemakkelijk te vinden op het internet, en omdat het zou moeten een heel hoofdstuk uit te leggen hoe haak, waar haak en zo… maar je moet weten dat het hetzelfde idee dan de kernel API ‘ s, behalve dat je jezelf te implementeren wat Microsoft verstrekt op Vista+ systemen.

om meer te weten te komen over het coderen van stuurprogramma ‘ s, kunt u controleren dat handige links:
http://msdn.microsoft.com/en-us/library/windows/hardware/gg490655.aspx
http://www.codeproject.com/Articles/9504/Driver-Development-Part-1-Introduction-to-Drivers

om meer te weten te komen over hooks, kun je dat basisvoorbeeld controleren:
http://www.unknowncheats.me/forum/c-and-c/59147-writing-drivers-perform-kernel-level-ssdt-hooking.html

proces

het eerste waar de gebruiker tegen beschermd moet worden, is het opstarten van kwaadaardige processen. Dit is de basis. Antivirus moet een Pssetcreateprocess Notifyroutineex callback registreren. Door dit te doen, op elk proces creatie, en voordat de belangrijkste thread begint te lopen (en veroorzaken kwaadaardige dingen) de antivirus callback wordt gemeld en ontvangt alle nodige informatie.

het ontvangt de procesnaam, het bestand object, de PID, enzovoort. Aangezien het proces in behandeling is, kan de bestuurder zijn service vertellen om het geheugen van het proces te analyseren voor iets kwaadaardig. Het Sticht iets, de bestuurder zal eenvoudig CreationStatus op FALSE instellen en terugkeren.

NTSTATUS PsSetCreateProcessNotifyRoutineEx( _In_ PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, _In_ BOOLEAN Remove);
VOID CreateProcessNotifyEx( _Inout_ PEPROCESS Process, _In_ HANDLE ProcessId, _In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo);
typedef struct _PS_CREATE_NOTIFY_INFO { SIZE_T Size; union { ULONG Flags; struct { ULONG FileOpenNameAvailable :1; ULONG Reserved :31; }; }; HANDLE ParentProcessId; CLIENT_ID CreatingThreadId; struct _FILE_OBJECT *FileObject; PCUNICODE_STRING ImageFileName; PCUNICODE_STRING CommandLine; NTSTATUS CreationStatus;} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

Threads

in hetzelfde idee als voor processen, threads kunnen een manier zijn voor kwaadwillige dingen om schade te veroorzaken. Bijvoorbeeld, men kan injecteren wat code in een legit proces, en start een externe thread op die code in de context van het proces (gemakkelijk te volgen? 🙂 ). Op die manier, een legit proces kan kwaadaardige dingen doen.

we kunnen nieuwe threads filteren met de pssetcreatethreadnotifyroutine callback. Elke keer dat een thread wordt gemaakt, wordt de antivirus op de hoogte gebracht met de TID en de PID. Dus, het is in staat om te kijken naar de threadrescode start, analyseren en ofwel stoppen met de thread of hervatten.

NTSTATUS PsSetCreateThreadNotifyRoutine( _In_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine);

VOID(*PCREATE_THREAD_NOTIFY_ROUTINE) ( IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create );

afbeeldingen

de derde dynamische bedreiging gaat over afbeeldingen die in het geheugen kunnen worden geladen. Een image is een PE bestand, ofwel een EXE, een DLL of SYS bestand. Om op de hoogte te worden gesteld van geladen afbeeldingen, registreert u gewoon pssetloadimagenotifyroutine. Die callback stelt ons in staat om op de hoogte te worden gebracht wanneer de afbeelding in virtueel geheugen wordt geladen, zelfs als het nooit wordt uitgevoerd. We kunnen dan detecteren wanneer een proces probeert een DLL te laden, een driver te laden of een nieuw proces te starten.

de callback krijgt informatie over het volledige afbeeldingspad (nuttig voor statische analyse), en het belangrijkste naar mijn mening, het basisadres van de afbeelding (voor in-memory analyse). Als de afbeelding kwaadaardig is de antivirus kan kleine trucs gebruiken om de uitvoering te voorkomen, zoals het ontleden van de in-memory afbeelding en ga naar het entrypoint, bel dan de assemblage opcode ” ret ” om het te vernietigen.

NTSTATUS PsSetLoadImageNotifyRoutine( _In_ PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine);
VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE)( __in_opt PUNICODE_STRING FullImageName, __in HANDLE ProcessId, __in PIMAGE_INFO ImageInfo );
typedef struct _IMAGE_INFO { union { ULONG Properties; struct { ULONG ImageAddressingMode : 8; //code addressing mode ULONG SystemModeImage : 1; //system mode image ULONG ImageMappedToAllPids : 1; //mapped in all processes ULONG Reserved : 22; }; }; PVOID ImageBase; ULONG ImageSelector; ULONG ImageSize; ULONG ImageSectionNumber;} IMAGE_INFO, *PIMAGE_INFO;

bestandssysteem

zodra elk dynamisch ding is beveiligd, zou een antivirus de gebruiker on-the-fly op de hoogte moeten kunnen stellen van kwaadaardige dingen, niet alleen wanneer ze op het punt staan te starten. Een antivirus moet in staat zijn om bestanden te scannen wanneer de gebruiker een map opent, een archief, of wanneer het is gedownload op de schijf. Meer, een antivirus moet in staat zijn om zichzelf te beschermen, door het verbieden van een programma om zijn bestanden te verwijderen.

de manier om dit alles te doen, is door een driver in het bestandssysteem te installeren, en meer specifiek een minifilter van een oud filter (oude manier). Hier zullen we het hebben over minifilter.

een minifilter is een specifiek soort driver, die callbacks kan registreren bij elke lees / schrijf operatie die op het bestandssysteem wordt uitgevoerd (IRP belangrijke functies). Een IRP (Interrupt Request Paquet) is een object dat gebruikt wordt om een lees/schrijf operatie op de schijf te beschrijven, die samen met de stuurprogramma stack wordt verzonden. De minifilter zal eenvoudig in die stack worden geplaatst, en die IRP ontvangen om te beslissen wat ermee te doen (allow/deny operatie).

voor een klein voorbeeld van minifilter, Controleer die nuttige link of die. De microsoft richtlijnen zijn hier.

u vindt hier en hier ook 2 voorbeelden van de WDK documentatie.

een basis minifilter callback ziet er zo uit. Er zijn 2 soorten callback, Pre operation en Post operation, die in staat zijn om te filteren voor of na de query. Hier is een preoperation pseudo code:

FLT_PREOP_CALLBACK_STATUS PreOperationCallback (__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext){ ... if ( all_good ) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } else { // Access denied Data->IoStatus.Information = 0; Data->IoStatus.Status = STATUS_ACCESS_DENIED; return FLT_PREOP_COMPLETE; } }

register

het register is een van de meest kritieke plaatsen om te bewaken. Er zijn vele vele manieren voor een malware om aanhoudende hand op het systeem te houden door het registreren van een enkele (of enkele) sleutels/waarden in het register. De meest bekende plaatsen zijn Run keys, en diensten. Dit is ook de plaats waar de antivirus kan worden verslagen (samen met het bestandssysteem), door simpelweg het verwijderen van de driver/service sleutels, zodat het niet meer zal herstarten bij het opstarten van het systeem.

dit is geen echte noodzaak voor een antivirus om herstartplaatsen te bewaken, de meeste niet. maar ze moeten hun install registersleutels bewaken, om te voorkomen dat ze gemakkelijk worden verslagen door malwares. Dit kan door CmRegisterCallback te registreren.

de callback geeft voldoende informatie om de volledige sleutelnaam, het soort toegang (aanmaken, hernoemen, verwijderen, … ) en de beller PID te krijgen. Op die manier is het gemakkelijk om al dan niet toegang te verlenen tot de oproep, door het statusveld van post Operation callback in te stellen.

NTSTATUS CmRegisterCallbackEx(_In_ PEX_CALLBACK_FUNCTION Function,_In_ PCUNICODE_STRING Altitude,_In_ PVOID Driver,_In_opt_ PVOID Context,_Out_ PLARGE_INTEGER Cookie,_Reserved_ PVOID Reserved);
EX_CALLBACK_FUNCTION RegistryCallback;NTSTATUS RegistryCallback( _In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ ... }Argument1 = typedef enum _REG_NOTIFY_CLASS { RegNtDeleteKey, RegNtPreDeleteKey = RegNtDeleteKey,...Argument2 = typedef struct _REG_POST_OPERATION_INFORMATION { PVOID Object; NTSTATUS Status; PVOID PreInformation; NTSTATUS ReturnStatus; PVOID CallContext; PVOID ObjectContext; PVOID Reserved;} REG_POST_OPERATION_INFORMATION, *PREG_POST_OPERATION_INFORMATION;

Netwerk (Firewall)

om de deuren van het hele internetverkeer, dat op bepaalde systemen (servers, enorme bandbreedte gebruikers) enorm kan zijn, te bewaken zonder te worden vertraagd door de context switching die plaatsvindt in userland, is het totaal niet aan te raden om een firewall te installeren die geen onderliggende driver heeft, behalve voor sommige webbrowserfilters die genoeg kunnen zijn voor http-verkeer, maar die niet beschermen tegen malware communicatie in/uit.

om een correcte implementatie van firewall te hebben, moet men een NDIS, TDI of een andere methode voor een low level IP filtering driver coderen. NDIS / TDI is een beetje lastig om te doen, en zou veel kennis vereisen (meer dan andere filters in mijn mening).

hoe dan ook, hier zijn enkele aanwijzingen om te beginnen met het coderen van een dergelijk stuurprogramma, de microsoft richtlijnen, en oude CodeProject tutorial (maar nog steeds goed om te lezen), een voorbeeld van NDIS firewall, en een voorbeeld van TDI firewall. Hier is ook een goed schrijven over NDIS firewall bypass truc, en een kleine uitleg over de netwerk driver stack,

Userland protection

de userland protection is geen noodzaak, maar kan een extra module zijn tegen Trojan Bankers, en meer specifiek tegen process spies. Ze worden over het algemeen geïnjecteerd in elk proces, om verschillende redenen.

Ten eerste zijn ze in staat (on demand) om het proces te stoppen als het is geïdentificeerd als malware (Dit zou niet moeten gebeuren, omdat AVs verondersteld worden om het te stoppen voordat het Start). Het is altijd makkelijker om een proces te stoppen als je in de context bent.

ten tweede zijn ze in staat om kritische processen, zoals webbrowsers, te beschermen tegen het aansluiten van malwares die API-oproepen kunnen omleiden en filteren om wachtwoorden, bankgegevens te verzamelen en internetstroom om te leiden naar malwareservers. Ze kijken alleen naar IAT wijziging, voor splicing, en kan ook haken zelf LoadLibray van een malware DLL te voorkomen, en dus bepaalde methoden van code-injectie te verbieden.

een eenvoudige manier om een protector DLL in alle processen te injecteren is door de appinitdll registersleutel te gebruiken om de protector DLL te registreren. Het zal de DLL laden in elk proces dat op het systeem is gestart, zodra ze de User32 koppelen.dll image (de meeste van hen doen).

Analysis Engine

de analysis engine is een van de belangrijkste onderdelen, Het is verantwoordelijk voor het analyseren van bestanden/geheugen monsters afkomstig van de drivers. Als het moet snel (zelfs met een enorme database), en moet kunnen omgaan met de meeste bestandstypen (Self-gewonnen uitvoerbare bestanden, Archieven – RAR, ZIP, Ingepakte bestanden – zoals UPX, … ) en dus moeten veel modules om dit te doen:

  • Uitpakker : Die module moet in staat zijn op te sporen en pak de meeste van de bekende compressie (zoals UPX, Gordeldier, ASPack, …)
  • Handtekening motor: De database van een antivirus bevat miljoenen handtekeningen, en de motor moet in staat zijn om snel te zoeken voor hen een voorbeeld. Dus, een zeer krachtig algoritme moet deel uitmaken van het. Enkele voorbeelden : AhoCorasick, RabinKarp, string matching algoritmes.
  • Sandbox: deze module is niet nodig, maar zou een pluspunt zijn om samples in een beperkt geheugen te kunnen draaien, zonder effect op het systeem. Dat kan helpen om monsters verpakt met onbekende packers uitpakken, en helpen de heuristische engine (zie na) om API-oproepen die kunnen worden beschouwd als verdacht of kwaadaardig detecteren. Wat een goede zandbak hier.
  • heuristische motor: zoals hierboven gezegd, de heuristische Motor zoekt niet naar handtekeningen, maar zoekt eerder naar verdacht gedrag (dwz. voorbeeld dat een verbinding opent op de website hxxp://malware_besite.com). dat kan worden gedaan door statische analyse, of door middel van een zandbak.

Signature syntaxis

de signature syntaxis is het” woordenboek ” van de taal die de signature engine begrijpt. Het is een manier om te formaliseren Wat is het patroon te vinden, hoe het te zoeken en waar het te zoeken in het monster. De syntaxis moet eenvoudig genoeg zijn voor de onderzoekers om te begrijpen, krachtig genoeg om elke use case te behandelen, en gemakkelijk te ontleden voor betere prestaties van de motor.

VirusTotal heeft een goede syntaxis en engine (Yara project) ontwikkeld, die open source is. Dat zou een goede aanwijzer moeten zijn om je eigen syntaxis te maken, of gewoon te gebruiken. Hier is ook een goede post blog over hoe maak handtekeningen voor antivirus.

voorbeeld van ondertekening:

rule silent_banker : banker{ meta: description = "This is just an example" thread_level = 3 in_the_wild = true strings: $a = {6A 40 68 00 30 00 00 6A 14 8D 91} $b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9} $c = "UVODFRYSIHLNWPEJXQZAKCBGMT" condition: $a or $b or $c}

zelfbescherming

de zelfbescherming is erg belangrijk voor een antivirus, om te voorkomen dat een malware wordt verslagen en om de gebruiker te blijven beschermen. Dit is de reden waarom een antivirus in staat moet zijn om zijn eigen installatie te bewaken en persistentie te houden bij het opnieuw opstarten.

er zijn verschillende plaatsen om te beschermen: bestanden, registersleutels, processen/Threads, geheugen.

  • bestandsbescherming is geà mplementeerd in de minifilter, met specifieke regels voor de bestanden van het antivirus (geen toegang bij verwijderen, hernoemen, verplaatsen, schrijven).
  • registerbeveiliging wordt opgenomen in het registerfilter, waarbij toegang wordt geweigerd voor registersleutels van het stuurprogramma en de service.
  • de drivers threads zijn beveiligd, omdat het onmogelijk is om kernelmodule uit te laden zonder het systeem te crashen
  • om de service te kunnen beschermen, wat een gebruikersproces is, 2 oplossingen:
  1. het gemakkelijkste zou zijn om regels toe te voegen voor storingen in de service manager, en elke foutregel in te stellen op “Herstart service”. Op die manier, wanneer de service niet wordt gestopt door service manager, het herstart. Natuurlijk zou de service geen commando ‘ s moeten kunnen accepteren totdat het systeem niet herstart (of stopt).
491838-428-487-263x300
  1. de tweede methode, die meer generiek is, zou zijn om callbacks op proceshandvatten met ObRegisterCallbacks.

Door het instellen van het ObjectType te PsProcessType en de Werking te OB_OPERATION_HANDLE_CREATE, ontvangt u een Pre-en Post-operatie terugbellen, en je bent in staat om terug te keren ACCESS_DENIED in ReturnStatus als het proces ingang opgevraagd heeft GrantedAccess die proces beëindigen rechten (of proces schrijf rechten, of iets dat kan leiden tot een crash/doden), en natuurlijk, als het proces is een van de antivirus moet guard (de dienst bijvoorbeeld).

natuurlijk moet men ook dubbele handle en het PsThreadType bewaken om elke beëindigingsmethode te vermijden die vereist is om een handle op het proces of een thread te grijpen. Hier is een klein voorbeeld van het gebruik van die callback.

NTSTATUS ObRegisterCallbacks( _In_ POB_CALLBACK_REGISTRATION CallBackRegistration, _Out_ PVOID *RegistrationHandle);
typedef struct _OB_CALLBACK_REGISTRATION { USHORT Version; USHORT OperationRegistrationCount; UNICODE_STRING Altitude; PVOID RegistrationContext; OB_OPERATION_REGISTRATION *OperationRegistration;} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
typedef struct _OB_OPERATION_REGISTRATION { POBJECT_TYPE *ObjectType; OB_OPERATION Operations; POB_PRE_OPERATION_CALLBACK PreOperation; POB_POST_OPERATION_CALLBACK PostOperation;} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;
VOID ObjectPostCallback( _In_ PVOID RegistrationContext, _In_ POB_POST_OPERATION_INFORMATION OperationInformation);
typedef struct _OB_POST_OPERATION_INFORMATION { OB_OPERATION Operation; union { ULONG Flags; struct { ULONG KernelHandle :1; ULONG Reserved :31; }; }; PVOID Object; POBJECT_TYPE ObjectType; PVOID CallContext; NTSTATUS ReturnStatus; POB_POST_OPERATION_PARAMETERS Parameters;} OB_POST_OPERATION_INFORMATION, *POB_POST_OPERATION_INFORMATION;

typedef union _OB_POST_OPERATION_PARAMETERS { OB_POST_CREATE_HANDLE_INFORMATION CreateHandleInformation; OB_POST_DUPLICATE_HANDLE_INFORMATION DuplicateHandleInformation;} OB_POST_OPERATION_PARAMETERS, *POB_POST_OPERATION_PARAMETERS;
typedef struct _OB_POST_CREATE_HANDLE_INFORMATION { ACCESS_MASK GrantedAccess;} OB_POST_CREATE_HANDLE_INFORMATION, *POB_POST_CREATE_HANDLE_INFORMATION;

GUI (Graphical User Interface)

dit is het zichtbare deel van de ijsberg. Naar mijn mening, een (misschien wel de) belangrijkste deel als u wilt uw product te verkopen. Gebruikers houden van wat mooi is, makkelijk te gebruiken, intuïtief. Zelfs als het niet 100% efficiënt is. De GUI moet sexy zijn.

de GUI is slechts een lege shell, het doet alleen grafische behandelingen, en stuurt/ontvangt commando ‘ s naar de kern (de service). Het toont ook voortgangsbalken, wat wordt geanalyseerd, biedt configuratie, en dus … hier is de Avast UI. Sexy, toch? 🙂

avast

architectuur

de Globale architectuur zou er zo uit kunnen zien:

  1. GUI: geen beheerdersrechten, zwak
  2. Guard DLL(s) : bescherming van webbrowser, MEDIUM
  3. Service : beheerdersrechten. Dient als een gateway naar kernel code en neemt beslissingen samen met een database, STRONG
  4. Driver(s) : Kernel filters, STRONG

de GUI heeft geen beheerrecht nodig, het neemt alleen acties van gebruikers en stuurt deze naar de service. Het toont ook de status van het product. Niets meer, Dit is niet het doel. Als de GUI wordt uitgeschakeld, is dit geen probleem omdat de service het opnieuw moet kunnen opstarten.

de guard-DLL ‘ s (indien aanwezig) worden massaal in alle processen geïnjecteerd en moeten in staat zijn om te zoeken naar IAT-hooks en/of kwaadaardige threads. Ze moeten moeilijk te lossen of te verslaan zijn. Ze zijn niet kritisch, maar belangrijk.

de dienst vormt de kern van het product. Het zou onkillbaar moeten zijn, of op zijn minst in staat moeten zijn om zichzelf te herstarten bij kill. De service is verantwoordelijk voor de communicatie tussen alle modules van het product, Het stuurt commando ’s naar stuurprogramma’ s, neemt commando ‘ s van de gebruiker, en vraagt de database voor monsteranalyse. Dit zijn de hersenen.

de kerneldrivers zijn ook kritisch. Zij zijn de tentakels die informatie verzamelen over alles wat er op het systeem gebeurt, en deze doorgeven aan de Dienst voor beslissing. Ze zijn ook in staat om de toegang tot bewaakte plaatsen te weigeren, op basis van de beslissing van de dienst.

conclusie

het maken van een sterke, betrouwbare en stabiele antivirus-engine is een ingewikkelde taak, waarvoor geëxperimenteerde mensen nodig zijn met een zeer sterke kennis van Windows-kernelprogrammering, Windows-toepassingsprogrammering, GUI-ontwerp, softwarearchitectuur, malware-analyse, …

het bouwen van stabiele stuurprogramma ‘ s is ook een ingewikkelde taak, omdat een beetje zandkorrel het hele systeem kan laten crashen. Het moet testen, testen, en veel testen. Als uw motor klaar is en werkt, hoeft u alleen maar onderzoekers in te huren om malware te analyseren en handtekeningen toe te voegen aan uw database 🙂 waar wacht u nog op? Ga door! 😀

koppelingen

  • http://www.symantec.com/connect/articles/building-anti-virus-engine

Leave a Reply

Het e-mailadres wordt niet gepubliceerd.