cum se face un motor antivirus

trimiterea

evaluarea Utilizatorilor4. 54(13 voturi)

Introducere

când călătoresc în jurul forumurilor techies, văd adesea unii oameni (și mulți nu foarte experimentați) care întreabă „cum fac un antivirus”, uneori cu limbaje nu foarte adaptate (bat, PHP, …) și având o idee greșită despre ce este un antivirus și cum ar trebui construit.

am văzut, de asemenea, multe „software-uri Antivirus” făcute de copii, cu foarte puțini oameni încă la școală și aproximativ 4 ore pe zi de codificare pe câteva săptămâni. Nu spun kiddies nu sunt calificați, dar eu spun construirea unui motor antivirus are nevoie fie mulțime de oameni calificați, cu locuri de muncă cu normă întreagă, plus mult timp pentru a elibera un software decent sau o mulțime de bani pentru a le plăti (în cazul în care nu sunt voluntari).

deci, voi acoperi aici liniile directoare pentru o codificare antivirus de bază, Pentru Windows și în C/C++. Se poate găsi aici indicii pentru a proiecta un motor antivirus, sau pur și simplu să învețe cum cele mai multe dintre ele sunt construite.

protecție

pentru o protecție bună, un Antivirus trebuie să aibă cel puțin un driver, pentru a putea rula cod în kernel și, în general, să aibă acces la API-urile kernel. Începând cu Vista, Microsoft a înțeles că industria Antivirus are nevoie de chei pentru a intra în kernel și a activa filtrele în locuri strategice, cum ar fi sistemul de fișiere, registrul și rețeaua. Nu vă mirați dacă construirea unui antivirus pentru sistemele pre-Vista poate fi o adevărată durere, deoarece nu a fost proiectat pentru acest lucru.

  • cu toate acestea, pe sistemele Pre-Vista, companiile Antivirus foloseau funcții asemănătoare rootkit-ului pentru a proteja ușile (chiar dacă nu a fost deloc recomandat de Microsoft) și pentru a vă putea proteja sistemul. Ei au folosit ceea ce noi numim „cârlige” (ocoluri API pentru filtrarea scop).
  • pe Vista+, Microsoft a furnizat API-uri pentru a insera driverul nostru de nivel scăzut între apelurile userland și API-urile kernel. În acest fel, este ușor să înregistrați un produs antivirus în kernel. Mai mult, acest tip de sistem bazat pe Înregistrare ne permite să expediem securitatea sistemului nostru în straturi, unde mai multe produse cu scopuri diferite pot coabita. Acest lucru nu a fost cazul pentru cârlige, ca punerea în aplicare a fost total dependent de produs.

notă: Nu voi acoperi soluțiile cu cârlige pentru sistemele pre-Vista, pentru că este ușor de găsit pe internet și pentru că ar avea nevoie de un capitol întreg pentru a explica cum să cârlig, unde să cârlig și așa… dar trebuie să știți că este aceeași idee decât API-urile kernel-ului, cu excepția faptului că trebuie să vă implementați ceea ce Microsoft a furnizat pe sistemele Vista+.

pentru a afla mai multe despre drivere de codificare, puteți verifica dacă link-uri utile:
http://msdn.microsoft.com/en-us/library/windows/hardware/gg490655.aspx
http://www.codeproject.com/Articles/9504/Driver-Development-Part-1-Introduction-to-Drivers

pentru a afla despre cârlige, puteți verifica exemplul de bază:
http://www.unknowncheats.me/forum/c-and-c/59147-writing-drivers-perform-kernel-level-ssdt-hooking.html

Process

primul lucru pentru a proteja utilizatorul de, este lansarea proceselor malware. Acesta este lucrul de bază. Antivirus ar trebui să înregistreze un apel invers PsSetCreateProcessNotifyRoutineEx. Procedând astfel, la fiecare creare a procesului și înainte ca firul principal să înceapă să ruleze (și să provoace lucruri rău intenționate), apelul antivirus este notificat și primește toate informațiile necesare.

primește numele procesului, obiectul fișierului, PID-ul și așa mai departe. Pe măsură ce procesul este în așteptare, șoferul poate spune serviciului său să analizeze memoria procesului pentru orice rău intenționat. Ea fondează ceva, șoferul va seta pur și simplu CreationStatus la fals și să se întoarcă.

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;

Fire

în aceeași idee decât pentru procese, firele pot fi o modalitate prin care lucrurile rău intenționate pot provoca daune. De exemplu, se poate injecta un cod într-un proces legitim și se poate începe un fir de la distanță pe acel cod în contextul procesului (ușor de urmărit? 🙂 ). În acest fel, un proces legitim poate face lucruri rău intenționate.

