Loki's anti's analysis
Notice: As noted by @Antelox this is just a loader that drops LokiBot, not LokiBot itself.
Preface
Loki is a credential harvester bot sold in Russian underground forums and black markets.
Goal
For this blogpost, the goal will be to defeat/patch Loki's anti measures to be able to properly analyze malicious behavior.
Analysis
Initial triage
File hashes for the sample I'll be using:
MD5: 09D2E274F1F50AB81105A3A6B9BE34CF
SHA-1: 04AD370BFE1A0AFA273568EE18F8C14BD8E612DC
SHA-256: D7AAAFB88B91A937D1EF8BCAA97F88A13545364269F510A95DAB4A72B68A4313
The sample has a pretty low detection ratio (8/58) as of now (29/03/2017) on VirusTotal.
TrID report:
Win32 Executable Delphi generic (37.4%)
Windows screen saver (34.5%)
Win32 Executable (generic) (11.9%)
Win16/32 Executable Delphi generic (5.4%)
Generic Win/DOS Executable (5.2%)
PEiD report:
PEiD BobSoft Mini Delphi -> BoB / BobSoft
The sample reports a compilation timestamp of 1991-03-05 11:25:56
, this is most likely faked.
Average entropy for the sample is 6.77, so we will have to investigate a little further to know if it is packed or not.
Obfuscation / Anti
Strings
Loki uses cryptic-looking strings like "BwOjhICsHS"
, "js7X6kq2Bh"
and "LU81txhLQMvIQfqmNA2Q1c5LsEH55"
. They look like it might be obfuscated data but they are actually used as direct arguments for GetModuleHandle
and GetProcAddress
. Of course, these calls return NULL
.
Anti#1: Pointless loops
Just beside the cryptic strings, Loki has a lot of (15 that I've seen) small loops that do nothing but decrease a register a big amount of times. This can be either to mess with analysts or to enforce a timeout in automatic analysis tools like AV sandboxes (just like PE Cloak does).
Anti#2: Big memory allocations
Loki does allocate very big memory sections and writes useless data to them. That is another AV sandbox evasion trick, as AV's sandboxes can not allocate such a large memory space. It writes the memory with junk data because on most modern operative systems the memory is not actually allocated until you use it.
Here's how Loki does it:
mem = VirtualAlloc_0(0, 544844866u, 0x3000u, 4u);// Allocate a lot of mem
if ( mem )
{
j = 1;
do
{
k = j + 1;
if ( j >= 0xFFFFFFFF )
sys_error_5();
remainder = k % 0x45;
if ( k % 0x45 > 0xFF )
sys_err_4();
if ( __CFADD__(j, mem) )
sys_error_5();
mem[j++] = remainder; // Write memory with useless data
}
while ( j != 544840799 );
v20 = 1163247917; // Some more useless looping
do
--v20;
while ( v20 );
VirtualFree_0(mem, 0, 0x8000u); // Free the memory
As you may guess this just fills the memory with sequential bytes from 0x00
to 0x44
.
Anti#3: Obscure instructions / calls
-
Loki does operations that will most likely break in a sandbox. For this sample, I've observed the
StrokeAndFillPath(NULL)
. -
Executes useless asm instructions like
xchg ecx, ecx
ormov ecx, ecx
. I'm betting that's trying to breakx86
emulation (i.e: more sandbox evasion). -
Keeps calling
rdtsc
until the following condition is met:
do
timestamp = __rdtsc();
while ( (unsigned int)timestamp > HIDWORD(timestamp) );
This last loop may be trying to detect stubbing of the rdtsc
instruction.
Anti#4: Checking the foreground window
Loki tries to detect human activity by constantly checking the foreground window. If the foreground window does not change Loki will enter an endless loop where it only calls Sleep
and GetForegroundWindow
Anti#5: Scrambled functions
One thing that pops up when opening the Loki sample in IDA is how many E
-filled strings it does report.
DATA:00482C84 00000009 C EEEEE�`O
DATA:00482CC3 00000007 C EEEEEEE
DATA:00482CE6 00000005 C EEEEE
DATA:00482CF6 00000005 C EEEEP
DATA:00482D03 0000000D C EEEEEEEEEEEEE
DATA:00482D33 00000007 C EEEEE_f
DATA:00482D61 00000007 C EEEEEjd
DATA:00482D76 00000009 C EEEEEEVj\\
DATA:00482D84 00000006 C EEEEE^
DATA:00482D8F 00000007 C EEEEEEE
DATA:00482DA2 00000006 C EEEEEW
DATA:00482DB9 0000000F C EEEEEEEEEEEEjs^
DATA:00482DCD 00000008 C EEEEEEEE
DATA:00482DE0 00000005 C EEEEY
DATA:00482DF5 00000007 C EEEEEEE
DATA:00482E2F 00000007 C EEEEEjc
DATA:00482E53 0000000C C EEEEEEEEEEEE
DATA:00482E93 00000009 C EEEEEEEEE
DATA:00482EA1 00000008 C EEEEEEEE
DATA:00482EC0 0000000C C EEEEEE����_
DATA:00482ECD 00000008 C EEEEEEEE
DATA:00482EF1 00000006 C EEEEEE
And that's not even the 10% of them.
Turns out the E
strings (or 0x45
values) are used to separate pieces of a function, which are connected with jmp
instructions.
This breaks static analysis most of the time, as a disassembler will, most likely, interpret the data between the 0x45
values as some other ASCII values.
Lucky for us we can reconstruct this function in IDA by creating a function (P
and C
keys).
Sadly, the result is not very... nice.
Anti#6: Virtualization detection with CPUID
Loki uses the cpuid
instruction to detect virtualization.
Of course, this comparison is not immediate. It first moves the data on the stack a couple of times so you get dizzy. But, in the end, it gets to comparing. This sample compares against KVMKVMKVM
, Microsoft HV
, prl hyperv
& XenVMMXenVMM
. If any of this comparisons succeeds the function will return 1
, in the contrary, the function will return 0
.
I am running VMWare (cpuid
string is vmwarevmware
), so checks failed as there is no comparison for VMWare (there might be on some other samples).
After hypervisor comparison, Loki moves to CPU vendor comparisons. Funny thing is it checks against the same blacklist (the hypervisor blacklist), so all checks failed again.
Are we done yet? Of course not!
Anti#7: SSE instructions support
Now Loki does this:
mov eax, 1
movd xmm0, eax ; Moves the one into xmm0
xor eax, eax ; Zeroes eax
movd eax, xmm0 ; Recovers the 1 back into eax
mov [esp+4+arg_1C], eax ; Writes the value
cmp [esp+4+arg_1C], 1 ; Checks it's still one
jnz badVMorCPUdetected ; Crash if check failed
This dumb code checks for SSE instruction support, as the xmm0
register was introduced by the SSE standard in 1999.
Anti#8: Dynamically resolved functions
If any of the previous checks fails Loki will jump to a specific address, else, it will start resolving DLL functions. Loki's DLL resolution is not straightforward, as everything else is, but if we explore the inner-most function setting breakpoints in all possible ret
statements we can easily check that it is returning the address of VirtualAlloc
on its first call.
If we observe some more calls to this function we can gather that the function takes a parameter. This parameter is used to identify the function to resolve. Successive calls will resolve CreateProcessW
, GetThreadContext
, SizeOfResource
, GetFileAttributes
, SetFileAttributes
, SetFilePointer
, WideCharToMultiByte
, VirtualFree
, GetModuleHandleA
, SetThreadContext
, LoadResource
, ResumeThread
, FindResourceW
, TerminateProcess
, GetCommandLineW
, ExitProcess
, GetModuleFileNameW
, WriteFile
, CreateFileW
, CloseHandle
, GetFileSize
, ReadFile
, CreateDirectoryW
, DeleteFileW
, GetTempPathW
, CopyFileW
, GetSystemDirectoryW
, CreateToolhelp32Snapshot
, Process32FirstW
, Process32NextW
, ReadProcessMemory
, WriteProcessMemory
, VirtualAllocEx
, Sleep
, CreateThread
, TerminateThread
(Phew! That's a long list).
But that's only for kernel32
, the process is repeated for shell32
, user32
, advapi32
and ntdll
.
The most remarkable functions resolved from these DLLs are:
shell32
:
ShellExecute
user32
:
CallWindowProcW
MessageBoxA
advapi32
:
RegSetValueEx
ntdll
ZwUnmapViewOfSection
ZwResumeThread
ZwMapViewOfSection
ZwCreateSection
It is also remarkable that at the end of the function resolution process for advapi32
it tries to resolve a last function with identifier 1584B9A5h
. If said function is correctly resolved Loki will call ExitProcess
. Else, it calls VirtualAlloc
again, but don't get too excited, it is to store more resolved functions from ntdll
.
After that the outer-most function returns, so we can easily mark that functions and inner ones as function resolvers.
Anti#9: Checks process names lenghts
Loki iterates over process names and checks if there is any whose name length is bigger or equal to 64. If so, Loki exits. This is most likely because malware analysts often name malware samples after their SHA-256, which is exactly 64 characters in length. Therefore, if a process name with that length is found it may be a SHA-256 and it may be a sample under analysis, so better exit. Anyway, this anti is easily countered naming your sample something short.
Anti#10: Say my name (aka checking self-name)
Lokis checks the executale's path against a list of hard-coded values. In my particular sample this list was:
0018FA2C 00 00 00 00 00 00 00 00 73 00 61 00 6E 00 64 00 ........s.a.n.d.
0018FA3C 62 00 6F 00 78 00 00 00 6D 00 61 00 6C 00 77 00 b.o.x...m.a.l.w.
0018FA4C 61 00 72 00 65 00 00 00 73 00 61 00 6D 00 70 00 a.r.e...s.a.m.p.
0018FA5C 6C 00 65 00 00 00 00 00 76 00 69 00 72 00 75 00 l.e.....v.i.r.u.
0018FA6C 73 00 00 00 73 00 65 00 6C 00 66 00 2E 00 00 00 s...s.e.l.f.....
To retrieve the path of the executable Loki uses GetModuleHandleA
. To make the comparison a custom strstr
-like function is used, meaning that if the executable's path contains any of the blacklisted items the check will succeed.
Also, Loki writes the strings to memory in the most ridiculous fashion possible, by pushing and popping individual letters.
Anti#11: Checking running processes
Loki checks running processes names against a blacklist. For my particular sample the blacklist was:
0018F9DC 00 00 3D 00 00 00 00 00 70 00 72 00 6F 00 63 00 ..=.....p.r.o.c.
0018F9EC 65 00 78 00 70 00 36 00 34 00 2E 00 65 00 78 00 e.x.p.6.4...e.x.
0018F9FC 65 00 00 00 70 00 72 00 6F 00 63 00 6D 00 6F 00 e...p.r.o.c.m.o.
0018FA0C 6E 00 36 00 34 00 2E 00 65 00 78 00 65 00 00 00 n.6.4...e.x.e...
0018FA1C 70 00 72 00 6F 00 63 00 6D 00 6F 00 6E 00 2E 00 p.r.o.c.m.o.n...
0018FA2C 65 00 78 00 65 00 00 00 6F 00 6C 00 6C 00 79 00 e.x.e...o.l.l.y.
0018FA3C 64 00 62 00 67 00 2E 00 65 00 78 00 65 00 00 00 d.b.g...e.x.e...
0018FA4C 70 00 72 00 6F 00 63 00 65 00 78 00 70 00 2E 00 p.r.o.c.e.x.p...
0018FA5C 65 00 78 00 65 00 00 00 77 00 69 00 6E 00 64 00 e.x.e...w.i.n.d.
0018FA6C 62 00 67 00 2E 00 65 00 78 00 65 00 00 00 00 00 b.g...e.x.e.....
Anti#12: Is Being Debugged
Loki checks the IsBeingDebugged
flag from the PEB. If it finds the process is being debugged it generates an access fault.
Anti#13: You can never be too sure
Loki calls the Is Being Debugged (#12) 4 times (through execution of my sample) and CPUID (#5) twice.
Anti#14: Check if AV software is running
Loki checks if anti-virus software is running by comparing running process names.
My Loki sample checks for avp.exe
(Kaspersky), avgcsrva.exe
(AVG), bdagent.exe
+ bdwtxag.exe
(BitDefender) and dwengine.exe
(Dr. Web).
If Loki finds any of these AV products it writes a 1
to esp+28
and esp+30
, which probably uses later to decide if execution should continue.
Malicious activity will be analyzed in a separate blogpost.