There was an interesting questuion on my forum a couple of days ago titled "
its legal to host unwrap?"; bear in mind the title means the opposite, i.e. is it illegal not its illegal. These semantics do not alter the question though! The poster wants to host a free unwrapper, its an interesting question that is already answered at one level. i.e. someone else has already done it.
There is already a Swiss site that hosts a 10g unwrapper for free -
http://hz.codecheck.ch/UnwrapIt/Unwrap.jsp - I am myself unsure what the legal position would be in hosting an unwrapper as you would have no control over what people could unwrap. If you unwrap privately for clients as a paid service then as Gary suggests in his reply in the forum you can put in place contracts where the client has to show that he has legal ownership of the code he wants unwrapping but as the poster suggests he wants to host a free service. A contracted service which is of course more controlled is then a source recovery service and companies do have a genuine need for this where they have lost the original source code. Just because someone else has done it already doesnt make it legal!
I was at the UKOUG conference Monday and Tuesday and with clients yesterday and today so had littlke time to blog but one of the things I was going to talk about as it happens was unwrapping as I was cornered twice at the UKOUG conference by people asking me about unwrapping and the paper I wrote a few years ago for the
How to unwrap PL/SQL that i presented at Black Hat in Las Vegas. Of course more recently Anton made available some details on
how to unwrap 10g PL/SQL on his blog. David also talked about unwrapping in his Oracle Hackers Handbook - book, interestingly he had a view on the legality in that he refrained from publishing the lookup table used in the wrap process but this was actually about trade secrets and reverse engineering and not about using an unwrapper. I published a simple demo unwrapper that used the DIANA and PIDL packages to show how Oracle unwraps as part of the pstub code used for remote PL/SQL calls. This needs to work with wrapped and unwrapped code hence the need for it to work with DIANA. This is how 9i wrap works, 10g is different but both still use DIANA under the hood of course. The code is called
unwrap_r.sql but it wont unwrap anything real as its simply demonstrating the use of DIANA and PIDL and those mechanisms only expose the signatures of packages and nothing else.
I also have unwrappers for 10g and 9i and lower completely written in PL/SQL of course. Here is a little demo of it running on some 9i PL/SQL code. First create a simple procedure to use for this test case. The code is just made up for this experiment and is very simple:
SQL> get sample1.sql 1 create or replace procedure test_proc (pv_num in number, 2 pv_var in varchar2, pv_var3 in out integer) is 3 l_num number:=3; 4 l_var number; 5 j number:=1; 6 procedure nested (pv_len in out number) 7 is 8 x number; 9 begin 10 x:=pv_len*5; 11 end; 12 begin 13 case l_num 14 when 1 then 15 -- IF 1 16 l_var:=3; 17 dbms_output.put_line('This is a header'); 18 dbms_output.put_line('The number is '||l_var); 19 dbms_output.put_line('The case var is '||l_num); 20 when 2 then 21 -- IF 2 22 l_var:=4; 23 dbms_output.put_line('This is a header'); 24 dbms_output.put_line('The number is '||l_var); 25 dbms_output.put_line('The case var is '||l_num); 26 when 3 then 27 -- IF 3 28 l_var:=6; 29 dbms_output.put_line('This is a header'); 30 dbms_output.put_line('The number is '||l_var); 31 dbms_output.put_line('The case var is '||l_num); 32 else 33 dbms_output.put_line('wrong choice'); 34 end case; 35 if ((j=1) and (j=3)) then 36 dbms_output.put_line('here is IF'); 37 elsif ((j=2) or (j!=3)) then 38 dbms_output.put_line('The elsif clause'); 39 else 40 dbms_output.put_line('else clause'); 41 end if; 42 j:=4; 43 nested(j); 44 dbms_output.put_line('nested=:'||j); 45 for j in reverse 1..pv_num 46 loop 47 if mod(j,2) = 0 then 48 dbms_output.put_line('for loop with reverse'); 49 end if; 50 end loop; 51* end; SQL>
|
I can then wrap this with the 9i wrap utility:
C:\unwrapper>wrap iname=sample1.sql oname=sample1.plb
PL/SQL Wrapper: Release 9.2.0.1.0- Production on Mon Jun 01 14:02:34 2009
Copyright (c) Oracle Corporation 1993, 2001. All Rights Reserved.
Processing sample1.sql to sample1.plb
C:\unwrapper>head sample1.plb
|
Then I can show it is indeed wrapped by viewing the contents (Note the above commands are in a DOS box, the head command is on the same machine but from cygwin as the head command is available:
$ head -20 sample1.plb create or replace procedure test_proc wrapped 0 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 3 7 9200000
|
Now load the wrapped file into an 11gR1 database and check its stored wrapped:
SQL> @sample1.plb
Procedure created.
SQL> select substr(text,1,60) 2 from dba_source 3 where name='TEST_PROC' 4 and rownum=1;
SUBSTR(TEXT,1,60) ------------------------------------------------------------ procedure test_proc wrapped 0 abcd abcd abcd abcd abcd abcd
SQL>
|
Now we can simply unwrap it using my PL/SQL based unwrapper:
SQL> @unwrap_c
unwrap_c: Release 1.4.0.0.0 - Production on Mon Jun 01 14:07:13 2009 Copyright (c) 2008, 2009 PeteFinnigan.com Limited. All rights reserved.
NAME OF OBJECT TO CHECK [P1]: TEST_PROC OWNER OF OBJECT TO CHECK [SYS]: SYS TYPE OF THE OBJECT [PROCEDURE]: PROCEDURE OUTPUT METHOD Screen/File [S]: S FILE NAME FOR OUTPUT [priv.lst]: OUTPUT DIRECTORY [DIRECTORY or file (/tmp)]:
create or replace procedure TEST_PROC( PV_NUM in NUMBER, PV_VAR in VARCHAR2, PV_VAR3 in out INTEGER) is L_NUM NUMBER:=3; L_VAR NUMBER; J NUMBER:=1; procedure NESTED( PV_LEN in out NUMBER) is X NUMBER; begin X:= PV_LEN * 5; end; begin case L_NUM when 1 then L_VAR:=3; DBMS_OUTPUT. PUT_LINE('This is a header'); DBMS_OUTPUT. PUT_LINE('The number is ' || L_VAR); DBMS_OUTPUT. PUT_LINE('The case var is ' || L_NUM); when 2 then L_VAR:=4; DBMS_OUTPUT. PUT_LINE('This is a header'); DBMS_OUTPUT. PUT_LINE('The number is ' || L_VAR); DBMS_OUTPUT. PUT_LINE('The case var is ' || L_NUM); when 3 then L_VAR:=6; DBMS_OUTPUT. PUT_LINE('This is a header'); DBMS_OUTPUT. PUT_LINE('The number is ' || L_VAR); DBMS_OUTPUT. PUT_LINE('The case var is ' || L_NUM); else DBMS_OUTPUT. PUT_LINE('wrong choice'); end case; if ( ( J = 1) and ( J = 3)) then DBMS_OUTPUT. PUT_LINE('here is IF'); elsif ( ( J = 2) or ( J != 3)) then DBMS_OUTPUT. PUT_LINE('The elsif clause'); else DBMS_OUTPUT. PUT_LINE('else clause'); end if; J:=4; NESTED( J); DBMS_OUTPUT. PUT_LINE('nested=:' || J); for J in reverse 1.. PV_NUM loop if MOD( J,2) = 0 then DBMS_OUTPUT. PUT_LINE('for loop with reverse'); end if; end loop; end; /
INFO: Elapsed time = [.1 Seconds]
PL/SQL procedure successfully completed.
For more information please visit
SQL>
|
This gives a 100% source code recovery for 9i and lower PL/SQL code and I have mechanisms built in that can prove this even if the original source code was lost. For 10g and 11g the algorithm is simpler and if its implemented right it will always give a complete source recovery. For 9i this (100% source recovery) is much harder to achieve as the method of wrapping is much more complex - see my Black Hat paper above for details of why its harder.