oracle utl_encode,Oracle 加密package解密(unwrap)

最近碰到一个问题,需要解密几个使用oracle wrap加密过的程序包,查了下,已经有很多可用的程序,支持10g,11g,连12c都支持。查找过程中,花了点时间研究了下解密的过程,简单记录一下。

1. 解密算法解析

要解密,首先要了解加密的原理,在这几个版本的oracle中,解密的理论依据都来源于 "The oracle hacker's handbook" by David Litchfield 这本书,书中介绍了wrap的过程:

将源码使用lz压缩算法压缩,得到压缩串lzstr

对压缩串做sha-1运算,得到40位的哈希串shstr

将哈希串与压缩串拼在一起,shstr+lzstr

将得到的字符串再按字节执行字典表替换

再执行base64编码,就得到了加密的字符串

如果要解密加密以后的字符串,按照以上加密的过程反向推算,可以发现最关键的就是第4步中的字典表替换过程,要解决这个问题,我们就要得到这个替换的字典表,幸运的是,经过测试发现,oracle的这个字典表是固定的按字节映射,所以:

我们可以通过wrap加密一个简单字符串,比如create PACKAGE a0,得到加密后的密文,然后执行一次base64解码,就能得到替换后的字符串,代码如下

select substr(utl_encode.base64_decode(utl_raw.cast_to_raw(

rtrim(substr(wrap_text,

instr(wrap.wrap,chr(10),1,20) + 1),chr(10)))),

41) from dual

这里说明一下,wrap以后的密文里,前面20行是没有用的,密文是从第21行开始的,第21行的前40个字符,是拼接的哈希串,从第41个字符开始才是字典表转换后的密文

本例中,得到的结果为

78DA0B7074F67674775548346000001185029E

对字符串create PACKAGE a0执行lz压缩,得到字典表转换前的字符串,本例中的结果为

308399B8F5339FF5BF5C5A2170A6A6F302E141

这样我们得到了转换前和转换后的字符串,就可以按字节建立起我们需要的字典表了,比如本例中:

转换前-转换后

30 78

83 DA

依次类推,我们就可以建立起这个字典表,因为一个字节从00-FF有256个字符,所以相应的这个典表应该有256条记录,这里我们只得到了其中的一部分,还需要选用不同的字符串,多次穷举运算,最终得到全部的记录。事实上,因为这个字典表是固定的,所以得到的结果可以重复使用。当然也可以直接使用别人提供的。

有了字典表,接下来的工作就比较简单了,按照加密过程反向运算即可:

从数据库中得到package的密文

base64解码

字典表反向映射

去掉前40位哈希串

lz解压缩

2. 解密程序源码

这里贴上部分源码,供参考

LZ解压和压缩的代码,使用java source过程,为了支持比较大的package,使用CLOB作为参数类型,经测试,可以支持较大的程序包

create or replace java source named CUX_UNWRAPPER

as

import java.io.BufferedReader;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.Reader;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.util.zip.Deflater;

import java.util.zip.InflaterInputStream;

import oracle.jdbc.OracleDriver;

import oracle.sql.CLOB;

public class UNWRAPPER {

//解压缩

public static CLOB Inflate(CLOB src) throws IOException, SQLException {

StringBuffer sb = new StringBuffer();

String s = src.stringValue();

try{

ByteArrayInputStream bis =

new ByteArrayInputStream(decodeHex(s.toCharArray()));

//ByteArrayInputStream bis = new ByteArrayInputStream(src);

InflaterInputStream iis = new InflaterInputStream(bis);

for (int c = iis.read(); c != -1; c = iis.read()) {

sb.append((char)c);

}

}catch (Exception e)

{

e.printStackTrace();

}

Connection conn = DriverManager.getConnection("jdbc:default:connection:");

CLOB clob = CLOB.createTemporary(conn, false, CLOB.DURATION_SESSION);

clob.setString(1, sb.toString());

return clob;

}

public static byte[] Deflate(String src, int quality) {

try {

byte[] tmp = new byte[src.length() + 100];

Deflater defl = new Deflater(quality);

defl.setInput(src.getBytes("UTF-8"));

defl.finish();

int cnt = defl.deflate(tmp);

byte[] res = new byte[cnt];

for (int i = 0; i < cnt; i++)

res = tmp;

return res;

} catch (Exception e) {

}

return null;

}

public static int toDigit(char ch, int index) {

int digit = Character.digit(ch, 16);

if (digit == -1) {

throw new RuntimeException("illegal hexadecimal character " + ch +

" at index " + index);

}

return digit;

}

//16进制字符串转字节数组

public static byte[] decodeHex(char[] data) {

int len = data.length;

if ((len & 0x01) != 0) {

throw new RuntimeException("odd number of characters ");

}

byte[] out = new byte[len >> 1];

for (int i = 0, j = 0; j < len; i++) {

int f = toDigit(data[j], j) << 4;

j++;

f = f | toDigit(data[j], j);

j++;

out[i] = (byte)(f & 0xFF);

}

return out;

}

}

/

--编译java source

alter java source CUX_UNWRAPPER compile

/

解密plsql主程序

--程序包体

create or replace package body cux_unwrapper is

function deflate(src in varchar2) return raw is

