Sådan laver du en antivirusmotor

afsendelse

bruger Vurdering4.54 (13 stemmer)

introduktion

når jeg strejfer rundt i techies-fora, ser jeg ofte nogle mennesker (og mange ikke meget erfarne), der beder om “hvordan laver jeg et antivirus”, nogle gange med ikke meget tilpassede sprog (bat, PHP,…) og har en forkert ide om, hvad et antivirus er, og hvordan det skal bygges.

jeg har også set mange “Antivirus programmel” lavet af kiddies, med meget få stadig-i-skole folk og omkring 4 timer om dagen for kodning på flere uger. Jeg fortæller ikke, at kiddies ikke er dygtige, men jeg fortæller, at opbygning af en antivirusmotor har brug for enten mange dygtige mennesker med fuldtidsjob plus meget tid til at frigive et anstændigt program eller mange penge til at betale dem, hvis de ikke er frivillige).

så jeg dækker her retningslinjerne for en grundlæggende antiviruskodning, til vinduer og i C/C++. Man kan her finde pegerne til at designe en antivirusmotor, eller bare lære, hvordan de fleste af dem er bygget.

beskyttelse

for en god beskyttelse skal en Antivirus have mindst en driver for at kunne køre kode i kernen og generelt have adgang til kernel API ‘ er. Fra Vista forstod Microsoft, at Antivirusbranchen havde brug for nøgler til at komme ind i kernen og aktivere filtre på strategiske steder, såsom filsystem, register og netværk. Vær ikke bedøvet, hvis det kan være en reel smerte at opbygge et antivirus til pre-Vista-systemer, fordi det ikke var designet til dette.

  • på Pre-Vista-systemer brugte Antivirusfirmaer dog rootkit-lignende funktioner til at beskytte dørene (selvom det slet ikke blev anbefalet af Microsoft) og være i stand til at beskytte dit system. De brugte det, vi kalder “kroge” (API omveje til filtrering formål).
  • på Vista+ leverede Microsoft API ‘er til at indsætte vores driver på lavt niveau mellem userland-opkald og kernel-API’ er. På den måde er det nemt at registrere et antivirusprodukt i kernen. Mere, den slags registreringsbaserede system giver os mulighed for at sende vores systemsikkerhed i lag, hvor flere produkter med forskellige mål kan samle. Dette var ikke tilfældet for kroge, da implementeringen var helt produktafhængig.

Bemærk: Jeg vil ikke dække løsningen med kroge til pre-Vista-systemer , fordi det er let at finde på internettet, og fordi det ville have brug for et helt kapitel for at forklare, hvordan man kobler, hvor man skal koble og så… men du skal vide, at det er den samme ide end kernel API ‘ erne, bortset fra at du skal implementere dig selv, hvad Microsoft leverede på Vista+ systems.

for at lære om kodningsdrivere kan du kontrollere, at nyttige 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

for at lære om kroge kan du kontrollere det grundlæggende eksempel:
http://www.unknowncheats.me/forum/c-and-c/59147-writing-drivers-perform-kernel-level-ssdt-hooking.html

Process

den første ting at beskytte brugeren mod, er lanceringen af ondsindede processer. Dette er den grundlæggende ting. Antivirus skal registrere en pssetcreateprocessnotifyroutineeks tilbagekald. Ved at gøre dette, på hver procesoprettelse, og før hovedtråden begynder at køre (og forårsage ondsindede ting), bliver antivirusprogrammet tilbagekaldt og modtager alle de nødvendige oplysninger.

det modtager procesnavnet, filobjektet, PID og så. Da processen afventer, kan føreren fortælle sin tjeneste at analysere processens hukommelse for noget ondsindet. Det det grundlægger noget, føreren vil simpelthen sætte CreationStatus til falsk og vende tilbage.

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;

tråde

i samme ide end for processer kan tråde være en måde for ondsindede ting at forårsage skader. For eksempel kan man injicere noget kode i en legitim proces og starte en fjerntråd på den kode inde i processens kontekst (let at følge? 🙂 ). På den måde kan en legitim proces gøre ondsindede ting.

vi kan filtrere nye tråde med pssetcreatethreadnotifyroutine tilbagekald. Hver gang en tråd oprettes, meddeles antivirusprogrammet med TID og PID. Således er det i stand til at undersøge trådens startadressekode, analysere den og enten stoppe tråden eller genoptage den.

