Last Panda Challenge 2010 write-up

Posted by vos on 28 июля, 2010
Весь трэш

== Intro ==

This challenge is a reversing task with the objective to create a license key or unpack a game exe — in fact, to make the game work.

PlayMe.exe clearly has a packer or protector inside: the entry point is outside main code section, imports section contains only one import, the .rdata, .data sections and some of resources are scrambled.
First, I had to analyze the self-extractor.

== Self-extractor analysis ==

The binary, when run, performs the following actions:

  • Determine kernel32 base address
  • Using the sole import GetProcAddress and kernel32 base address, resolve a plenty of library function imports for further work
  • Read 0x20 bytes from file «license.key»
  • Enumerate loaded system drivers and, if there is one starting with «PavP», copy 7 chars of its name and «.sys» to [licenseKey + 0x12]
  • Copy 3 chars from the beginning of own DOS header («MZ\x90») to [licenseKey + 0x1D]
  • Unpack .text (main code) section:
  • — Initialize RC4 algorithm with first 0x10 bytes of licenseKey
  • — Xor the .text section with RC4 keystream, re-initializing the key scheduler every 0x20 bytes (so the xor key has a period of 32 bytes)
  • Check decrypt correctness using SHA1
  • ===
  • Decipher .rdata and .data sections using another algorithm, key is last 0x10 bytes of licenseKey. Then check correctness using SHA1
  • Decrypt Dialog resources using RC4 and first 0x10 bytes of licenseKey, but now with no scheduler reset, so there is no periodic repeat in xor key
  • Manually resolve imports from original import directory
  • Jump to OEP at 0040309C

== Unpacking the executable ==

I went straightforward and unpacked the binary in the same order as self-extractor does

  • First, I needed to decipher the program code (.text section)
  • It is RC4-encrypted and I know nothing about the key, so instead of attacking RC4, I attacked the xor
  • The xor key has a period of 32 bytes, and we can assume that, because it’s x86 code, the 0x00 byte will be most frequently met in decrypted data.
  • Using these assumptions, CrypTool easily calculates the xor key and decrypts the .text section. And its SHA1 hash matches the hard-coded one.
  • ===
  • Now I had to decrypt .rdata and .data sections. They are encrypted with another algorithm and another key.
  • From reversing, I learned that the last 3 bytes of this key will be always overwritten by «MZ\x90». So the key is «?????????????MZ\x90»
  • Moveover, if a driver starting with «PavP» is found, key becomes «??PavP???.sysMZ\x90»
  • Through some googling, I found that PavProc.sys is a kernel-space part of Panda Antivirus 🙂 the key is likely to be «??PavProc.sysMZ\x90»
  • So, I had only to bruteforce 2 bytes, and I knew the SHA1 hash for checking, this part was quick. Key is «\xB8\x34PavProc.sysMZ\x90», and SHA1 matches
  • ===
  • Now, the most tough part. The four Dialog resources that are encrypted with RC4 the right way (without re-initting the KSA during encryption), but with the same keystream (re-initting KSA before each encryption)
  • I knew the first 32 bytes of keystream from CrypTool’s analysis in part 1, so I deciphered first 32 bytes of each Dialog
  • One of them contained the string «T\0e\0t\0r\0i», obviously the next bytes of plaintext are «\0s\0» 🙂 I had 3 more bytes or key…
  • After that, another Dialog contained «T\0e\0», I extended it with «t\0r\0i\0s\0″…
  • So, you got the point, I kept extending text strings like «Help», «Hall of Fame», until I ran out of them, and had to examine the structure of Dialog resource itself
  • I even coded an app for that, if it’s interesting for you 🙂
  • Using the app, I managed to put together the xor key. I am fully confident in three of four dialogs, in that they are identical to the original. The tail of the longest one (Hall of Fame dialog) couldn’t be compared to anything else, so I had to make up some controls inside 🙂 I think labels «Name» and «Score» make perfect sense there, and they fit in just right.

== Rebuilding the executable ==

Now, the game binary had to be slightly rebuilt so that the packer will no longer be necessary

  • Binary-patched the PlayMe.exe, putting decrypted versions of sections in place of encrypted ones
  • Put the remade dialog resources in place of encrypted
  • Changed the entry point of executable to OEP (0040309C)
  • Changed import directory address to original 004044CC
  • Deleted section with self-extractor named «locked», which is no more needed
  • Recalculated SizeOfImage and Checksum in PE header. (steps 3-6 were taken using PETools)

That’s all 🙂 Took me an enormous time of 14 hours, including 3 hours of coding the XorEditor and 1.5 hrs of writing this paper 🙂

The final result: (pw: infected — not really infected, some shitty online AV scanner disliked a signature)

Comments are closed.