Obfuscating PL/SQL Code

Semantic Designs can construct custom obfuscators for virtually any source language as a part of the corresponding Source Formatter. This page contains PL/SQL sample code, its obfuscated version, and the generated obfuscation map.

PL/SQL Sample Code before Obfuscation

(This is the same formatted code shown on the PL/SQL Formatter example page)

DECLARE
  part_no NUMBER(4);
  in_stock BOOLEAN;
  my_sal REAL(7, 2);
  credit_limit CONSTANT NUMBER := 5000.00;
  my_title books.title%TYPE;
  dept_rec dept%ROWTYPE; -- declare record variableCURSOR c1 IS
  emp_rec c1%ROWTYPE; -- declare record variable that represents
  -- a row fetched from the emp table
  acct_balance NUMBER(11, 2);
  acct CONSTANT NUMBER(4) := 3;
  debit_amt CONSTANT NUMBER(5, 2) := 500.00;
BEGIN
  tax := price * tax_rate;
  valid_id := FALSE;
  bonus := current_salary * 0.10;
  wages := gross_pay(emp_id, st_hrs, ot_hrs) - deductions;
  SELECT ename, sal, hiredate, job FROM emp;
  SELECT salary * 0.10 INTO bonus FROM employees
    WHERE employee_id = emp_id;
  SELECT AVG(sal) INTO my_sal FROM emp;
  adjust_salary(7788, my_sal); -- assigns a new value to my_sal
  FOR someone IN
    (SELECT * FROM employees)
  LOOP
    DBMS_OUTPUT.PUT_LINE('First name = ' || someone.first_name);
    DBMS_OUTPUT.PUT_LINE('Last name = ' || someone.last_name);
  END LOOP;
  my_deptno := dept_rec.deptno;
  FETCH c1 INTO emp_rec;
  SELECT bal INTO acct_balance FROM accounts
    WHERE account_id = acct
    FOR UPDATE OF bal;
  IF acct_balance >= debit_amt THEN
    UPDATE accounts
      SET bal = bal - debit_amt
      WHERE account_id = acct;
  ELSE
    INSERT INTO temp
      VALUES (acct, acct_balance, 'Insufficient funds');
  -- insert account, current balance, and message
  END IF;
  COMMIT;
  -- This CASE statement performs different actions based
  -- on a set of conditional tests.
  CASE
    WHEN shape = 'square' THEN
      area := side * side;
    WHEN shape = 'circle' THEN
      BEGIN
        area := pi * (radius * radius);
        DBMS_OUTPUT.PUT_LINE('Value is not exact because pi is irrational.');
      END;
    WHEN shape = 'rectangle' THEN
      area := LENGTH * width;
  ELSE
    BEGIN
      DBMS_OUTPUT.PUT_LINE('No formula to calculate area of a' || shape);
      RAISE PROGRAM_ERROR;
    END;
  END CASE;
  FOR num IN 1 .. 500 LOOP
    INSERT INTO roots
      VALUES (num, SQRT(num));
  END LOOP;
END;

DECLARE
  salary emp.sal%TYPE := 0;
  mgr_num emp.mgr%TYPE;
  last_name emp.ename%TYPE;
  starting_empno emp.empno%TYPE := 7499;
BEGIN
  SELECT mgr INTO mgr_num FROM emp
    WHERE empno = starting_empno;
  WHILE salary <= 2500 LOOP
    SELECT sal, mgr, ename INTO salary, mgr_num, last_name FROM emp
      WHERE empno = mgr_num;
  END LOOP;
  INSERT INTO temp
    VALUES (NULL, salary, last_name);
  COMMIT;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    INSERT INTO temp
      VALUES (NULL, NULL, 'Not found');
    COMMIT;
END;

DECLARE
  PROCEDURE award_bonus(emp_id NUMBER) IS
    bonus REAL;
    comm_missing EXCEPTION;
  BEGIN -- executable part starts here
    SELECT comm * 0.15 INTO bonus FROM emp
      WHERE empno = emp_id;
    IF bonus IS NULL THEN
      RAISE comm_missing;
    ELSE
      UPDATE payroll
        SET pay = pay + bonus
        WHERE empno = emp_id;
    END IF;
  EXCEPTION -- exception-handling part starts here
    WHEN comm_missing THEN
      COMMIT;
  END award_bonus;
BEGIN
  bonus := 1.0;
END;

CREATE PACKAGE emp_actions AS -- package specification
  PROCEDURE hire_employee(empno NUMBER,
                          ename CHAR);
  PROCEDURE fire_employee(emp_id NUMBER);
END emp_actions;

CREATE PACKAGE BODY emp_actions AS -- package body
  PROCEDURE hire_employee(empno NUMBER,
                          ename CHAR) IS
  BEGIN
    INSERT INTO emp
      VALUES (empno, ename);
  END hire_employee;
  PROCEDURE fire_employee(emp_id NUMBER) IS
  BEGIN
    DELETE FROM emp
      WHERE empno = emp_id;
  END fire_employee;
END emp_actions;

DECLARE
  TYPE Staff IS TABLE OF Employee;
  staffer Employee;
  FUNCTION new_hires(hiredate DATE)
                    RETURN Staff IS
  BEGIN
    NULL;
  END;
BEGIN
  staffer := new_hires('10-NOV-98') (5);
END;

