indexwritingsjournal › 2011

grawity's journal

DRM continued

There has been some discussion about a recently released e-textbook, due to it being distributed with a Windows-only DRM layer despite lacking copyrighted content. I've no need for this textbook, but the descriptions of the DRM seemed familiar, so I decided to take a look – for educational purposes, of course. It turned out to be the same tux_XFS DRM I've described earlier, with the same annoyances (no copy, no printing, ancient Adobe Reader; breaks clipboard for the entire session while open), but with no apparent protection of any kind – distributed publicly over the Internet, and no serial number.

The publisher's website did have a form asking for my name and email, though, but the download did not have any identifying information (unlike some shareware programs I've seen, which would embed the user's name into the installer). However, the ebook's installer silently runs the tux_XFS online activator.

Overall, the protection looked the same as in previous ebooks – except this time, the .exe swap trick didn't work; the new .exe just wouldn't load XFS.dll. It could be that there is some secret handshake to be done with the launcher in this version. So I went for a different approach.

The overlay still used the same container format and the same method of encrypting Reader's temporary files; I successfully decrypted the latter with the key I found from the previous ebook. However, Reader keeps an exclusive lock on the files while running, and deletes them afterwards, so this is not a very convenient method, as well as still requiring Windows.

Finally I attacked the XFS container files directly. The format of 001.dat turned out to be rather simple, and even though I still don't know the purpose of some data ranges (especially in the container header), most of them correspond neatly to various Windows file APIs (filename, DOS attributes, timestamp) or to metadata about the container item (start position, raw size). Soon I had written a cross-platform script for extracting and decrypting these containers. It does not handle the "no copy"/"no print" bits in the .pdf files, though, and I'm still searching for a reliable tool to remove those.

Related note: The container files store files in 64 kB blocks, padded with null bytes. At the end of each file, there will be several kB worth of null bytes, XORed with a 256-byte key. Hello known-plaintext attack...

Reprogramming USB drives

The prestigio USB pendrive my mother had suddenly started throwing out various I/O errors when adding or removing files; it would refuse to update the first few blocks where the FAT lies, becoming more-or-less useless. An obvious solution was to buy a second identical pendrive and copy all data to it.

...however, the new drive turned out to be "enhanced" (read: "fucked up") with a second read-only partition of some sorts, with 500 MB reserved but only a tenth of it actually used (by an outdated AVG Free and a dozen stock JPEGs). "Hey, how about we waste 6% of the drives we sell for absolutely no reason?" In addition, the disk was not divided using MBR partitions but instead appeared as two distinct LUNs.

Browsing the internets, I found a tool for re-programming UT165 flash chips, which prestigio pendrives are built upon. I was able to merge both LUNs of the new drive – and not only that, but I could also low-level reformat the old drive, skipping the bad blocks (which left me with a perfectly working 3.3 GB drive out of 4 GB).

Async Kerberos logins

My computers have Kerberos set up, which is practically useless (the "machines" are only one, not counting Cluenet boxen) but still somewhat cool. Using pam_krb5 to obtain Kerberos tickets on login, however, can result in really slow logins when the connection is unreliable. Since the accounts are primarily kept locally (/etc/passwd), I have switched to pam_exec running a background script that obtains tickets using PKINIT (since apparently I cannot pipe passwords to kinit).

Note to self: PKINIT requires krb5-pkinit to be installed on the server. As obvious as it may look, I already forgot it twice, being used to Arch's "everything in one package" philosophy.

In which I incriminate myself

I'm occassionally asked to crack proprietary DRMs of ebooks by an unnamed publisher. Although their ebooks themselves are just PDFs displayed in a packaged Adobe Reader 7, they must be opened through a "launcher" program, which attempts to prevent the book from being copied.

Some books were distributed on USB pendrives and simply checked Registry for a specific storage device ID – which could be bypassed by simply writing a launcher-launcher which adds the necessary values. I had to do this for one book since the PDF file communicated with the launcher in some way. (There was also some sort of "drive type = removable" check, where I went old-skool with w32dasm and hiew.) Other launchers were done away with entirely, keeping just a batch script to start the packaged Reader.

The last few releases were easier. Some books used online activation, others still checked hardware IDs; however, the PDF files were static, without dependencies on the launcher or the packaged version of Reader. Extracting them was easy – three ebooks had simple password protection, and the launcher would "type in" the password. They went down against Asterisk Logger. Other two were encrypted using simple XOR, but %TEMP% had the decrypted files for me to grab, and this allowed me to find the XOR key too.

The latest book was quite interesting: temp files were encrypted, and Process Explorer showed nonexistent executables running. As its own debug log revealed, a special DLL loaded into AcroRd32 would hook such Windows calls as ZwOpenFile, essentially setting up an overlay file system which contained the protected files and was only visible to AcroRd32. The trick was to make it run cmd.exe instead, and use that to copy files. (As it turned out, the overlay would also automagically decrypt Acr*.tmp with yet another XOR key. Figuring out what happens when you XOR-decrypt a series of null bytes is left as an exercise to the reader.)

An interesting thing to note: The filesystem overlay was also used for sending messages from the PDF to the launcher, by attempting to open nonexistent documents named #I, #O<filename>, and so on.

VirtualBox bridged network and WLAN

Bridging wlan0 is a pain. You normally cannot add it to a bridge interface (brctl returns "Operation not permitted"), and using VirtualBox "bridged" filter results in a big mess of ARP and DHCP conflicts. The cause of this is that 802.11 frames contain only three addresses by default: the MAC addresses of both wireless devices (laptop and AP) and of the final recipient (as in Ethernet). It is always assumed that there is only one possible originator.

802.11 can carry the fourth, originator's MAC address, and this is used in WDS mode by repeaters. This feature can be enabled on Linux too, using iw, and enabling this mode will allow wlan0 to be used in bridge interfaces, as well as with VirtualBox bridged networking:

