Unwrapping 10G wrapped PL/SQL


Many people have tried to unwrap wrapped PL/SQL. Most people haven’t succeeded in doing it, but since Pete Finnegan’s presentation on the 2006 Black Hat conference, http://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Finnigan.pdf, unwrapping PL/SQL on the older database versions, pre 10G, is possible. David Litchfield, in his book “The Oracle Hacker’s Handbook”, describes a method to unwrap code on a 10G database. According to the description it doesn’t seem too difficult to do:
“First, it is base64 decoded. Then, each byte is
resubstituted with a second corresponding substitution table. Last, the text is
decompressed, leaving the clear text of the PL/SQL.”
A year ago, after reading the book, I first tried to unwrap some code. Without success! But this year I was looking for a nice project to kill some time and I decided to give it another go. From Litchfield’s description it is clear that you need a substitution table. Litchfield didn’t reveal the table. I couldn’t find it with Google. Looking for it in the wrap utility it not allowed (and difficult too, see for instance this work from some Israeli researchers http://webcourse.cs.technion.ac.il/236349/Spring2009/ho/WCFiles/final_report.pdf ) So I had to find another way to get the table. I started with just looking at the output of some “simple” wrapped procedures. You can use wrap.exe to wrap the PL/SQL code in a file, but it is much easier to use dbms_ddl.wrap.
Unwrapping 10G wrapped PL/SQL image001
In this output we see a line with a000000 en 15 lines with abcd. These lines I have found in every wrapped code. The third line, in this example 367, has probably something to do with database version. On my it’s always 367, on a10.2.0.1 it’s always 2e and with my wrap.exe it’s always b2. The 19th line, indicates the type of the wrapped object . A procedure gives 7, a function 8 and a package body b.
The next line contains 2 hex numbers. The first is the length from the unwrapped text without the create + 1, the second is the length of the base64-encoded text. So looking at the output from dbms_ddl.wrap reveals some information, but even the small procedure from this example produces too much wrapped text for me to get a clue on a substitution table. But luckily it is possible to wrap even smaller pieces of code.
Unwrapping 10G wrapped PL/SQL image002
If we base64-decode this, skip the first 20 bytes (the size of a SHA1-hash, see Litchfields book) we have only 18 bytes left to decipher. For the base64-decoding we can use the package utl_encode. Oracle has also a package utl_compress, which can be used for Lempel-Ziv compression/decompression, but his time I used some Java in the database for compression/decompression (see attached file for the code). Now we have 2 options:
  • Find a substitution table, apply it, and decompress the result to get unwrapped PL/SQL code.
  • Compress PL/SQL code, and compare this to the base64-decoded output to FIND (a part of) the substitution table.
So let’s try the second option. The only problem is what to compress. I started with the same input I use for dbms_ddl.wrap.
Unwrapping 10G wrapped PL/SQL image003
Oops, the zipped code is one byte short. But remember the two hex numbers in the wrapped output. The first hex number was the length of the unwrapped code + 1. So I tried adding a newline character to the input of the compression. That gives 1 more byte for the zipped code, but it has another problem. In the output of the wrapped column we have 2 lines with 166. These lines should both have the same value in the zipped column. And that is not so. So I tried adding a space. With more success.
Unwrapping 10G wrapped PL/SQL image004
And this gives us 16 entries, from the 256, for the substation table. How easy can it be! Doing the same for “PACKAGE b” gives some more values, but “PACKAGE c” results again in 2 different “zipped values” for the same “wrapped values”. Adding a space to the input text wasn’t correct choice . After a few more trials I found out it has to the byte 0. So now we have a way to generate a part of the substitution table. Is it enough to create the complete substitution table? Calling this query in a PL/SQL loop with a lot of different names results again in errors. But this error has to do with the second byte. And for the people who don’t know the Zip-format by heart (http://www.gzip.org/zlib/rfc-zlib.html), that’s where the compression level is stored. So I changed the compression level to 9, and then I was able to generate a complete substitution table. And with this table unwrapping not that difficult at all. You can use it in a SQL-statement.
 Unwrapping 10G wrapped PL/SQL image006
Or you can use it in a Java-program to unwrap the plb-files of all your lost sources.
The used code can be downloaded here used code


  1. Murali March 8, 2011
    • Anton Scheffer March 8, 2011
  2. KAIN66 February 24, 2011
  3. Praveen Ray January 31, 2011
  4. Marcio Brazil October 22, 2010
  5. Florian Brunner June 30, 2010
  6. Steve April 12, 2010
  7. Anton Scheffer March 18, 2010
  8. renuka March 17, 2010
  9. Erik Kerkhoven December 7, 2009
  10. Anton Scheffer June 25, 2009
  11. Szymon June 24, 2009
  12. vonbean May 14, 2009
  13. Anton Scheffer April 20, 2009
  14. Lorenzo April 20, 2009
  15. Anton Scheffer April 17, 2009
  16. Lorenzo April 16, 2009
  17. Anton Scheffer April 1, 2009
  18. Arturo April 1, 2009
  19. Anton Scheffer April 1, 2009
  20. Mark Wooldridge April 1, 2009
  21. amos March 27, 2009
  22. mamv February 27, 2009
  23. Anton Scheffer February 13, 2009
  24. Donald February 12, 2009
  25. Anton Scheffer February 10, 2009
  26. Indrajit February 9, 2009
  27. Luca February 5, 2009
  28. YAP February 4, 2009
  29. Pete Finnigan February 3, 2009