DECLARE
  TYPE TimeRec IS
    RECORD (hours SMALLINT,
            minutes SMALLINT);
  TYPE MeetingTyp IS
    RECORD (date_held DATE,
            duration TimeRec, -- nested record
            location VARCHAR2(20),
            purpose VARCHAR2(50));
BEGIN
  NULL;
END;

CREATE TYPE Bank_Account AS
  OBJECT (acct_number INTEGER(5),
          balance REAL,
          status VARCHAR2(10),
          MEMBER PROCEDURE OPEN (amount IN REAL),
          MEMBER PROCEDURE verify_acct(num IN INTEGER),
          MEMBER PROCEDURE CLOSE (num IN INTEGER,
                                  amount OUT REAL),
          MEMBER PROCEDURE deposit(num IN INTEGER,
                                   amount IN REAL),
          MEMBER PROCEDURE withdraw(num IN INTEGER,
                                    amount IN REAL),
          MEMBER FUNCTION curr_bal(num IN INTEGER)
                                  RETURN REAL);

DECLARE
  comm_missing EXCEPTION; -- declare exception
BEGIN
  IF commission IS NULL THEN
    RAISE comm_missing; -- raise exception
  END IF;
  bonus := (salary * 0.10) + (commission * 0.15);
EXCEPTION
  WHEN comm_missing THEN
    NULL;
END;

DECLARE
  n NUMBER;
BEGIN
  n := 10E127; -- causes a 'numeric overflow or underflow' error
END;

DECLARE
  x BINARY_FLOAT := sqrt(2.0); -- Single-precision floating-point number
  y BINARY_DOUBLE := sqrt(2.0); -- Double-precision floating-point number
BEGIN
  NULL;
END;

BEGIN
  str := 'Hello, world!';
  str := 'XYZ Corporation';
  str := '10-NOV-91';
  str := 'He said "Life is like licking honey from a thorn."';
  str := '$1,000,000';
  str := q'{I'm a string, you're a string.}'; -- q'!...!' notation lets us use single quotes inside the literal.
  string_var := q'{I'm a string, you're a string.}';
  -- To use delimiters [, {, <, and (, pair them with ], }, >, and ).
  -- Here we pass a string literal representing a SQL statement
  -- to a subprogram, without doubling the quotation marks around
  -- 'INVALID'.
  func_call(q'{select index_name from user_indexes where status = 'INVALID'}');
  -- For NCHAR and NVARCHAR2 literals, use the prefix nq instead of q.
  where_clause := q'{where col_value like '%e'}';
END;

DECLARE
  d1 DATE := DATE '1998-12-25';
  t1 TIMESTAMP := TIMESTAMP '1997-10-22 13:01:01';
  t2 TIMESTAMP WITH TIME ZONE := TIMESTAMP '1997-01-31 09:26:56.66 +02:00';
  -- Three years and two months
  -- (For greater precision, we would use the day-to-second interval)
  i1 INTERVAL YEAR TO MONTH := INTERVAL '3-2' YEAR TO MONTH;
  -- Five days, four hours, three minutes, two and 1/100 seconds
  i2 INTERVAL DAY TO SECOND := INTERVAL '5 04:03:02.01' DAY TO SECOND;
BEGIN
  NULL;
END;

DECLARE
  some_condition BOOLEAN;
  pi NUMBER := 3.1415926;
  radius NUMBER := 15;
  area NUMBER;
BEGIN
  /* Perform some simple tests and assignments */
  IF 2 + 2 = 4 THEN
    some_condition := TRUE; /* We expect this THEN to always be done */
  END IF;
  /*
  The following line computes the area of a
  circle using pi, which is the ratio between
  the circumference and diameter.
  */
  area := pi * radius ** 2;
END;

DECLARE
  credit_limit CONSTANT REAL := 5000.00;
  max_days_in_year CONSTANT INTEGER := 366;
  urban_legend CONSTANT BOOLEAN := FALSE;
  hours_worked INTEGER DEFAULT 40;
  employee_count INTEGER := 0;
  acct_id INTEGER(4) NOT NULL := 9999;
  credit NUMBER(7, 2);
  debit credit%TYPE;
  NAME VARCHAR2(20) := 'JoHn SmItH';
  -- If we increase the length of NAME, the other variables
  -- become longer too.
  upper_name NAME%TYPE := UPPER(NAME);
  lower_name NAME%TYPE := LOWER(NAME);
  init_name NAME%TYPE := INITCAP(NAME);
  the_trigger user_triggers.trigger_name%TYPE;
  my_empno employees.employee_id%TYPE;
  -- %ROWTYPE can include all the columns in a table...
  emp_rec employees%ROWTYPE;
  -- ...or a subset of the columns, based on a cursor.
  CURSOR c1 IS
    SELECT department_id, department_name FROM departments;
  dept_rec c1%ROWTYPE;
  -- Could even make a %ROWTYPE with columns from multiple tables.
  CURSOR c2 IS
    SELECT employee_id, email, employees.manager_id, location_id FROM employees, departments
      WHERE employees.department_id = departments.department_id;
  join_rec c2%ROWTYPE;
BEGIN
  -- We know EMP_REC can hold a row from the EMPLOYEES table.
  SELECT * INTO emp_rec FROM employees
    WHERE ROWNUM < 2;
  -- We can refer to the fields of EMP_REC using column names
  -- from the EMPLOYEES table.
  IF emp_rec.department_id = 20 AND emp_rec.last_name = 'JOHNSON' THEN
    emp_rec.salary := emp_rec.salary * 1.15;
  END IF;