iw dev wlan0 set 4addr on

However, with 4addr enabled, you're likely to get completely ignored by the AP: association succeeds but all data frames disappear into the ether. This could be for security reasons (because it's damn hard to spoof the source MAC address. Yeah.) In my router (running OpenRG), it's necessary to enable "WDS" mode for the wireless AP interface, add a WDS device restricted to my laptop's MAC address, and add it to the LAN bridge. 4addr packets now work.

There's another problem with this, though – the router now rejects three-address packets from the laptop, which can be rather inconvenient (having to toggle 4addr every time the WLAN network is changed). The workaround is to add, on the laptop, a second wireless interface linked to the same device, but with a different MAC address. First undo the earlier configuration:

iw dev wlan0 set 4addr off

Then, add a second interface – the name was chosen arbitrarily – with a different MAC address:

iw dev wlan0 interface add wds.wlan0 type managed 4addr on
ip link set dev wds.wlan0 addr <addr>
ip link set dev wds.wlan0 up

Here <addr> must match the WDS device address configured in the router; other than that, it can be any valid MAC address. The original MAC of wlan0 then remains for "normal" usage.

It's possible to use both wlan0 and wds.wlan0 at the same time – although I've only tested associating to the same AP twice, not to different APs. I'm guessing they would need to at least be on the same channel.

(Update: Some people have asked me why I wrote this when VirtualBox can bridge WiFi "just fine". The answer is that VirtualBox does not send the virtual machines' MAC addresses; rather, it performs NAT at the MAC layer too. – 2014-08-22)

Kerberos on Windows XP

After joining Windows XP to an external Kerberos realm with ksetup /setrealm and then unjoining it, Windows completely loses the ability to log in as a Kerberos account. Instead of looking up a Kerberos KDC (registry configuration or _kerberos._udp.<realm> SRV records), it attempts to find an Active Directory domain with the same name, by looking up _kerberos._tcp.dc._msdcs.<realm> and attempting to make a CLDAP lookup on it for (&(&(DnsDomain=NULLROUTE.EU.ORG)(Host=HAILSTORM))(NtVer=0x20000006)).

Why? I have no idea, yet. Registry accesses by LSASS as shown by ProcMon remain the same.

After a realm join and unjoin using ksetup followed by a standard workgroup join and reboot, it started working. After second reboot, it stopped. Now it works again.

Disappearing AutoPlay items

Sometimes the AutoPlay action window in Windows XP stops displaying such built-in actions as "Open folder" or "Take no action".This is usually caused by a misconfigured event handler. (I'm not sure yet how the handler gets misconfigured, though.)

  1. Run regedit.
  2. Navigate to HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers\EventHandlers.
  3. Find the offending value and delete it. Often problems are caused by Picasa2ImportPicturesOnArrival.

ntpasswd, chntpw and group membership

After promoting a user to Administrators with chntpw or ntpasswd, the Administrators group becomes impossible to access (returning "Invalid argument"). This is caused by chntpw incrementing the member count in SAM, but failing to actually append the SID of the new member. (Similarly, the Users group has its member count decremented, but the old SID is still there. This does not result in an error because Windows just ignores the rest.) Fixing this requires some dark magic.

  1. Run regedit as LocalSystem (using psexec or similar hacks).
  2. Navigate to HKLM\SAM\SAM\Domains\Builtin\Alias.
  3. Fix Administrators group: In 00000220\C, decrement dword at 0030h.(a dword is 4 bytes, big-endian)
  4. Fix Users group: In 00000221\C, increment dword at 0030h.
  5. Fix groups of the "promoted" user: In subkey Members\<authority>\<relative>, change the "(Default)" value to 21 02 00 00 (RID 545, the built-in Users group). Use "Modify binary data" for this.

ConsoleKit and local sessions

(Update: Since the writing of this post, ConsoleKit was replaced with systemd, so this post is a bit out-of-date, and the method to fix startx was changed. The PAM module remains necessary. – 2014-06-15)

After upgrading an Arch system that has been untouched for two months, ConsoleKit sessions created by startx were no longer marked as active. Apparently, is now needed in order for ConsoleKit to consider the session to be "local".

From my /etc/pam.d/login:

session         optional
-session        optional

Inserting processes into a pipeline

(Update: This is no longer necessary. On Linux, you can just attach pv to a file descriptor of another process, using pv -d <pid>:<fd>. – 2014-06-23)

Window one: Whoops, forgot pv...

$ tar c foo | gzip > foo.tgz

Window two: Create two named pipes, and run pv reading from one & writing to the other.

$ mkfifo /tmp/in /tmp/out
$ pv /tmp/in > /tmp/out

Window three: Run gdb on the writer. Close stdout (file descriptor 1), and open a named pipe in its place (the 1 is O_WRONLY):

$ gdb -p `pgrep -x tar`
(gdb) p close(1)
$1 = 0
(gdb) p open("/tmp/in", 1)
$2 = 1

Here, open() returned file descriptor 1 again. In some very unlikely cases, if you got a different fd number, it would be necessary to use p dup2(<fd>, 1) to copy it to fd 1.

Window four: Do the same on the reader. Close stdin (file descriptor 0), then open the "output" named pipe as the new stdin (the 0 is O_RDONLY). Finally, detach the debugger.

$ gdb -p `pgrep -x gzip`
(gdb) p close(0)
$1 = 0
(gdb) p open("/tmp/out", 0)
$2 = 0
(gdb) detach
Detaching from program: /bin/gzip, process 1900
(gdb) q

Back to window three. Detach the debugger here, too.

(gdb) detach
Detaching from program: /bin/tar, process 1899
(gdb) q


year 2010

year 2009