NTSTATUS PsSetCreateThreadNotifyRoutine( _In_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine);
VOID(*PCREATE_THREAD_NOTIFY_ROUTINE) ( IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create );

billeder

den tredje dynamiske trussel handler om billeder, der kan indlæses i hukommelsen. Et billede er en PE-fil, enten en eks, en DLL eller SYS-fil. For at blive underrettet om indlæste billeder skal du blot registrere PsSetLoadImageNotifyRoutine. Denne tilbagekald giver os mulighed for at blive underrettet, når billedet indlæses i virtuel hukommelse, selvom det aldrig udføres. Vi kan derefter registrere, hvornår en proces forsøger at indlæse en DLL, at indlæse en driver eller at affyre en ny proces.

tilbagekaldelsen får oplysninger om den fulde billedsti (nyttig til statisk analyse), og jo vigtigere efter min mening er Billedbaseadressen (til analyse i hukommelsen). Hvis billedet er ondsindet, kan antivirusprogrammet bruge små tricks til at undgå udførelsen, som at analysere billedet i hukommelsen og gå til entrypoint, og ring derefter forsamlingsopkoden “ret” for at annullere det.

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;

filsystem

når hver dynamisk ting er sikret, skal en antivirus være i stand til at underrette brugeren om ondsindede ting on-the-fly, ikke kun når de er ved at starte. En antivirus skal kunne scanne filer, når brugeren åbner en mappe, et arkiv, eller når det er hentet på disken. Desuden bør et antivirusprogram være i stand til at beskytte sig selv ved at forbyde ethvert program at slette sine filer.

måden at gøre alt dette på er at installere en driver i filsystemet og mere specifikt et minifilter af et ældre filter (gammel måde). Her vil vi tale om minifilter.

et minifilter er en bestemt type driver, der er i stand til at registrere tilbagekald på hver læse/skrive-operation, der er foretaget på filsystemet (IRP-hovedfunktioner). En IRP (Interrupt anmodning Paket) er et objekt, der bruges til at beskrive en læse/skrive operation på disken, som overføres sammen med driverstakken. Minifilteret indsættes simpelthen i den stak og modtager den IRP for at beslutte, hvad de skal gøre med den (Tillad/nægt operation).

for et lille eksempel på minifilter, tjek venligst det nyttige link eller det ene. Microsofts retningslinjer er her.

du finder også 2 eksempler på dokumentationen her og her.

et grundlæggende minifilteropkald ser sådan ud. Der er 2 slags tilbagekald, Pre operation og Post operation, som er i stand til at filtrere før efter forespørgslen. Her er en preoperation pseudo kode:

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; } }

Registry

registreringsdatabasen er et af de mest kritiske steder at beskytte. Der er mange måder for en ondsindet program til at holde vedvarende hånd på systemet ved at registrere en enkelt (eller få) nøgler/værdier i registreringsdatabasen. De mest kendte steder er køre nøgler og tjenester. Dette er også det sted, hvor antivirusprogrammet kan besejres (sammen med filsystemet) ved blot at fjerne dets driver/servicetaster, så det ikke længere genstarter ved systemstart.

dette er ikke en reel nødvendighed for et antivirusprogram til at beskytte genstartsteder, de fleste af dem gør det ikke. men de skal beskytte deres installationsregisternøgler for at undgå at blive besejret let af fejl. Dette kan gøres ved at registrere CmRegisterCallback.

tilbagekaldelsen giver nok information til at få det fulde nøglenavn, typen af adgang (Opret, Omdøb, Slet, … ) og opkalderen PID. På den måde er det nemt at give adgang eller ikke til opkaldet ved at indstille statusfeltet for tilbagekald efter operation.

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;

for at beskytte dørene til hele internettrafikken, som kan være enorm på visse systemer (servere, enorme båndbredde brugere) uden at blive bremset af den kontekstskift, der finder sted i userland, anbefales det ikke at installere en brandvæg, der ikke har nogen underliggende driver, bortset fra nogle internetfiltre, der kan være nok til http-trafik, men som ikke beskytter mod ondsindet kommunikation ind/ud.

for at få en korrekt implementering af brandvæg skal man kode en NDIS, TDI eller en anden metode til lavt niveau IP-filtreringsdriver. NDIS / TDI er lidt vanskelig at gøre, og ville kræve meget viden (mere end andre filtre efter min mening).