END;

DECLARE
  dept_rec1 departments%ROWTYPE;
  dept_rec2 departments%ROWTYPE;
  CURSOR c1 IS
    SELECT department_id, location_id FROM departments;
  dept_rec3 c1%ROWTYPE;
BEGIN
  dept_rec1 := dept_rec2; -- allowed
-- dept_rec2 refers to a table, dept_rec3 refers to a cursor
-- dept_rec2 := dept_rec3; -- not allowed
END;

DECLARE
  dept_rec departments%ROWTYPE;
BEGIN
  SELECT * INTO dept_rec FROM departments
    WHERE department_id = 30 AND ROWNUM < 2;
END;

BEGIN
  -- We assign an alias (COMPLETE_NAME) to the expression value, because
  -- it has no column name.
  FOR item IN
    (SELECT first_name || ' ' || last_name complete_name FROM employees
       WHERE ROWNUM < 11)
  LOOP
    -- Now we can refer to the field in the record using this alias.
    dbms_output.put_line('Employee name: ' || item.complete_name);
  END LOOP;
END;

-- CREATE TABLE employees2 AS SELECT last_name FROM employees;
<< MAIN >>
DECLARE
  last_name VARCHAR2(10) := 'King';
  my_last_name VARCHAR2(10) := 'King';
BEGIN
  -- Deletes everyone, because both LAST_NAMEs refer to the column
  DELETE FROM employees2
    WHERE last_name = last_name;
  dbms_output.put_line('Deleted ' || SQL%ROWCOUNT || ' rows.');
  ROLLBACK;
  -- OK, column and variable have different names
  DELETE FROM employees2
    WHERE last_name = my_last_name;
  dbms_output.put_line('Deleted ' || SQL%ROWCOUNT || ' rows.');
  ROLLBACK;
  -- OK, block name specifies that 2nd LAST_NAME is a variable
  DELETE FROM employees2
    WHERE last_name = main.last_name;
  dbms_output.put_line('Deleted ' || SQL%ROWCOUNT || ' rows.');
  ROLLBACK;
END;

-- DROP TABLE employees2;
DECLARE
  FUNCTION dept_name(department_id IN NUMBER)
                    RETURN departments.department_name%TYPE IS
    department_name departments.department_name%TYPE;
  BEGIN
    -- DEPT_NAME.DEPARTMENT_NAME specifies the local variable
    -- instead of the table column
    SELECT department_name INTO dept_name.department_name FROM departments
      WHERE department_id = dept_name.department_id;
    RETURN department_name;
  END;
BEGIN
  FOR item IN
    (SELECT department_id FROM departments)
  LOOP
    dbms_output.put_line('Department: ' || dept_name(item.department_id));
  END LOOP;
END;

DECLARE
  a CHAR;
  b REAL;
BEGIN
  -- identifiers available here: a (CHAR), b
  DECLARE
    a INTEGER;
    c REAL;
  BEGIN
    -- identifiers available here: a (INTEGER), b, c
    NULL;
  END;
  DECLARE
    d REAL;
  BEGIN
    -- identifiers available here: a (CHAR), b, d
    NULL;
  END;
-- identifiers available here: a (CHAR), b
END;

<< outer >>
DECLARE
  birthdate DATE;
BEGIN
  DECLARE
    birthdate DATE;
  BEGIN
    IF birthdate = outer.birthdate THEN
      NULL;
    END IF;
  END;
END;

PROCEDURE check_credit(xxx REAL) IS
  rating NUMBER;
  FUNCTION valid(xxx REAL)
                RETURN BOOLEAN IS
    rating NUMBER;
  BEGIN
    IF check_credit.rating < 3 THEN
      NULL;
    END IF;
  END;
BEGIN
  NULL;
END;

DECLARE
  counter INTEGER;
BEGIN
  -- COUNTER is initially NULL, so 'COUNTER + 1' is also null.
  counter := counter + 1;
  IF counter IS NULL THEN
    dbms_output.put_line('Sure enough, COUNTER is NULL not 1.');
  END IF;
END;

DECLARE
  done BOOLEAN; -- DONE is initially NULL
  counter NUMBER := 0;
BEGIN
  done := FALSE; -- Assign a literal value
  WHILE done != TRUE -- Compare to a literal value
  LOOP
    counter := counter + 1;
    done := (counter > 500); -- If counter > 500, DONE = TRUE
  END LOOP;
END;

DECLARE
  emp_id employees.employee_id%TYPE := 100;
  emp_name employees.last_name%TYPE;
  wages NUMBER(7, 2);
BEGIN
  SELECT last_name, salary + (salary * nvl(commission_pct, 0)) INTO emp_name, wages FROM employees
    WHERE employee_id = emp_id;
  dbms_output.put_line('Employee ' || emp_name || ' might make ' || wages);
END;

DECLARE
  on_hand INTEGER := 0;
  on_order INTEGER := 100;
BEGIN
  -- Does not cause divide-by-zero error; evaluation stops after 1st expr.
  IF (on_hand = 0) OR ( (on_order / on_hand) < 5) THEN
    dbms_output.put_line('There are no more widgets left!');
  END IF;
END;

DECLARE
  PROCEDURE assert(assertion VARCHAR2,
                   truth BOOLEAN) IS
  BEGIN
    IF truth IS NULL THEN
      dbms_output.put_line('Assertion ' || assertion || ' is unknown (NULL)');
    ELSIF truth = TRUE THEN
      dbms_output.put_line('Assertion ' || assertion || ' is TRUE');
    ELSE
      dbms_output.put_line('Assertion ' || assertion || ' is FALSE');
    END IF;
  END;
