Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,24 @@ UnwrapperPLSQL
Oracle 10g/11g PL/SQL unwrapper 0.2 by Niels Teusink.

From: http://blog.teusink.net/2010/04/unwrapping-oracle-plsql-with-unwrappy.html

## Usage

### Python 2 (original)

```sh
python unwrap.py infile.plb [outfile]
```

### Python 3 (port)

`unwrap.py` targets Python 2 (`print` statement, `base64.decodestring`,
`chr()` returning bytes). `unwrap_py3.py` is a Python-3 port that
preserves the original algorithm and the embedded `charmap` table.

```sh
python3 unwrap_py3.py infile.plb [outfile]
```

Smoke-tested against ~460 real Way4 (OpenWay) Oracle package bodies (~900k
total lines of decoded PL/SQL) on Python 3.9.
61 changes: 61 additions & 0 deletions unwrap_py3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Oracle 10g/11g PL/SQL unwrapper — Python 3 port of Niels Teusink's unwrap.py.
Original: http://blog.teusink.net/2010/04/unwrapping-oracle-plsql-with-unwrappy.html
Public domain.
"""

import re
import base64
import zlib
import sys

charmap = [0x3d, 0x65, 0x85, 0xb3, 0x18, 0xdb, 0xe2, 0x87, 0xf1, 0x52, 0xab, 0x63, 0x4b, 0xb5, 0xa0, 0x5f, 0x7d, 0x68, 0x7b, 0x9b, 0x24, 0xc2, 0x28, 0x67, 0x8a, 0xde, 0xa4, 0x26, 0x1e, 0x03, 0xeb, 0x17, 0x6f, 0x34, 0x3e, 0x7a, 0x3f, 0xd2, 0xa9, 0x6a, 0x0f, 0xe9, 0x35, 0x56, 0x1f, 0xb1, 0x4d, 0x10, 0x78, 0xd9, 0x75, 0xf6, 0xbc, 0x41, 0x04, 0x81, 0x61, 0x06, 0xf9, 0xad, 0xd6, 0xd5, 0x29, 0x7e, 0x86, 0x9e, 0x79, 0xe5, 0x05, 0xba, 0x84, 0xcc, 0x6e, 0x27, 0x8e, 0xb0, 0x5d, 0xa8, 0xf3, 0x9f, 0xd0, 0xa2, 0x71, 0xb8, 0x58, 0xdd, 0x2c, 0x38, 0x99, 0x4c, 0x48, 0x07, 0x55, 0xe4, 0x53, 0x8c, 0x46, 0xb6, 0x2d, 0xa5, 0xaf, 0x32, 0x22, 0x40, 0xdc, 0x50, 0xc3, 0xa1, 0x25, 0x8b, 0x9c, 0x16, 0x60, 0x5c, 0xcf, 0xfd, 0x0c, 0x98, 0x1c, 0xd4, 0x37, 0x6d, 0x3c, 0x3a, 0x30, 0xe8, 0x6c, 0x31, 0x47, 0xf5, 0x33, 0xda, 0x43, 0xc8, 0xe3, 0x5e, 0x19, 0x94, 0xec, 0xe6, 0xa3, 0x95, 0x14, 0xe0, 0x9d, 0x64, 0xfa, 0x59, 0x15, 0xc5, 0x2f, 0xca, 0xbb, 0x0b, 0xdf, 0xf2, 0x97, 0xbf, 0x0a, 0x76, 0xb4, 0x49, 0x44, 0x5a, 0x1d, 0xf0, 0x00, 0x96, 0x21, 0x80, 0x7f, 0x1a, 0x82, 0x39, 0x4f, 0xc1, 0xa7, 0xd7, 0x0d, 0xd1, 0xd8, 0xff, 0x13, 0x93, 0x70, 0xee, 0x5b, 0xef, 0xbe, 0x09, 0xb9, 0x77, 0x72, 0xe7, 0xb2, 0x54, 0xb7, 0x2a, 0xc7, 0x73, 0x90, 0x66, 0x20, 0x0e, 0x51, 0xed, 0xf8, 0x7c, 0x8f, 0x2e, 0xf4, 0x12, 0xc6, 0x2b, 0x83, 0xcd, 0xac, 0xcb, 0x3b, 0xc4, 0x4e, 0xc0, 0x69, 0x36, 0x62, 0x02, 0xae, 0x88, 0xfc, 0xaa, 0x42, 0x08, 0xa6, 0x45, 0x57, 0xd3, 0x9a, 0xbd, 0xe1, 0x23, 0x8d, 0x92, 0x4a, 0x11, 0x89, 0x74, 0x6b, 0x91, 0xfb, 0xfe, 0xc9, 0x01, 0xea, 0x1b, 0xf7, 0xce]


def decode_base64_package(base64str: str) -> bytes:
# strip the first 20 bytes (SHA1 hash, not verified here)
base64dec = base64.b64decode(base64str)[20:]
decoded = bytes(charmap[b] for b in base64dec)
return zlib.decompress(decoded)


def main():
if len(sys.argv) < 2:
sys.stderr.write("Usage: %s infile.plb [outfile]\n" % sys.argv[0])
sys.exit(1)

with open(sys.argv[1], 'r', encoding='latin-1', errors='replace') as infile:
lines = infile.readlines()

out_fh = sys.stdout
if len(sys.argv) >= 3:
out_fh = open(sys.argv[2], 'w', encoding='utf-8')

pat = re.compile(r"^[0-9a-f]+ ([0-9a-f]+)$")
i = 0
n = len(lines)
while i < n:
m = pat.match(lines[i].rstrip('\r\n'))
if m:
base64len = int(m.group(1), 16)
# Original logic preserved: accumulate lines (with newlines)
# until total length >= base64len, then strip newlines.
base64str = ''
j = 0
while i + 1 + j < n and len(base64str) < base64len:
j += 1
base64str += lines[i + j]
base64str = base64str.replace('\n', '').replace('\r', '')
try:
decoded = decode_base64_package(base64str)
out_fh.write(decoded.decode('utf-8', errors='replace'))
out_fh.write('\n')
except Exception as e:
sys.stderr.write(f"failed to decode block: {e}\n")
i += j + 1
else:
i += 1


if __name__ == '__main__':
main()