under alle omstændigheder er her nogle tip til at begynde at kode en sådan driver, microsoft-retningslinjerne og den gamle codeproject-tutorial (men stadig god at læse), et eksempel på NDIS-brandmur og et eksempel på TDI-brandmur. Her er også en god skrivning om NDIS bypass trick, og en lille forklaring om netværksdriverstakken,

userland protection

userland protection er ikke en nødvendighed, men kan være et ekstra modul mod trojanske bankfolk og mere specifikt mod processpioner. De injiceres generelt i hver proces af flere grunde.

for det første er de i stand til (efter behov) at dræbe processen, hvis den er blevet identificeret som ondsindet program (dette bør ikke ske, fordi AVs skal stoppe det, før det starter). Det er altid lettere at stoppe en proces, når du er i dens sammenhæng.

for det andet er de i stand til at beskytte kritisk proces, som f.eks. De holder kun øje med iat-modifikation, til splejsning, og kan også indstille kroge selv for at undgå belastning af en ondsindet DLL, og dermed forbyde visse metoder til kodeinjektion.

en nem måde at injicere en protector DLL i alle processer er at bruge AppInitDll-registreringsnøglen til at registrere protector DLL. Det indlæser DLL ‘ en i hver proces, der startes på systemet, så snart de forbinder User32.DLL billede (de fleste af dem gør).

analysemotor

analysemotoren er en af de vigtigste dele, den er ansvarlig for at analysere fil – /hukommelsesprøver, der kommer fra driverne. Hvis skal være hurtig (selv med en enorm database), og bør være i stand til at håndtere de fleste af de filtyper (Selvudpakkede eksekverbare filer, arkiver-RAR, lynlås, pakkede filer – op, … ) og dermed bør have mange moduler til at gøre dette:

  • Unpacker: dette modul skal være i stand til at opdage og pakke de fleste af de kendte pakker ud (op, Armadillo, ASPack, …)
  • Signaturmotor: databasen med et antivirusprogram indeholder millioner af signaturer, og motoren skal være i stand til hurtigt at søge efter dem i en prøve. Således bør en meget kraftig algoritme være en del af den. Nogle eksempler : Ahocorasick, RabinKarp, streng matchende algoritmer.
  • sandkasse : dette modul er ikke nødvendigt, men ville være et plus for at kunne køre prøver i en begrænset hukommelse uden nogen effekt på systemet. Det kan hjælpe med at pakke prøver pakket med ukendte pakker og hjælpe den heuristiske motor (se efter) med at opdage API-opkald, der kan betragtes som mistænkelige eller ondsindede. En god sandkasse her.
  • heuristisk motor : som sagt ovenfor søger den heuristiske motor ikke efter underskrifter, men ser snarere efter mistænkelig opførsel (dvs. det kan gøres ved statisk analyse eller gennem en sandkasse.

Signatursyntaks

signatursyntaksen er “ordbogen” for det sprog, som signaturmotoren forstår. Det er en måde at formalisere, hvad der er mønsteret at finde, hvordan man søger det, og hvor man skal søge det i prøven. Syntaksen skal være enkel nok til, at forskerne kan forstå, kraftig nok til at håndtere enhver brugssag og let at analysere for bedre motorpræstationer.

VirusTotal har udviklet en god syntaks og motor (Yara project), som er open source. Det burde være en god pointer til at lave din egen syntaks, eller bare bruge den. Her er også en god postblog om, hvordan du opretter signaturer til antivirus.

eksempel på underskrift:

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}

Self-protection

self beskyttelse er meget vigtigt for en antivirus, for at undgå at blive besejret af en ondsindet program og fortsætte med at beskytte brugeren. Dette er grunden til, at et antivirusprogram skal være i stand til at beskytte sin egen installation og holde vedholdenhed ved genstart.