BEGIN
  assert('2 + 2 = 4', 2 + 2 = 4);
  assert('10 > 1', 10 > 1);
  assert('10 <= 1', 10 <= 1);
  assert('5 BETWEEN 1 AND 10', 5 BETWEEN 1 AND 10);
  assert('NULL != 0', NULL != 0);
  assert('3 IN (1,3,5)', 3 IN (1, 3, 5));
  assert(q'{'A'\r\n < 'Z'}', 'A' < 'Z');
  assert(q'{'baseball' LIKE '%all%'}', 'baseball' LIKE '%all%');
  assert(q'{'suit' || 'case' = 'suitcase'}', 'suit' || 'case' = 'suitcase');
END;

DECLARE
  fraction BINARY_FLOAT := 1 / 3;
BEGIN
  IF fraction = 11 / 33 THEN
    dbms_output.put_line('Fractions are equal (luckily!)');
  END IF;
END;

DECLARE
  done BOOLEAN;
BEGIN
  -- Each WHILE loop is equivalent
  done := FALSE;
  WHILE done = FALSE LOOP
    done := TRUE;
  END LOOP;
  done := FALSE;
  WHILE NOT (done = TRUE) LOOP
    done := TRUE;
  END LOOP;
  done := FALSE;
  WHILE NOT done LOOP
    done := TRUE;
  END LOOP;
END;

DECLARE
  grade CHAR(1) := 'B';
  appraisal VARCHAR2(20);
BEGIN
  appraisal := CASE grade
                 WHEN 'A' THEN 'Excellent'
                 WHEN 'B' THEN 'Very Good'
                 WHEN 'C' THEN 'Good'
                 WHEN 'D' THEN 'Fair'
                 WHEN 'F' THEN 'Poor'
               ELSE 'No such grade'
               END;
  dbms_output.put_line('Grade ' || grade || ' is ' || appraisal);
END;

DECLARE
  grade CHAR(1) := 'B';
  appraisal VARCHAR2(120);
  id NUMBER := 8429862;
  attendance NUMBER := 150;
  min_days CONSTANT NUMBER := 200;
  FUNCTION attends_this_school(id NUMBER)
                              RETURN BOOLEAN IS
  BEGIN
    RETURN TRUE;
  END;
BEGIN
  appraisal := CASE
                 WHEN attends_this_school(id) = FALSE THEN 'N/A - Student not enrolled'
                 -- Have to put this condition early to detect
                 -- good students with bad attendance
                 WHEN grade = 'F' OR attendance < min_days THEN 'Poor (poor performance or bad attendance)'
                 WHEN grade = 'A' THEN 'Excellent'
                 WHEN grade = 'B' THEN 'Very Good'
                 WHEN grade = 'C' THEN 'Good'
                 WHEN grade = 'D' THEN 'Fair'
               ELSE 'No such grade'
               END;
  dbms_output.put_line('Result for student ' || id || ' is ' || appraisal);
END;

DECLARE
  x NUMBER := 5;
  y NUMBER := NULL;
BEGIN
  IF x != y THEN -- yields NULL, not TRUE 
    dbms_output.put_line('x != y'); -- not executed
  ELSIF x = y THEN -- also yields NULL
    dbms_output.put_line('x = y');
  ELSE
    dbms_output.put_line(q'{Can't tell if x and y are equal or not...}');
  END IF;
END;

DECLARE
  a NUMBER := NULL;
  b NUMBER := NULL;
BEGIN
  IF a = b THEN -- yields NULL, not TRUE 
    dbms_output.put_line('a = b'); -- not executed
  ELSIF a != b THEN -- yields NULL, not TRUE 
    dbms_output.put_line('a != b'); -- not executed
  ELSE
    dbms_output.put_line(q'{Can't tell if two NULLs are equal}');
  END IF;
END;

DECLARE
  null_string VARCHAR2(80) := TO_CHAR('');
  address VARCHAR2(80);
  zip_code VARCHAR2(80) := SUBSTR(address, 25, 0);
  NAME VARCHAR2(80);
  valid BOOLEAN := (NAME != '');
BEGIN
  NULL;
END;

DECLARE
  the_manager VARCHAR2(40);
  NAME employees.last_name%TYPE;
BEGIN
  -- NULL is a valid argument to DECODE. In this case, manager_id is null
  -- and the DECODE function returns 'nobody'.
  SELECT DECODE (manager_id, NULL, 'nobody', 'somebody'), last_name INTO the_manager, NAME FROM employees
    WHERE employee_id = 100;
  dbms_output.put_line(NAME || ' is managed by ' || the_manager);
END;

DECLARE
  string_type VARCHAR2(60);
  old_string string_type%TYPE := 'Apples and oranges';
  my_string string_type%TYPE := 'more apples';
  -- NULL is a valid argument to REPLACE, but does not match
  -- anything so no replacement is done.
  new_string string_type%TYPE := REPLACE (old_string, NULL, my_string);
BEGIN
  dbms_output.put_line('Old string = ' || old_string);
  dbms_output.put_line('New string = ' || new_string);
END;

DECLARE
  string_type VARCHAR2(60);
  dashed string_type%TYPE := 'Gold-i-locks';
  -- When the substitution text for REPLACE is NULL,
  -- the text being replaced is deleted.
  NAME string_type%TYPE := REPLACE (dashed, '-', NULL);
