
Progetto di ricerca che esegue il reverse engineering delle interfacce COM di Windows Security Center per tracciare la registrazione degli antivirus tramite ATL, vtable, WSCAPI e RPC, con verifica in fase di esecuzione via WMI.
OWN-Defender è un progetto di ricerca sulla sicurezza di Windows incentrato sulla comprensione di come il Centro Sicurezza di Windows (WSC) rappresenta e gestisce i prodotti antivirus attraverso le sue interfacce COM.
Il progetto è nato come indagine sul comportamento dimostrato da DefendNot, ma anziché trattare l'implementazione esistente come una scatola nera, l'ho usata come punto di partenza per un reverse engineering indipendente e una verifica autonoma.
L'obiettivo di questo progetto è comprendere l'intero percorso di esecuzione:
COM
↓
CLSID / IID
↓
CoCreateInstance
↓
QueryInterface
↓
ATL Interface Map
↓
vtable
↓
IWscAVStatus4
↓
CWscIsv
↓
WSCAPI.dll
↓
RPC
↓
Centro Sicurezza di Windows
Il progetto è stato sviluppato e testato in un ambiente di ricerca Windows controllato.
Solo per uso di Ricerca / Educativo
Questo progetto è destinato alla ricerca sugli internals di Windows, al reverse engineering, all'educazione sulla sicurezza e ai test di sicurezza autorizzati. Non utilizzarlo per interferire con software di sicurezza su sistemi che non possiedi o per i quali non hai esplicito permesso di test.
La domanda iniziale era semplice:
Come fa il Centro Sicurezza di Windows a sapere che esiste un prodotto antivirus?
Invece di fermarmi alla documentazione pubblica dell'API, volevo capire cosa accade sotto l'API.
Questo ha portato a diverse domande:
QueryInterface() l'interfaccia?__int64 a1 per un metodo?Register() è effettivamente la funzione di registrazione AV?Il primo passo è stato identificare la classe COM del Centro Sicurezza di Windows.
Il progetto utilizza la classe COM WSC:
CLSID_WscIsv
F2102C37-90C3-450C-B3F6-92BE1693BDF2
L'implementazione contiene anche una logica per individuare dinamicamente il CLSID dal registro di Windows invece di affidarsi esclusivamente a un valore hardcoded.
Concettualmente:
HKLM
└── SOFTWARE
└── Classes
└── CLSID
└── {CLSID}
└── API ISV del Centro Sicurezza di Windows
Questo ha fornito la prima relazione importante:
Registro
↓
CLSID
↓
API ISV del Centro Sicurezza di Windows
La sfida successiva è stata determinare quale interfaccia COM dovesse essere richiesta.
Il progetto utilizza:
IWscAVStatus4
con:
4DCBAFAC-29BA-46B1-80FC-B8BDE3C0AE4D
Una delle lezioni importanti emerse dalla ricerca è stata:
Un nome GUID da solo non è una prova sufficiente.
Ho verificato la relazione attraverso il reverse engineering anziché assumere che il nome dell'interfaccia e il GUID fossero corretti.
L'indagine ha incluso:
QueryInterface_ATL_INTMAP_ENTRYQueryInterfaceUno dei passaggi di reversing più utili è stato seguire l'implementazione di:
CComAggObject<CWscIsv>::QueryInterface()
che alla fine raggiunge:
ATL::CComObjectRootBase::InternalQueryInterface()
La mappa delle interfacce ATL viene utilizzata per confrontare l'IID richiesto con le voci di interfaccia registrate.
Concettualmente:
IID richiesto
↓
QueryInterface()
↓
InternalQueryInterface()
↓
Mappa interfacce ATL
↓
Confronto GUID
↓
Interfaccia corrispondente
↓
Puntatore all'interfaccia
Questo ha fornito una prova indipendente che il GUID oggetto di indagine corrispondeva effettivamente all'interfaccia COM prevista.
Register()Una delle maggiori sfide del reversing è stata capire perché IDA/Ghidra non sempre mostravano la firma del metodo che mi aspettavo.
L'interfaccia ricostruita contiene:
virtual HRESULT __stdcall Register(
BSTR path,
BSTR name,
unsigned int,
unsigned int
) = 0;
Tuttavia, il decompilatore poteva mostrare un'implementazione come:
_IWscAVStatus4<CWscIsv>::Register(__int64 a1)
All'inizio questo sembrava incoerente.
Ulteriori indagini hanno mostrato che la rappresentazione del decompilatore descriveva un wrapper/thunk e la chiamata vtable indiretta sottostante, piuttosto che presentare la firma logica completa dell'interfaccia.
Questa è diventata una lezione importante:
L'output del decompilatore è un'interpretazione del codice macchina, non la verità originale a livello di sorgente.
Per risolvere queste discrepanze, ho confrontato:
Definizione interfaccia COM
↓
Layout vtable
↓
Assembly
↓
Wrapper/thunk
↓
Convenzione di chiamata
↓
Funzione target effettiva
Register()Un'altra fonte di confusione era la presenza di più funzioni con nomi come:
IWscAVStatus2::Register
IWscAVStatus4::Register
IWscFWStatus2::Register
RegisterAV
La realizzazione importante è stata che nomi simili non significano interfacce identiche.
Per esempio:
AV
↓
IWscAVStatus4
↓
Registrazione AV
mentre:
Firewall
↓
IWscFWStatus2
↓
Registrazione firewall
Il numero dell'interfaccia e l'implementazione circostante dovevano essere verificati invece di selezionare una funzione semplicemente perché il suo nome conteneva Register.
Questa è stata una delle parti più utili della ricerca perché mi ha costretto a correlare:
Interfaccia
+
IID
+
vtable
+
implementazione
+
layout dei parametri
+
target della chiamata
Dopo aver identificato l'interfaccia corretta, ho tracciato l'operazione di registrazione più in profondità nel binario.
Il percorso osservato era approssimativamente:
IWscAVStatus4::Register()
↓
CWscIsv
↓
RegisterSecurityProductFunction
↓
wscRegisterSecurityProduct()
↓
WSCAPI.dll
↓
s_wscRegisterSecurityProduct()
↓
NdrClientCall3()
↓
RPC
↓
Centro Sicurezza di Windows
Questo è stato particolarmente importante perché il metodo COM stesso non era l'operazione finale.
La chiamata alla fine attraversava un confine RPC.
Questo ha cambiato il modo in cui vedevo l'architettura:
COM
≠
implementazione finale
Invece:
COM
↓
implementazione locale
↓
API WSC
↓
client RPC
↓
componente Windows
WSCAPI.dllIl livello successivo era WSCAPI.dll.
Il percorso ricostruito tramite reverse engineering raggiungeva:
wscRegisterSecurityProduct()
che alla fine invocava:
s_wscRegisterSecurityProduct()
e poi:
NdrClientCall3()
Questo era il punto in cui l'indagine passava da una normale chiamata COM all'infrastruttura RPC di Windows.
Comprendere questo livello ha aiutato a spiegare perché il comportamento non poteva essere completamente compreso guardando solo la DLL COM originale.