der er flere steder at beskytte: filer, registreringsnøgler, processer/tråde, hukommelse.

  • filbeskyttelse implementeres i minifilteret med særlige regler for antivirusfilerne (ingen adgang til sletning, omdøbning, flytning, skrivning).
  • Registreringsbeskyttelse foretages i registreringsfilteret, med adgang nægtet for registreringsnøgler til driveren og tjenesten.
  • driverens tråde er beskyttet, fordi det er helt umuligt at aflæse kernemodulet uden at gå ned i systemet
  • for at kunne beskytte tjenesten, som er en userland-proces, 2 løsninger:
  1. det nemmeste ville være at tilføje regler for fejl i service manager og indstille hver fejlregel til “genstart service”. På den måde, når tjenesten ikke stoppes af service manager, genstarter den. Selvfølgelig bør tjenesten ikke kunne acceptere kommandoer, før systemet ikke genstarter (eller stopper).
491838-428-487-263x300
  1. den anden metode, som er mere generisk, ville være at indstille tilbagekald på proceshåndtag med obregistercallbacks.

ved at indstille ObjectType til PsProcessType og operationen til OB_OPERATION_HANDLE_CREATE, modtager du en tilbagekald før og efter operation, og du er i stand til at returnere ACCESS_DENIED til Returstatus, hvis proceshåndtaget, der er anmodet om, har givet adgang, som har procesafslutningsrettigheder (eller processkrivningsrettigheder eller noget, der kan føre til et nedbrud/drab), og selvfølgelig, hvis processen er en, skal antivirusprogrammet beskytte (dets service er ikke påkrævet), og hvis processen er for eksempel).

selvfølgelig skal man også beskytte Duplikathåndtag og PsThreadType for at undgå enhver termineringsmetode, der kræver at få fat i et håndtag på processen eller en tråd. Her er et lille eksempel på brugen af denne tilbagekald.

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 (grafisk brugergrænseflade)

dette er den synlige del af isbjerget. Efter min mening er en (måske den) vigtigste del, hvis du vil sælge dit produkt. Brugere elsker det, der er smukt, let at bruge, intuitivt. Selvom det ikke er 100% effektivt. GUI ‘ en må være fræk.

GUI ‘ en er kun en tom skal, den udfører kun grafiske behandlinger og sender/modtager kommandoer til kernen (tjenesten). Det viser også statuslinjer, hvad der analyseres, giver konfiguration, og så… her er Avast UI. Lækker, ikke? 🙂

avast

arkitektur

den globale arkitektur kunne være noget, der ser sådan ud:

  1. GUI: ingen administrative rettigheder, svag
  2. Guard DLL(s) : beskyttelse mod internet, MEDIUM
  3. Service : administratorrettigheder. Fungerer som en indgangsport til kernekode og træffer beslutninger sammen med en database, stærk
  4. Driver(er) : Kernefiltre, stærk

GUI ‘ en har ikke brug for nogen administrativ ret, den tager kun brugerhandlinger og overfører dem til tjenesten. Det viser også produktstatus. Intet mere, Dette er ikke dens mål. Hvis GUI ‘ en dræbes, er dette ikke et problem, da tjenesten skal kunne genstarte den.

guard DLL ‘ erne (hvis nogen) injiceres massivt i alle processer og skal være i stand til at kigge efter iat-kroge og/eller ondsindede tråde. De skal være ret svære at losse eller besejre. De er ikke kritiske, men vigtige.

tjenesten er kernen i produktet. Det skal være unkillable, eller i det mindste bør være i stand til selv-genstart på kill. Tjenesten er ansvarlig for kommunikation mellem alle moduler i produktet, den sender kommandoer til drivere, tager kommandoer fra brugeren og spørger databasen til prøveanalyse. Dette er hjernen.

kernedriverne er også kritiske. De er tentaklerne, der samler information om alt, hvad der sker på systemet, og overfører dem til tjenesten til beslutning. De er også i stand til at nægte adgang til beskyttede steder, baseret på tjenestens beslutning.

konklusion

at lave en stærk, pålidelig og stabil antivirusmotor er en kompliceret opgave, der har brug for eksperimenterede mennesker med en meget stærk viden inden for vinduer kernel programmering, vinduer applikationsprogrammering, GUI design, programarkitektur, ondsindet analyse, …

opbygning af stabile drivere er også en kompliceret opgave, fordi et lille sandkorn kan gå ned i hele systemet. Det har brug for test, test og masser af test. Når din motor er færdig og arbejder, skal du bare ansætte forskere til at analysere ondskab og tilføje signaturer til din database. hvad venter du på? Kom så!

Links

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

Leave a Reply

Din e-mailadresse vil ikke blive publiceret.