BEGIN
  dbms_output.put_line('Dashed name = ' || dashed);
  dbms_output.put_line('Dashes removed = ' || NAME);
END;

PL/SQL Code after Obfuscation

Notice that comments are gone, names have been scrambled. The obfuscator uses a special list provided by the user to define names that should be preserved, ensuring that public interfaces and accesses to public libraries remain valid. If you obfuscate a set of PL/SQL source files simultaneously, only the public symbols they collectively offer will be sensibly named in the source files.

declare
Oil100 number(4);
iil101 boolean;
Oil10i real(7, 2);
iil10i constant number := 5000.00;
Oil10l iil110.Oil111%type;
iil111 Oil11i%rowtype;
iil11l Oil1i0%rowtype;
iil1i0 number(11, 2);
Oil1i1 constant number(4) := 3;
iil1ii constant number(5, 2) := 500.00;
begin
Oil1il := iil1il * Oil1l0;
iil1l1 := false;
Oil1li := iil1li * 0.10;
Oil1ll := iili00(Oili01, iili01, Oili0i) - iili0l;
select Oili10, iili10, Oili11, iili1i from Oili1l;
select iili1l * 0.10 into Oil1li from Oilii0
where iilii1 = Oili01;
select Oiliii(iili10) into Oil10i from Oili1l;
iiliii(7788, Oil10i);
for Oiliil in
(select * from Oilii0)
loop
iilil0.Oilil1('First name = ' || Oiliil.iilil1);
iilil0.Oilil1('Last name = ' || Oiliil.Oilili);
end loop;
iilill := iil111.Oill00;
fetch Oil1i0 into iil11l;
select iill00 into iil1i0 from Oill01
where iill0i = Oil1i1
for update of iill00;
if iil1i0 >= iil1ii then
update Oill01
set iill00 = iill00 - iil1ii
where iill0i = Oil1i1;
else
insert into Oill0l
values (Oil1i1, iil1i0, 'Insufficient funds');
end if;
commit;
case
when iill0l = 'square' then
Oill10 := iill11 * iill11;
when iill0l = 'circle' then
begin
Oill10 := Oill1i * (iill1i * iill1i);
iilil0.Oilil1('Value is not exact because pi is irrational.');
end;
when iill0l = 'rectangle' then
Oill10 := length * Oill1l;
else
begin
iilil0.Oilil1('No formula to calculate area of a' || iill0l);
raise iilli0;
end;
end case;
for Oilli1 in 1 .. 500 loop
insert into iilli1
values (Oilli1, Oillii(Oilli1));
end loop;
end;

declare
iili1l Oili1l.iili10%type := 0;
iillil Oili1l.Oilll0%type;
Oilili Oili1l.Oili10%type;
iilll0 Oili1l.Oilll1%type := 7499;
begin
select Oilll0 into iillil from Oili1l
where Oilll1 = iilll0;
while iili1l <= 2500 loop
select iili10, Oilll0, Oili10 into iili1l, iillil, Oilili from Oili1l
where Oilll1 = iillil;
end loop;
insert into Oill0l
values (null, iili1l, Oilili);
commit;
exception
when iillli then
insert into Oill0l
values (null, null, 'Not found');
commit;
end;

declare
procedure Oillll(Oili01 number) is
Oil1li real;
iillll exception;
begin
select ll0000 * 0.15 into Oil1li from Oili1l
where Oilll1 = Oili01;
if Oil1li is null then
raise iillll;
else
update Ol0000
set il0001 = il0001 + Oil1li
where Oilll1 = Oili01;
end if;
exception
when iillll then
commit;
end Oillll;
begin
Oil1li := 1.0;
end;

create package ll0001 as
procedure Ol000i(Oilll1 number,
                 Oili10 char);
procedure il000i(Oili01 number);
end ll0001;

create package body ll0001 as
procedure Ol000i(Oilll1 number,
                 Oili10 char) is
begin
insert into Oili1l
values (Oilll1, Oili10);
end Ol000i;
procedure il000i(Oili01 number) is
begin
delete from Oili1l
where Oilll1 = Oili01;
end il000i;
end ll0001;

declare
type ll000l is table of Ol000l;
il0010 Ol000l;
function ll0010(Oili11 date)
               return ll000l is
begin
null;
end;
begin
il0010 := ll0010('10-NOV-98') (5);
end;

declare
type Ol0011 is
record (il0011 smallint,
        ll001i smallint);
type Ol001i is
record (il001l date,
        ll001l Ol0011,
        Ol00i0 varchar2(20),
        il00i0 varchar2(50));
begin
null;
end;

create type ll00i1 as
object (Ol00i1 integer(5),
        il00ii real,
        ll00ii varchar2(10),
        member procedure open(Ol00il in real),
        member procedure il00il(Oilli1 in integer),
        member procedure close(Oilli1 in integer,
                               Ol00il out real),
        member procedure ll00l0(Oilli1 in integer,
                                Ol00il in real),
        member procedure Ol00l0(Oilli1 in integer,
                                Ol00il in real),
        member function il00l1(Oilli1 in integer)
                              return real);

declare
iillll exception;
begin
if ll00l1 is null then
raise iillll;
end if;
Oil1li := (iili1l * 0.10) + (ll00l1 * 0.15);
exception
when iillll then
null;
end;

