File Coverage

blib/lib/DBIx/Oracle/Unwrap.pm
Criterion Covered Total %
statement 37 37 100.0
branch 4 8 50.0
condition 2 6 33.3
subroutine 9 9 100.0
pod 3 3 100.0
total 55 63 87.3


line stmt bran cond sub pod time code
1             package DBIx::Oracle::Unwrap;
2 3     3   198957 use strict;
  3         8  
  3         128  
3 3     3   3816 use MIME::Base64;
  3         3002  
  3         238  
4 3     3   14294 use IO::Uncompress::Inflate qw(inflate $InflateError);
  3         246470  
  3         505  
5 3     3   5314 use Readonly;
  3         12330  
  3         195  
6 3     3   3835 use File::Slurp;
  3         90301  
  3         2616  
7            
8             =head1 NAME
9            
10             DBIx::Oracle::Unwrap - Unwrap code obfuscated with the Oracle wrap command
11            
12             =head1 VERSION
13            
14             Version 0.06
15            
16             =cut
17            
18             our $VERSION = '0.06';
19            
20             =head1 SYNOPSIS
21            
22             This class unwraps text that has been obfuscated using the wrap utiltity
23             supplied with version 10 of Oracle and above. Note that it does not unwrap
24             text from earlier versions, as the method of obfuscation is different
25            
26             How to unwrap code in a file
27            
28             use DBIx::Oracle::Unwrap;
29             my $filename = "$HOME/plsql/mypackage.plb";
30             my $unwrapper = DBIx::Oracle::Unwrap->new();
31             my $unwrapped_text = $unwrapper->unwrap_file($filename);
32            
33             How to unwrap code in the database.
34            
35             use DBIx::Oracle::Unwrap;
36             use DBI;
37            
38             my $dbh = DBI->connect('DBI:Oracle:orcl', 'scott', 'tiger');
39            
40             my $source_sql = q/
41             SELECT text
42             FROM user_source
43             WHERE name = 'MYPACKAGE'
44             AND type = 'PACKAGE BODY'
45             ORDER BY line
46             /;
47            
48             my $source = join("",@{$dbh->selectcol_arrayref($source_sql)});
49             my $unwrapper = DBIx::Oracle::Unwrap->new();
50             my $unwrapped_text = $unwrapper->unwrap($source);
51            
52             =head1 METHODS
53            
54             =cut
55            
56             # this is the substituion table. All the characters in the base64 decoded text
57             # must be replaced with its lookup
58            
59             Readonly::Array my @sub_table => (
60             0x3d, 0x65, 0x85, 0xb3, 0x18, 0xdb, 0xe2, 0x87, 0xf1, 0x52, 0xab, 0x63,
61             0x4b, 0xb5, 0xa0, 0x5f, 0x7d, 0x68, 0x7b, 0x9b, 0x24, 0xc2, 0x28, 0x67,
62             0x8a, 0xde, 0xa4, 0x26, 0x1e, 0x03, 0xeb, 0x17, 0x6f, 0x34, 0x3e, 0x7a,
63             0x3f, 0xd2, 0xa9, 0x6a, 0x0f, 0xe9, 0x35, 0x56, 0x1f, 0xb1, 0x4d, 0x10,
64             0x78, 0xd9, 0x75, 0xf6, 0xbc, 0x41, 0x04, 0x81, 0x61, 0x06, 0xf9, 0xad,
65             0xd6, 0xd5, 0x29, 0x7e, 0x86, 0x9e, 0x79, 0xe5, 0x05, 0xba, 0x84, 0xcc,
66             0x6e, 0x27, 0x8e, 0xb0, 0x5d, 0xa8, 0xf3, 0x9f, 0xd0, 0xa2, 0x71, 0xb8,
67             0x58, 0xdd, 0x2c, 0x38, 0x99, 0x4c, 0x48, 0x07, 0x55, 0xe4, 0x53, 0x8c,
68             0x46, 0xb6, 0x2d, 0xa5, 0xaf, 0x32, 0x22, 0x40, 0xdc, 0x50, 0xc3, 0xa1,
69             0x25, 0x8b, 0x9c, 0x16, 0x60, 0x5c, 0xcf, 0xfd, 0x0c, 0x98, 0x1c, 0xd4,
70             0x37, 0x6d, 0x3c, 0x3a, 0x30, 0xe8, 0x6c, 0x31, 0x47, 0xf5, 0x33, 0xda,
71             0x43, 0xc8, 0xe3, 0x5e, 0x19, 0x94, 0xec, 0xe6, 0xa3, 0x95, 0x14, 0xe0,
72             0x9d, 0x64, 0xfa, 0x59, 0x15, 0xc5, 0x2f, 0xca, 0xbb, 0x0b, 0xdf, 0xf2,
73             0x97, 0xbf, 0x0a, 0x76, 0xb4, 0x49, 0x44, 0x5a, 0x1d, 0xf0, 0x00, 0x96,
74             0x21, 0x80, 0x7f, 0x1a, 0x82, 0x39, 0x4f, 0xc1, 0xa7, 0xd7, 0x0d, 0xd1,
75             0xd8, 0xff, 0x13, 0x93, 0x70, 0xee, 0x5b, 0xef, 0xbe, 0x09, 0xb9, 0x77,
76             0x72, 0xe7, 0xb2, 0x54, 0xb7, 0x2a, 0xc7, 0x73, 0x90, 0x66, 0x20, 0x0e,
77             0x51, 0xed, 0xf8, 0x7c, 0x8f, 0x2e, 0xf4, 0x12, 0xc6, 0x2b, 0x83, 0xcd,
78             0xac, 0xcb, 0x3b, 0xc4, 0x4e, 0xc0, 0x69, 0x36, 0x62, 0x02, 0xae, 0x88,
79             0xfc, 0xaa, 0x42, 0x08, 0xa6, 0x45, 0x57, 0xd3, 0x9a, 0xbd, 0xe1, 0x23,
80             0x8d, 0x92, 0x4a, 0x11, 0x89, 0x74, 0x6b, 0x91, 0xfb, 0xfe, 0xc9, 0x01,
81             0xea, 0x1b, 0xf7, 0xce
82             );
83            
84             sub _decode {
85 2     2   4 my $self = shift;
86 2         4 my $text = shift;
87            
88 2 50       7 return unless $text;
89            
90             # Decode text and ignore the SHA1 hash (first 20 characters)
91 2         23 my $decoded = substr(decode_base64($text), 20, length($text) - 1);
92 2 50       8 return unless $decoded;
93            
94 2         4 my ($zipped, $source);
95            
96             #Translate each character
97 2         46 foreach my $byte (split //, $decoded) {
98 142         1155 $zipped .= chr($sub_table[ord($byte)]);
99             }
100            
101             # Uncompress (inflate) the data
102 2 50       48 my $status = inflate \$zipped => \$source
103             or die "Can't decompress requested data: $InflateError\n";
104 2         3879 return $source;
105             }
106            
107             =head2 new
108            
109             Create an instance of DBIx::Oracle::Unwrap:
110            
111             my $unwrapper = DBIx::Oracle::Unwrap->new;
112            
113             =cut
114            
115             sub new {
116 1     1 1 6161 my ($class, %args) = @_;
117 1         6 return bless \%args, $class;
118             }
119            
120             =head2 unwrap
121            
122             Unwrap the provided text:
123            
124             my $unwrapped_text = $unwrapper->unwrap($mytext);
125            
126             =cut
127            
128             sub unwrap {
129 2     2 1 10 my $self = shift;
130 2         5 my $text = shift;
131 2         25 my @line = split("\n", $text);
132            
133             # Line 20 marks the beginning the last line of the header. Everything
134             # beyond is the wrapped code. The second number on line 20 is the length of
135             # the base 64 encoded text. If the 20th line doesn't meet the pattern below
136             # then chances are the code is either not wrapped, or it uses the wrapper
137             # from Oracle 9
138            
139             # The second line appears to be '0' in the older wrapper, so don't even
140             # try unwrapping if that's the case with the supplied text
141            
142             return
143 2 50 33     52 unless (($line[19] =~ /^[0-9a-f]+ [0-9a-f]+$/)
      33        
144             && ($line[1] ne '0')
145             && ($line[0] =~ /\bwrapped\b/));
146 2         14 my $enc_source = join("", @line[20 .. scalar(@line) - 1]);
147 2         9 return $self->_decode($enc_source);
148             }
149            
150             =head2 unwrap_file
151            
152             Unwrap the text in the file provided:
153            
154             $my $unwrapped_text = $unwrapper->unwrap_file($file_name);
155            
156             =cut
157            
158             sub unwrap_file {
159 1     1 1 808 my $self = shift;
160 1         2 my $file_name = shift;
161 1         9 my $file_text = read_file($file_name);
162 1         171 return $self->unwrap($file_text);
163             }
164            
165             =head1 SEE ALSO
166            
167             L is a script supplied with this distribution that will
168             unwrap obfuscated files. It writes to STDOUT, so redirect to
169             a file if you want to keep the output
170            
171             unwrap mysource.plb > unwrapped.plb
172            
173             =head1 ACKNOWLEDGEMENTS
174            
175             This code is largely based on uwrap.py, by Niels Teusink. See
176             L
177            
178             Thanks to Niels for supporting this port of his code.
179            
180             =head1 AUTHOR
181            
182             Dan Horne, C<< >>
183            
184             =head1 BUGS
185            
186             Please report any bugs or feature requests to C
187             rt.cpan.org>, or through the web interface at
188             L. I will be
189             notified, and then you'll automatically be notified of progress on your bug as I
190             make changes.
191            
192             =head1 SUPPORT
193            
194             You can find documentation for this module with the perldoc command.
195            
196             perldoc DBIx::Oracle::Unwrap
197            
198            
199             You can also look for information at:
200            
201             =over 4
202            
203             =item * RT: CPAN's request tracker (report bugs here)
204            
205             L
206            
207             =item * AnnoCPAN: Annotated CPAN documentation
208            
209             L
210            
211             =item * CPAN Ratings
212            
213             L
214            
215             =item * Search CPAN
216            
217             L
218            
219             =back
220            
221             =head1 LICENSE AND COPYRIGHT
222            
223             Copyright 2012 Dan Horne
224            
225             This Perl implementation is based on Python code by Niels Teusink, 2010
226             L
227            
228             This program is free software; you can redistribute it and/or modify it
229             under the terms of the the Artistic License (2.0). You may obtain a
230             copy of the full license at:
231            
232             L
233            
234             Any use, modification, and distribution of the Standard or Modified
235             Versions is governed by this Artistic License. By using, modifying or
236             distributing the Package, you accept this license. Do not use, modify,
237             or distribute the Package, if you do not accept this license.
238            
239             If your Modified Version has been derived from a Modified Version made
240             by someone other than you, you are nevertheless required to ensure that
241             your Modified Version complies with the requirements of this license.
242            
243             This license does not grant you the right to use any trademark, service
244             mark, tradename, or logo of the Copyright Holder.
245            
246             This license includes the non-exclusive, worldwide, free-of-charge
247             patent license to make, have made, use, offer to sell, sell, import and
248             otherwise transfer the Package with respect to any patent claims
249             licensable by the Copyright Holder that are necessarily infringed by the
250             Package. If you institute patent litigation (including a cross-claim or
251             counterclaim) against any party alleging that the Package constitutes
252             direct or contributory patent infringement, then this Artistic License
253             to you shall terminate on the date that such litigation is filed.
254            
255             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
256             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
257             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
258             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
259             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
260             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
261             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
262             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
263            
264            
265             =cut
266            
267             1; # End of DBIx::Oracle::Unwrap