begin

return deflate(src, 6);

end;

--压缩

function deflate(src in varchar2, quality in number) return raw as

language java name 'UNWRAPPER.Deflate( java.lang.String, int ) return byte[]';

--解压缩

function inflate(src in clob) return clob as

language java name 'UNWRAPPER.Inflate( oracle.sql.CLOB ) return oracle.sql.CLOB';

--输出clob

procedure out_put(p_clob clob) is

l_len integer;

l_pos integer := 1;

l_chunk integer := 2000;

l_buffer varchar2(4000);

l_amount integer;

l_ins_pos integer := 1;

begin

l_len := dbms_lob.getlength(p_clob);

while l_pos < l_len loop

if l_len - l_pos < l_chunk then

l_ins_pos := l_len;

else

l_ins_pos := dbms_lob.instr(p_clob, chr(10), l_pos + l_chunk, 1);

end if;

l_amount := l_ins_pos - l_pos + 1;

dbms_lob.read(p_clob, l_amount, l_pos, l_buffer);

dbms_output.put_line(l_buffer);

l_pos := l_ins_pos + 1;

end loop;

end out_put;

--解密主程序

PROCEDURE unwrap(p_owner IN VARCHAR,

p_name IN VARCHAR,

p_type IN VARCHAR) AS

l_wrap varchar2(32767);

l_inf clob;

l_res clob;

l_src clob;

l_inflate varchar2(32767);

l_temp VARCHAR2(32767);

l_bt varchar2(32767);

l_text varchar2(32767);

l_char varchar2(2);

l_len number;

l_slen integer;

l_offset integer := 1;

l_chunk_len integer := 10080;

--解密字节对照字典表

l_dict varchar2(512) := '3D6585B318DBE287F152AB634BB5A05F' ||

'7D687B9B24C228678ADEA4261E03EB17' ||

'6F343E7A3FD2A96A0FE935561FB14D10' ||

'78D975F6BC4104816106F9ADD6D5297E' ||

'869E79E505BA84CC6E278EB05DA8F39F' ||

'D0A271B858DD2C38994C480755E4538C' ||

'46B62DA5AF322240DC50C3A1258B9C16' ||

'605CCFFD0C981CD4376D3C3A30E86C31' ||

'47F533DA43C8E35E1994ECE6A39514E0' ||

'9D64FA5915C52FCABB0BDFF297BF0A76' ||

'B449445A1DF0009621807F1A82394FC1' ||

'A7D70DD1D8FF139370EE5BEFBE09B977' ||

'72E7B254B72AC7739066200E51EDF87C' ||

'8F2EF412C62B83CDACCB3BC44EC06936' ||

'6202AE88FCAA4208A64557D39ABDE123' ||

'8D924A1189746B91FBFEC901EA1BF7CE';

l_sl varchar2(512) := '000102030405060708090A0B0C0D0E0F' ||

'101112131415161718191A1B1C1D1E1F' ||

'202122232425262728292A2B2C2D2E2F' ||

'303132333435363738393A3B3C3D3E3F' ||

'404142434445464748494A4B4C4D4E4F' ||

'505152535455565758595A5B5C5D5E5F' ||

'606162636465666768696A6B6C6D6E6F' ||

'707172737475767778797A7B7C7D7E7F' ||

'808182838485868788898A8B8C8D8E8F' ||

'909192939495969798999A9B9C9D9E9F' ||

'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF' ||

'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF' ||

'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF' ||

'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF' ||

'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF' ||

'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF';

cursor c_src is

SELECT src.line, src.text

FROM DBA_SOURCE src

WHERE owner = p_owner

AND Name = p_name

AND TYPE = p_type

order by line;

BEGIN

dbms_lob.createtemporary(lob_loc => l_inf,

cache => TRUE,

dur => dbms_lob.session);

--取得package密文

for rec in c_src loop

l_src := l_src || rtrim(rec.text);

end loop;

--out_put('source:');

l_src := rtrim(substr(l_src, instr(l_src, chr(10), 1, 20) + 1), chr(10));

l_src := replace(l_src, chr(10), '');

-- dbms_output.put_line('source:');

--l_src:=substr(l_src,41);

--base64解码

l_len := dbms_lob.getlength(l_src);

while l_offset < l_len loop

if (l_len - l_offset) < 10080 then

l_chunk_len := (l_len - l_offset);

end if;

l_temp := dbms_lob.substr(l_src, l_chunk_len, l_offset);

l_bt := utl_encode.base64_decode(utl_raw.cast_to_raw(l_temp));

if l_bt is not null then

if l_offset = 1 then

--去掉前40位哈希串

l_bt := substr(l_bt, 41);

end if;

-- l_wrap := l_wrap || l_bt;

--字典表转换

l_bt := utl_raw.translate(l_bt, l_sl, l_dict);

l_offset := l_offset + l_chunk_len;

-- l_inflate := l_inflate || l_bt;

dbms_lob.writeappend(l_inf, length(l_bt), l_bt);

end if;

end loop;

/* dbms_output.put_line('base:' || l_wrap);

dbms_output.put_line('');*/

--解压缩

l_res := inflate(l_inf);

out_put(l_res);

END unwrap;

END;

/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值