declare
Ol00li number;
begin
Ol00li := 10E127;
end;

declare
il00li binary_float := ll00ll(2.0);
Ol00ll binary_double := ll00ll(2.0);
begin
null;
end;

begin
il0100 := 'Hello, world!';
il0100 := 'XYZ Corporation';
il0100 := '10-NOV-91';
il0100 := 'He said "Life is like licking honey from a thorn."';
il0100 := '$1,000,000';
il0100 := 'I''m a string, you''re a string.';
ll0100 := 'I''m a string, you''re a string.';
Ol0101('select index_name from user_indexes where status = ''INVALID''');
il0101 := 'where col_value like ''%''';
end;

declare
ll010i date := date '1998-12-25';
Ol010i timestamp := timestamp '1997-10-22 13:01:01';
il010l timestamp with time zone := timestamp '1997-01-31 09:26:56.66 +02:00';
ll010l interval year to month := interval '3-2' year to month;
Ol0110 interval day to second := interval '5 04:03:02.01' day to second;
begin
null;
end;

declare
il0110 boolean;
Oill1i number := 3.1415926;
iill1i number := 15;
Oill10 number;
begin
if 2 + 2 = 4 then
il0110 := true;
end if;
Oill10 := Oill1i * iill1i ** 2;
end;

declare
iil10i constant real := 5000.00;
ll0111 constant integer := 366;
Ol0111 constant boolean := false;
il011i integer default 40;
ll011i integer := 0;
Ol011l integer(4) not null := 9999;
il011l number(7, 2);
ll01i0 il011l%type;
name varchar2(20) := 'JoHn SmItH';
Ol01i0 name%type := il01i1(name);
ll01i1 name%type := Ol01ii(name);
il01ii name%type := ll01il(name);
Ol01il il01l0.ll01l0%type;
Ol01l1 Oilii0.iilii1%type;
iil11l Oilii0%rowtype;
cursor Oil1i0 is
select il01l1, ll01li from Ol01li;
iil111 Oil1i0%rowtype;
cursor il01ll is
select iilii1, ll01ll, Oilii0.Ol0i00, il0i00 from Oilii0, Ol01li
where Oilii0.il01l1 = Ol01li.il01l1;
ll0i01 il01ll%rowtype;
begin
select * into iil11l from Oilii0
where Ol0i01 < 2;
if iil11l.il01l1 = 20 and iil11l.Oilili = 'JOHNSON' then
iil11l.iili1l := iil11l.iili1l * 1.15;
end if;
end;

declare
il0i0i Ol01li%rowtype;
ll0i0i Ol01li%rowtype;
cursor Oil1i0 is
select il01l1, il0i00 from Ol01li;
Ol0i0l Oil1i0%rowtype;
begin
il0i0i := ll0i0i;
end;

declare
iil111 Ol01li%rowtype;
begin
select * into iil111 from Ol01li
where il01l1 = 30 and Ol0i01 < 2;
end;

begin
for il0i0l in
(select iilil1 || ' ' || Oilili ll0i10 from Oilii0
 where Ol0i01 < 11)
loop
Ol0i10.il0i11('Employee name: ' || il0i0l.ll0i10);
end loop;
end;

<< main >>
declare
Oilili varchar2(10) := 'King';
ll0i11 varchar2(10) := 'King';
begin
delete from Ol0i1i
where Oilili = Oilili;
Ol0i10.il0i11('Deleted ' || sql%rowcount || ' rows.');
rollback;
delete from Ol0i1i
where Oilili = ll0i11;
Ol0i10.il0i11('Deleted ' || sql%rowcount || ' rows.');
rollback;
delete from Ol0i1i
where Oilili = main.Oilili;
Ol0i10.il0i11('Deleted ' || sql%rowcount || ' rows.');
rollback;
end;

declare
function il0i1i(il01l1 in number)
               return Ol01li.ll01li%type is
ll01li Ol01li.ll01li%type;
begin
select ll01li into il0i1i.ll01li from Ol01li
where il01l1 = il0i1i.il01l1;
return ll01li;
end;
begin
for il0i0l in
(select il01l1 from Ol01li)
loop
Ol0i10.il0i11('Department: ' || il0i1i(il0i0l.il01l1));
end loop;
end;

declare
ll0i1l char;
Ol0i1l real;
begin
declare
ll0i1l integer;
il0ii0 real;
begin
null;
end;
declare
ll0ii0 real;
begin
null;
end;
end;

<< outer >>
declare
Ol0ii1 date;
begin
declare
Ol0ii1 date;
begin
if Ol0ii1 = outer.Ol0ii1 then
null;
end if;
end;
end;

procedure il0ii1(ll0iii real) is
Ol0iii number;
function il0iil(ll0iii real)
               return boolean is
Ol0iii number;
begin
if il0ii1.Ol0iii < 3 then
null;
end if;
end;
begin
null;
end;

declare
ll0iil integer;
begin
ll0iil := ll0iil + 1;
if ll0iil is null then
Ol0i10.il0i11('Sure enough, COUNTER is NULL not 1.');
end if;
end;

declare
Ol0il0 boolean;
ll0iil number := 0;
begin
Ol0il0 := false;
while Ol0il0 != true loop
ll0iil := ll0iil + 1;
Ol0il0 := (ll0iil > 500);
end loop;
end;

