另一个似乎使用ORA_HASH的系统。
如果它“似乎使用”ORA_HASH,那么进行一些反向工程并检查确切调用的内容以及函数的反汇编代码是有意义的。
然而,如果你想深入了解Oracle内部,则以下内容可能会有所帮助。
首先,你必须弄清楚调用了哪个内部C函数。为此,你可以在一个会话中执行一些长时间运行的代码。我运行了以下代码:
select avg(ora_hash(rownum)) id from
(select rownum from dual connect by rownum <= 1e4),
(select rownum from dual connect by rownum <= 1e4);
它也可以是PL/SQL代码,你只需要确保不断调用 ora_hash。
当程序运行时:
我在Windows上进行了测试,看起来ora_hash是...->evaopn2()->
evahash()->...。
现在让我们搜索evahash。我们非常幸运,因为官方网站上有一个标题文件
https://oss.oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash.h带有evahash的链接。
最后,这里是实际的C代码页面:
http://burtleburtle.net/bob/hash/evahash.html。
到目前为止一切都很顺利,我们记得如果我们将其构建为库(在Windows上是DLL),则可以在Oracle中使用外部C函数。例如,在我的Win x64上,如果我更改函数签名:
extern "C" ub4 hash( ub1 *k, ub4 length, ub4 initval)
这个函数可以在Oracle中成功执行。但是,正如你所见,它与Oracle中的ora_hash略有不同。这个函数接受值、其长度和initval(可以是种子),而Oracle中的signature是ora_hash(expr,max_bucket,seed_value)。
让我们来尝试测试Oracle。
SQL> select ora_hash(utl_raw.cast_to_raw('0'), power(2, 32) - 1, 0) oh1,
2 ora_hash('0', power(2, 32) - 1, 0) oh2,
3 ora_hash(0, power(2, 32) - 1, 0) oh3,
4 ora_hash(chr(0), power(2, 32) - 1, 0) oh4
5 from dual;
OH1 OH2 OH3 OH4
3517341953 3517341953 1475158189 4056412421
C
int main()
{
ub1 ta[] = {0};
ub1* t = ta;
cout << hash(t, 1, 0) << endl;
ub1 ta0[] = {'0'};
ub1* t0 = ta0;
cout << hash(t0, 1, 0) << endl;
return 0;
}
1843378377
4052366646
没有任何一个数字匹配。
那么问题是什么呢?
ora_hash接受几乎任何类型的参数(例如select ora_hash(sys.odcinumberlist(1,2,3)) from dual
),而C函数接受字节数组的值。这意味着在函数调用之前会发生一些转换。
因此,在使用上述C哈希函数之前,您必须弄清楚实际值在传递给它之前如何转换。
您可以使用IDA PRO + hex rays对Oracle二进制文件进行反向工程,但这可能需要数天时间。更不用说特定于平台的细节。
因此,如果您想模仿ora_hash,最简单的方法是安装Oracle Express Edition并使用它来调用ora_hash。
我希望这很有趣。祝你好运。
更新
ora_hash和dbms_utility.get_hash_value可以相互映射(请参见https://jonathanlewis.wordpress.com/2009/11/21/ora_hash-function/)。
SQL> select dbms_utility.get_hash_value('0', 0 + 1, 1e6 + 1) ha1,
2 ora_hash('0', 1e6, 0) + 1 ha2
3 from dual;
HA1 HA2
338437 338437
如果我们展开dbms_utility的包体,我们会看到以下声明。
function get_hash_value(name varchar2, base number, hash_size number)
return number is
begin
return(icd_hash(name, base, hash_size));
end;
并且
function icd_hash(name varchar2,
base binary_integer,
hash_size binary_integer) return binary_integer;
pragma interface(c, icd_hash);
让我们搜索icd_hash
,我们可以发现它被映射到_psdhsh
(https://yurichev.com/blog/50/)。现在是时候反汇编oracle.exe并从中提取_psdhsh
的代码了。也许我明年会花一些时间在这上面。