putem filtra noi fire cu apel invers PsSetCreateThreadNotifyRoutine. De fiecare dată când este creat un fir, antivirusul este notificat cu TID și PID. Astfel, este capabil să privească codul de adresă de pornire al firului, să-l analizeze și fie să oprească firul, fie să-l reia.

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

imagini

a treia amenințare dinamică este despre imagini care pot fi încărcate în memorie. O imagine este un fișier PE, fie un EXE, un fișier DLL sau SYS. Pentru a fi notificat de imagini încărcate, pur și simplu înregistrați PsSetLoadImageNotifyRoutine. Acest apel invers ne permite să fim anunțați când imaginea este încărcată în memoria virtuală, chiar dacă nu este executată niciodată. Putem detecta apoi când un proces încearcă să încarce un DLL, să încarce un driver sau să declanșeze un nou proces.

apelul invers primește informații despre calea completă a imaginii (utilă pentru analiza statică) și, cu atât mai important, în opinia mea, Adresa de bază a imaginii (pentru analiza în memorie). Dacă imaginea este rău intenționată, antivirusul poate folosi mici trucuri pentru a evita execuția, cum ar fi analizarea imaginii din memorie și accesarea punctului de intrare, apoi apelați opcode-ul de asamblare „ret” pentru a o anula.

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;

sistem de fișiere

odată ce fiecare lucru dinamic este securizat, un antivirus ar trebui să poată notifica utilizatorul pentru lucruri rău intenționate din mers, nu numai atunci când sunt pe cale să înceapă. Un antivirus ar trebui să poată scana fișiere atunci când utilizatorul deschide un folder, o arhivă sau când este descărcat pe disc. Mai mult, un antivirus ar trebui să se poată proteja, interzicând oricărui program să-și șteargă fișierele.

modul de a face toate acestea este să instalați un driver în sistemul de fișiere și, mai precis, un minifilter al unui filtru vechi (mod vechi). Aici vom vorbi despre minifiltru.

un minifiltru este un anumit tip de driver, capabil să înregistreze callback-uri la fiecare operație de citire/scriere efectuată pe sistemul de fișiere (funcții majore IRP). Un IRP (Interrupt Request Paquet) este un obiect folosit pentru a descrie o operație de citire/scriere pe disc, care este transmisă împreună cu stiva driverului. Minifilterul va fi pur și simplu introdus în acea stivă și va primi acel IRP pentru a decide ce să facă cu acesta (permiteți/refuzați funcționarea).

pentru un mic exemplu de minifiltru, vă rugăm să verificați acel link util sau acela. Instrucțiunile microsoft sunt aici.

veți găsi, de asemenea, 2 Exemple de documentație WDK aici și aici.

un apel invers minifiltru de bază arata ca aceasta. Există 2 tipuri de apel invers, pre operație și post operație, care sunt capabili de a filtra înainte de după interogare. Iată un pseudo cod de preoperare:

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

Registrul

registrul este unul dintre cele mai critice locuri de pază. Există multe multe moduri pentru un malware pentru a păstra mâna persistente pe sistem prin înregistrarea unui singur (sau câteva) chei / valori în registru. Cele mai cunoscute locuri sunt Run keys și servicii. Acesta este și locul în care antivirusul poate fi învins (împreună cu sistemul de fișiere), prin simpla eliminare a cheilor driverului/serviciului, astfel încât să nu mai repornească la pornirea sistemului.

aceasta nu este o necesitate reală pentru un antivirus pentru a proteja locurile de repornire, majoritatea nu. dar trebuie să-și păzească cheile de instalare a registrului, pentru a evita să fie învinși cu ușurință de malwares. Acest lucru se poate face prin înregistrarea CmRegisterCallback.

apelul invers oferă suficiente informații pentru a obține numele complet al cheii, tipul de acces (creare, Redenumire, ștergere, … ) și PID-ul apelantului. În acest fel, este ușor să acordați acces sau nu la apel, setând câmpul de stare al post Operation callback.

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;

rețea (Firewall)

pentru a păzi ușile întregului trafic de internet care poate fi uriaș pe anumite sisteme (servere, utilizatori uriași de lățime de bandă) fără a fi încetinit de comutarea contextului care are loc în userland, nu este recomandat să instalați un firewall care nu are driver de bază, cu excepția unor filtre de browser web care pot fi suficiente pentru traficul http, dar care nu vor proteja împotriva comunicării malware in/out.

pentru a avea o implementare corectă a firewall-ului, ar trebui să codificați un NDIS, TDI sau o altă metodă pentru driverul de filtrare IP de nivel scăzut. NDIS / TDI este un pic dificil de făcut și ar necesita multă cunoaștere (mai mult decât alte filtre în opinia mea).