declare
Oili01 Oilii0.iilii1%type := 100;
il0il0 Oilii0.Oilili%type;
Oil1ll number(7, 2);
begin
select Oilili, iili1l + (iili1l * ll0il1(Ol0il1, 0)) into il0il0, Oil1ll from Oilii0
where iilii1 = Oili01;
Ol0i10.il0i11('Employee ' || il0il0 || ' might make ' || Oil1ll);
end;

declare
il0ili integer := 0;
ll0ili integer := 100;
begin
if (il0ili = 0) or ( (ll0ili / il0ili) < 5) then
Ol0i10.il0i11('There are no more widgets left!');
end if;
end;

declare
procedure Ol0ill(il0ill varchar2,
                 ll0l00 boolean) is
begin
if ll0l00 is null then
Ol0i10.il0i11('Assertion ' || il0ill || ' is unknown (NULL)');
elsif ll0l00 = true then
Ol0i10.il0i11('Assertion ' || il0ill || ' is TRUE');
else
Ol0i10.il0i11('Assertion ' || il0ill || ' is FALSE');
end if;
end;
begin
Ol0ill('2 + 2 = 4', 2 + 2 = 4);
Ol0ill('10 > 1', 10 > 1);
Ol0ill('10 <= 1', 10 <= 1);
Ol0ill('5 BETWEEN 1 AND 10', 5 between 1 and 10);
Ol0ill('NULL != 0', null != 0);
Ol0ill('3 IN (1,3,5)', 3 in (1, 3, 5));
Ol0ill('''A'' < ''Z''', 'A' < 'Z');
Ol0ill('''baseball'' LIKE ''%all%''', 'baseball' like '%all%');
Ol0ill('''suit'' || ''case'' = ''suitcase''', 'suit' || 'case' = 'suitcase');
end;

declare
Ol0l00 binary_float := 1 / 3;
begin
if Ol0l00 = 11 / 33 then
Ol0i10.il0i11('Fractions are equal (luckily!)');
end if;
end;

declare
Ol0il0 boolean;
begin
Ol0il0 := false;
while Ol0il0 = false loop
Ol0il0 := true;
end loop;
Ol0il0 := false;
while not (Ol0il0 = true) loop
Ol0il0 := true;
end loop;
Ol0il0 := false;
while not Ol0il0 loop
Ol0il0 := true;
end loop;
end;

declare
il0l01 char(1) := 'B';
ll0l01 varchar2(20);
begin
ll0l01 := case il0l01
          when 'A' then 'Excellent'
          when 'B' then 'Very Good'
          when 'C' then 'Good'
          when 'D' then 'Fair'
          when 'F' then 'Poor'
          else 'No such grade'
          end;
Ol0i10.il0i11('Grade ' || il0l01 || ' is ' || ll0l01);
end;

declare
il0l01 char(1) := 'B';
ll0l01 varchar2(120);
Ol0l0i number := 8429862;
il0l0i number := 150;
ll0l0l constant number := 200;
function Ol0l0l(Ol0l0i number)
               return boolean is
begin
return true;
end;
begin
ll0l01 := case
          when Ol0l0l(Ol0l0i) = false then 'N/A - Student not enrolled'
          when il0l01 = 'F' or il0l0i < ll0l0l then 'Poor (poor performance or bad attendance)'
          when il0l01 = 'A' then 'Excellent'
          when il0l01 = 'B' then 'Very Good'
          when il0l01 = 'C' then 'Good'
          when il0l01 = 'D' then 'Fair'
          else 'No such grade'
          end;
Ol0i10.il0i11('Result for student ' || Ol0l0i || ' is ' || ll0l01);
end;

declare
il00li number := 5;
Ol00ll number := null;
begin
if il00li != Ol00ll then
Ol0i10.il0i11('x != y');
elsif il00li = Ol00ll then
Ol0i10.il0i11('x = y');
else
Ol0i10.il0i11('Can''t tell if x and y are equal or not...');
end if;
end;

declare
ll0i1l number := null;
Ol0i1l number := null;
begin
if ll0i1l = Ol0i1l then
Ol0i10.il0i11('a = b');
elsif ll0i1l != Ol0i1l then
Ol0i10.il0i11('a != b');
else
Ol0i10.il0i11('Can''t tell if two NULLs are equal');
end if;
end;

declare
il0l10 varchar2(80) := ll0l10('');
Ol0l11 varchar2(80);
il0l11 varchar2(80) := ll0l1i(Ol0l11, 25, 0);
name varchar2(80);
il0iil boolean := (name != '');
begin
null;
end;

declare
Ol0l1i varchar2(40);
name Oilii0.Oilili%type;
begin
select decode(Ol0i00, null, 'nobody', 'somebody'), Oilili into Ol0l1i, name from Oilii0
where iilii1 = 100;
Ol0i10.il0i11(name || ' is managed by ' || Ol0l1i);
end;

declare
il0l1l varchar2(60);
ll0l1l il0l1l%type := 'Apples and oranges';
Ol0li0 il0l1l%type := 'more apples';
il0li0 il0l1l%type := replace(ll0l1l, null, Ol0li0);
begin
Ol0i10.il0i11('Old string = ' || ll0l1l);
Ol0i10.il0i11('New string = ' || il0li0);
end;

declare
il0l1l varchar2(60);
ll0li1 il0l1l%type := 'Gold-i-locks';
name il0l1l%type := replace(ll0li1, '-', null);
begin
Ol0i10.il0i11('Dashed name = ' || ll0li1);
Ol0i10.il0i11('Dashes removed = ' || name);
end;

Obfuscated Symbol Cross Reference

