This appendix describes how to use the standalone
wrap utility and subprograms of the
DBMS_DDL package to obfuscate, or wrap, PL/SQL source code. When you obfuscate (hide) PL/SQL units, you can deliver PL/SQL applications without exposing your source code and implementation details.
This appendix contains these topics:
See Also:For information on the
Obfuscation, or wrapping, of a PL/SQL unit is the process of hiding the PL/SQL source code. Wrapping can be done with the
wrap utility and
DBMS_DDL subprograms. The
wrap utility is run from the command line and processes an input SQL file, such as a SQL*Plus installation script. The
DBMS_DDL subprograms wrap a single PL/SQL unit, such as a single
PROCEDURE command, that has been generated dynamically.
The advantages of obfuscating, or hiding, the source code of PL/SQL units with the
wrap utility or wrap subprograms of the
DBMS_DDL package are:
It is difficult for other developers to misuse your application, or business competitors to see your algorithms.
SQL*Plus can process the obfuscated source files.
The Import and Export utilities accept wrapped files. You can back up or move wrapped procedures.
When wrapping a package or object type, wrap only the body, not the specification. This allows other developers see the information they need to use the package or type, but they cannot see its implementation.
PL/SQL source inside wrapped files cannot be edited. To change wrapped PL/SQL code, edit the original source file and wrap it again. You can either hold off on wrapping your code until it is ready for shipment to end-users, or include the wrapping operation as part of your build environment.
To be sure that all the important parts of your source code are obfuscated, view the wrapped file in a text editor before distributing it.
Although wrapping a compilation unit helps to hide the algorithm and makes reverse-engineering difficult, Oracle Corporation does not recommend it as a secure method for hiding passwords or table names. Obfuscating a PL/SQL unit prevents most users from examining the source code, but might not stop all attempts.
The wrapping does not obfuscate the source code for triggers. To hide the workings of a trigger, you can write a one-line trigger that calls a wrapped procedure.
Wrapping only detects tokenization errors, such as a runaway string, when obfuscating PL/SQL code. Wrapping does not detect syntax or semantic errors, such as tables or views that do not exist. Those errors are detected during PL/SQL compilation or when executing the output file in SQL*Plus.
Obfuscated PL/SQL program units cannot be imported into a database of a previous (lower) release. Wrapped compilation units are upward-compatible between Oracle releases, but are not downward-compatible. For example, you can load files processed by the V8.1.5
wrap utility into a V8.1.6 Oracle database, but you cannot load files processed by the V8.1.6
wrap utility into a V8.1.5 Oracle database.
Because the source code is parsed by the PL/SQL compiler, not by SQL*Plus, you cannot include substitution variables using the SQL*Plus
DEFINE notation inside the PL/SQL code. You can use substitution variables in other SQL statements that are not obfuscated.
Most of the comments are removed in wrapped files. See "Input and Output Files for the PL/SQL wrap Utility".
If you invoke
PARSE (when using an overload where the statement formal has datatype
VARCHAR2S for text which exceeds 32767 bytes) on the output of
WRAP, then you need to set the
LFFLG parameter to
PARSE adds newlines to the wrapped unit which corrupts the unit.
wrap utility processes an input SQL file and obfuscates only the PL/SQL units in the file, such as a package specification, package body, function, procedure, type specification, or type body. It does not obfuscate PL/SQL content in anonymous blocks or triggers or non-PL/SQL code.
To run the wrap utility, enter the
wrap command at your operating system prompt using the following syntax:
wrap iname=input_file [
Do not use any spaces around the equal signs.
input_file is the name of a file containing SQL statements, that you typically run using SQL*Plus. If you omit the file extension, an extension of
.sql is assumed. For example, the following commands are equivalent:
You can also specify a different file extension:
output_file is the name of the obfuscated file that is created. The
oname option is optional, because the output file name defaults to that of the input file and its extension defaults to
.plb. For example, the following commands are equivalent:
wrap iname=/mydir/myfile.sql oname=/mydir/myfile.plb
You can use the option
oname to specify a different file name and extension:
wrap iname=/mydir/myfile oname=/yourdir/yourfile.out
The input file can contain any combination of SQL statements. Most statements are passed through unchanged.
CREATE statements that define subprograms, packages, or object types are obfuscated; their bodies are replaced by a scrambled form that the PL/SQL compiler understands.
The following CREATE statements are obfuscated:
CREATE [OR REPLACE] FUNCTIONfunction_name
CREATE [OR REPLACE] PROCEDUREprocedure_name
CREATE [OR REPLACE] PACKAGEpackage_name
CREATE [OR REPLACE] PACKAGE BODYpackage_name
CREATE [OR REPLACE] TYPEtype_name
CREATE [OR REPLACE] TYPEtype_name
CREATE [OR REPLACE] TYPE BODYtype_name
CREATE [OR REPLACE] TRIGGER statement, and
[DECLARE] BEGIN..END anonymous blocks, are not obfuscated. All other SQL statements are passed unchanged to the output file.
All comment lines in the unit being wrapped are deleted, except for those in a
REPLACE header and C-style comments (delimited by
The output file is a text file, which you can run as a script in SQL*Plus to set up your PL/SQL procedures, functions, and packages. Run a wrapped file as follows:
CREATE PROCEDURE wraptest IS TYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER; all_emps emp_tab; BEGIN SELECT * BULK COLLECT INTO all_emps FROM employees; FOR i IN 1..10 LOOP DBMS_OUTPUT.PUT_LINE('Emp Id: ' || all_emps(i).employee_id); END LOOP; END; /
To wrap the file, run the following from the operating system prompt:
The output of the
wrap utility is similar to the following:
PL/SQL Wrapper: Release 10.2.0.0.0 on Tue Apr 26 16:47:39 2005
Copyright (c) 1993, 2005, Oracle. All rights reserved.
Processing wrap_test.sql to wrap_test.plb
If you view the contents of the
wrap_test.plb text file, the first line is
wrapped and the rest of the file contents is hidden.
You can run
wrap_test.plb in SQL*Plus to execute the SQL statements in the file:
wrap_test.plb is run, you can execute the procedure that was created:
SQL> CALL wraptest();
DBMS_DDL package contains procedures for obfuscating a single PL/SQL unit, such as a package specification, package body, function, procedure, type specification, or type body. These overloaded subprograms provide a mechanism for obfuscating dynamically generated PL/SQL program units that are created in a database.
DBMS_DDL package contains the
WRAP function and the
CREATE_WRAPPED procedure. The
CREATE_WRAPPED both wraps the text and creates the PL/SQL unit. When calling the wrap procedures, use the fully-qualified package name,
SYS.DBMS_DDL, to avoid any naming conflicts and the possibility that someone might create a local package called
DBMS_DDL or define the
DBMS_DDL public synonym. The input
REPLACE statement executes with the privileges of the user who invokes
DBMS_DDL package also provides the
MALFORMED_WRAP_INPUT exception (ORA-24230) which is raised if the input to the wrap procedures is not a valid PL/SQL unit.
Example A-1 illustrates how
CREATE_WRAPPED can be used to dynamically create and wrap a package specification and a package body in a database.
DECLARE -- the package_text variable contains the text to create the package spec and body package_text VARCHAR2(32767); FUNCTION generate_spec (pkgname VARCHAR2) RETURN VARCHAR2 AS BEGIN RETURN 'CREATE PACKAGE ' || pkgname || ' AS PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER); PROCEDURE fire_employee (emp_id NUMBER); END ' || pkgname || ';'; END generate_spec; FUNCTION generate_body (pkgname VARCHAR2) RETURN VARCHAR2 AS BEGIN RETURN 'CREATE PACKAGE BODY ' || pkgname || ' AS PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS BEGIN UPDATE employees SET salary = salary + amount WHERE employee_id = emp_id; END raise_salary; PROCEDURE fire_employee (emp_id NUMBER) IS BEGIN DELETE FROM employees WHERE employee_id = emp_id; END fire_employee; END ' || pkgname || ';'; END generate_body; BEGIN package_text := generate_spec('emp_actions'); -- generate package spec SYS.DBMS_DDL.CREATE_WRAPPED(package_text); -- create and wrap the package spec package_text := generate_body('emp_actions'); -- generate package body SYS.DBMS_DDL.CREATE_WRAPPED(package_text); -- create and wrap the package body END; / -- call a procedure from the wrapped package CALL emp_actions.raise_salary(120, 100);
SELECT text FROM USER_SOURCE WHERE name = 'EMP_ACTIONS';
The resulting output appears similar to:
PACKAGE emp_actions wrapped