oricum, iată câteva indicii pentru a începe codificarea unui astfel de driver, liniile directoare microsoft și vechiul tutorial codeproject (dar încă bun de citit), un exemplu de firewall NDIS și un exemplu de firewall TDI. Aici este, de asemenea, un bun scris despre NDIS firewall bypass truc, și o mică explicație despre stiva driver de rețea,

Userland protection

userland protection nu este o necesitate, dar poate fi un modul suplimentar împotriva bancheri troian, și mai precis împotriva spioni proces. Acestea sunt, în general, injectate în fiecare proces, din mai multe motive.

în primul rând, ei sunt capabili (la cerere) să omoare procesul dacă a fost identificat ca malware (acest lucru nu ar trebui să se întâmple, deoarece AVs ar trebui să-l oprească înainte de a începe). Este întotdeauna mai ușor să oprești un proces atunci când te afli în contextul său.

în al doilea rând, ei sunt capabili să protejeze procesele critice, cum ar fi browserele web, împotriva agățării malwares capabile să ocolească și să filtreze apelurile API pentru a aduna parole, informații bancare și pentru a redirecționa fluxul de internet către serverele malware. Ei urmăresc doar modificarea IAT, pentru îmbinare și pot seta, de asemenea, cârlige pentru a evita LoadLibray-ul unui DLL malware și, astfel, interzic anumite metode de injectare a codului.

o modalitate ușoară de a injecta un DLL protector în toate procesele este de a utiliza cheia de registry AppInitDll pentru a înregistra dll protector. Se va încărca DLL în fiecare proces a început pe sistem, de îndată ce acestea se leagă User32.imagine dll (cele mai multe dintre ele fac).

motor de analiză

motorul de analiză este una dintre cele mai importante părți, este responsabil pentru analizarea probelor de fișiere/memorie provenite de la drivere. Dacă trebuie să fie rapid (chiar și cu o bază de date uriașă) și ar trebui să poată gestiona majoritatea tipurilor de fișiere (executabile auto-extrase, arhive – RAR, ZIP, fișiere ambalate – UPX,…) și, prin urmare, ar trebui să aibă multe module pentru a face acest lucru:

  • Unpacker: acest modul trebuie să poată detecta și despacheta majoritatea ambalatorilor cunoscuți (UPX, Armadillo, ASPack, …)
  • Signature engine: baza de date a unui antivirus conține milioane de semnături, iar motorul ar trebui să poată căuta rapid într-un eșantion. Astfel, un algoritm foarte puternic ar trebui să facă parte din acesta. Câteva exemple : AhoCorasick, RabinKarp, algoritmi de potrivire a șirurilor.
  • Sandbox: acest modul nu este necesar, dar ar fi un plus pentru a putea rula probe într-o memorie limitată, fără efect asupra sistemului. Acest lucru ar putea ajuta la despachetarea probelor ambalate cu ambalatori necunoscuți și ar putea ajuta motorul euristic (vezi După) să detecteze apelurile API care ar putea fi considerate suspecte sau rău intenționate. Niște nisip bun aici.
  • motor euristic : așa cum am spus mai sus, motorul euristic nu caută semnături, ci mai degrabă caută un comportament suspect (adică. exemplu care deschide o conexiune pe site-ul hxxp: / / malware_besite. com). care se poate face prin analiză statică sau printr-un sandbox.

sintaxa semnăturii

sintaxa semnăturii este „Dicționarul” limbii pe care motorul semnăturii o înțelege. Este o modalitate de a formaliza care este modelul de găsit, cum să îl căutați și unde să îl căutați în eșantion. Sintaxa trebuie să fie suficient de simplă pentru ca cercetătorii să o înțeleagă, suficient de puternică pentru a gestiona fiecare caz de utilizare și ușor de analizat pentru performanțe mai bune ale motorului.

VirusTotal a dezvoltat o sintaxă și un motor bun (proiectul Yara), care este open source. Acesta ar trebui să fie un pointer bun pentru a vă crea propria sintaxă sau pur și simplu să o utilizați. Iată, de asemenea, un blog bun despre cum creați semnături pentru antivirus.

exemplu de semnătură:

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}

auto-protecție

auto-protecția este foarte importantă pentru un antivirus, pentru a evita să fie învins de un malware și să continue să protejeze utilizatorul. Acesta este motivul pentru care un antivirus ar trebui să poată să-și protejeze propria instalare și să păstreze persistența la repornire.