The obfuscator produces a cross reference mapping obfuscated symbols to the orginal symbols, so that obfuscated code in the field can still be decoded if necessary. In fact, by reversing this map, the obfuscator can unobfuscate the code (of course, it cannot restore the comments). Of course, you can only do this, if you have the map.

### Obfuscated Identifiers ###
AVG -> Oiliii
Bank_Account -> ll00i1
DBMS_OUTPUT -> iilil0
Employee -> Ol000l
INITCAP -> ll01il
LOWER -> Ol01ii
MeetingTyp -> Ol001i
NO_DATA_FOUND -> iillli
PROGRAM_ERROR -> iilli0
PUT_LINE -> Oilil1
ROWNUM -> Ol0i01
SQRT -> Oillii
SUBSTR -> ll0l1i
Staff -> ll000l
TO_CHAR -> ll0l10
TimeRec -> Ol0011
UPPER -> il01i1
a -> ll0i1l
account_id -> iill0i
accounts -> Oill01
acct -> Oil1i1
acct_balance -> iil1i0
acct_id -> Ol011l
acct_number -> Ol00i1
address -> Ol0l11
adjust_salary -> iiliii
amount -> Ol00il
appraisal -> ll0l01
area -> Oill10
assert -> Ol0ill
assertion -> il0ill
attendance -> il0l0i
attends_this_school -> Ol0l0l
award_bonus -> Oillll
b -> Ol0i1l
bal -> iill00
balance -> il00ii
birthdate -> Ol0ii1
bonus -> Oil1li
books -> iil110
c -> il0ii0
c1 -> Oil1i0
c2 -> il01ll
check_credit -> il0ii1
comm -> ll0000
comm_missing -> iillll
commission -> ll00l1
commission_pct -> Ol0il1
complete_name -> ll0i10
counter -> ll0iil
credit -> il011l
credit_limit -> iil10i
curr_bal -> il00l1
current_salary -> iil1li
d -> ll0ii0
d1 -> ll010i
dashed -> ll0li1
date_held -> il001l
dbms_output -> Ol0i10
debit -> ll01i0
debit_amt -> iil1ii
deductions -> iili0l
department_id -> il01l1
department_name -> ll01li
departments -> Ol01li
deposit -> ll00l0
dept -> Oil11i
dept_name -> il0i1i
dept_rec -> iil111
dept_rec1 -> il0i0i
dept_rec2 -> ll0i0i
dept_rec3 -> Ol0i0l
deptno -> Oill00
done -> Ol0il0
duration -> ll001l
email -> ll01ll
emp -> Oili1l
emp_actions -> ll0001
emp_id -> Oili01
emp_name -> il0il0
emp_rec -> iil11l
employee_count -> ll011i
employee_id -> iilii1
employees -> Oilii0
employees2 -> Ol0i1i
empno -> Oilll1
ename -> Oili10
fire_employee -> il000i
first_name -> iilil1
fraction -> Ol0l00
func_call -> Ol0101
grade -> il0l01
gross_pay -> iili00
hire_employee -> Ol000i
hiredate -> Oili11
hours -> il0011
hours_worked -> il011i
i1 -> ll010l
i2 -> Ol0110
id -> Ol0l0i
in_stock -> iil101
init_name -> il01ii
item -> il0i0l
job -> iili1i
join_rec -> ll0i01
last_name -> Oilili
location -> Ol00i0
location_id -> il0i00
lower_name -> ll01i1
manager_id -> Ol0i00
max_days_in_year -> ll0111
mgr -> Oilll0
mgr_num -> iillil
min_days -> ll0l0l
minutes -> ll001i
my_deptno -> iilill
my_empno -> Ol01l1
my_last_name -> ll0i11
my_sal -> Oil10i
my_string -> Ol0li0
my_title -> Oil10l
n -> Ol00li
new_hires -> ll0010
new_string -> il0li0
null_string -> il0l10
num -> Oilli1
nvl -> ll0il1
old_string -> ll0l1l
on_hand -> il0ili
on_order -> ll0ili
ot_hrs -> Oili0i
part_no -> Oil100
pay -> il0001
payroll -> Ol0000
pi -> Oill1i
price -> iil1il
purpose -> il00i0
put_line -> il0i11
radius -> iill1i
rating -> Ol0iii
roots -> iilli1
sal -> iili10
salary -> iili1l
shape -> iill0l
side -> iill11
some_condition -> il0110
someone -> Oiliil
sqrt -> ll00ll
st_hrs -> iili01
staffer -> il0010
starting_empno -> iilll0
status -> ll00ii
str -> il0100
string_type -> il0l1l
string_var -> ll0100
t1 -> Ol010i
t2 -> il010l
tax -> Oil1il
tax_rate -> Oil1l0
temp -> Oill0l
the_manager -> Ol0l1i
the_trigger -> Ol01il
title -> Oil111
trigger_name -> ll01l0
truth -> ll0l00
upper_name -> Ol01i0
urban_legend -> Ol0111
user_triggers -> il01l0
valid -> il0iil
valid_id -> iil1l1
verify_acct -> il00il
wages -> Oil1ll
where_clause -> il0101
width -> Oill1l
withdraw -> Ol00l0
x -> il00li
xxx -> ll0iii
y -> Ol00ll
zip_code -> il0l11
For more information: info@semanticdesigns.com    Follow us at Twitter: @SemanticDesigns

PL/SQL Obfuscation
Example