există mai multe locuri de protejat: fișiere, chei de Registry, procese/Fire, memorie.

  • Protecția fișierelor este implementată în minifiltru, cu reguli speciale privind fișierele antivirusului (fără acces la ștergere, Redenumire, Mutare, scriere).
  • protecția registrului se face în filtrul de registru, accesul fiind refuzat pentru cheile de registry ale șoferului și ale serviciului.
  • firele de drivere sunt protejate, pentru că este destul de imposibil de a descărca modulul de kernel fără crashing sistemul
  • pentru a putea proteja serviciul, care este un proces userland, 2 soluții:

  1. cel mai simplu ar fi să adăugați reguli pentru eșecuri în managerul de servicii și să setați fiecare regulă de eșec la „reporniți serviciul”. În acest fel, atunci când serviciul nu este oprit de managerul de servicii, acesta repornește. Desigur, serviciul nu ar trebui să poată accepta comenzi până când sistemul nu repornește (sau nu se oprește).
491838-428-487-263x300
  1. a doua metodă, care este mai generică, ar fi să setați callback-urile pe mânerele procesului cu ObRegisterCallbacks.

prin setarea ObjectType la PsProcessType și operațiunea de a OB_OPERATION_HANDLE_CREATE, primiți un apel invers pre și post operație, și sunteți în stare să se întoarcă ACCESS_DENIED în ReturnStatus dacă procesul de mâner interogat a GrantedAccess care au proces termina drepturile (sau proces scrie drepturi, sau ceva care poate duce la un accident/ucide), și, desigur, în cazul în care procesul este unul antivirus trebuie să păzească (serviciul său, de exemplu).

desigur, unul, de asemenea, trebuie să păzească mâner duplicat și PsThreadType pentru a evita orice metodă de terminare care necesită pentru a apuca un mâner pe procesul sau un fir. Iată un mic exemplu de utilizare a acestui apel invers.

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)

aceasta este partea vizibilă a aisbergului. În opinia mea, una (poate) cea mai importantă parte dacă doriți să vă vindeți produsul. Utilizatorii iubesc ceea ce este frumos, ușor de utilizat, intuitiv. Chiar dacă nu este 100% eficient. GUI trebuie să fie sexy.

GUI este doar un shell gol, nu numai tratamente grafice, și trimite/primi comenzi la miezul (serviciul). Se afișează, de asemenea, bare de progres, ceea ce este analizat, oferă configurare, și așa… aici e UI Avast. Sexy, nu? 🙂

avast

arhitectura

arhitectura globală ar putea fi ceva care arată astfel:

  1. GUI : nu există drepturi administrative, slab
  2. Guard DLL(S): Protecție browser web, Mediu
  3. serviciu: drepturi de administrator. Servește ca o poarta de acces la codul de kernel și să ia decizii, împreună cu unele baze de date, puternic
  4. Driver(e) : Filtre Kernel, puternic

GUI nu are nevoie de nici un drept administrativ, este nevoie doar de acțiuni de utilizator și le transmite la serviciu. De asemenea, afișează starea produsului. Nimic mai mult, acest lucru nu este scopul său. Dacă GUI este ucis, aceasta nu este o problemă, deoarece serviciul ar trebui să îl poată reporni.

DLL-urile de pază (dacă există) sunt injectate masiv în toate procesele și ar trebui să poată căuta cârlige IAT și/sau fire rău intenționate. Ar trebui să fie destul de greu de descărcat sau de învins. Ele nu sunt critice, ci importante.

serviciul este nucleul produsului. Ar trebui să fie unkillable, sau cel puțin ar trebui să poată auto-reporni pe kill. Serviciul este responsabil pentru comunicarea între toate modulele produsului, trimite comenzi către drivere, preia comenzi de la utilizator și interoghează baza de date pentru analiza eșantionului. Acesta este creierul.

driverele de kernel sunt, de asemenea, critice. Sunt tentaculele care adună informații despre tot ceea ce se întâmplă în sistem și le transmit serviciului pentru decizie. De asemenea, aceștia pot refuza accesul la locurile păzite, pe baza deciziei serviciului.

concluzie

realizarea unui motor antivirus puternic, fiabil și stabil este o sarcină complicată, care are nevoie de oameni experimentați cu cunoștințe foarte puternice în programarea kernel-ului windows, programarea aplicațiilor windows, proiectarea GUI, arhitectura software, analiza malware, …

construirea driverelor stabile este, de asemenea, o sarcină complicată, deoarece un mic bob de nisip poate prăbuși întregul sistem. Este nevoie de testare, testare, și mulțime de testare. Când motorul dvs. este terminat și funcționează, va trebui doar să angajați cercetători pentru a analiza malwares și pentru a adăuga semnături în baza dvs. de date, ce așteptați? Du-te!

link-uri

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

Leave a Reply

Adresa ta de email nu va fi publicată.