加密so文件中指定的函数
前言
上一篇文章中详细分析了对so文件中自定义section的加密,这一篇来分析下对so文件中自定义函数的加密
原文地址:http://bbs.pediy.com/showthread.php?t=191649
0×1
需要加密的函数是 : Java_com_example_shelldemo2_MainActivity_getString
0×2
加密
a、整个加密过程中最重要的便是在文件中找到指定函数的位置,然后去做加密处理,最后将加密过后的数据写入原位置
b、从一个ELF动态链接库文件中,根据已知的函数名称,找到相应的函数起始地址的过程如下:
从ELF的header找到文件的偏移
ehdr->e_phoff。找到d_tag为
PT_DYNAMIC的
pargram header。
一个phdr对应一个
Segment。包含了好几个节
从这个Segment开始位置找到名为:
DT_DYNAMIC
的节,然后从这个节中找到
Elf32_Sym
这个一个结构
根据结构的成员变量
st_name,来和函数名称比较,相符,就用
st_value和
st_size两个变量找到函数的数据
c、结合代码详细分析此流程
①、main函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
int
main
(
int
argc
,
char
*
*
argv
)
{
char
secName
[
]
=
".text"
;
char
funcName
[
]
=
"Java_com_example_shelldemo2_MainActivity_getString"
;
char
*
content
=
NULL
;
int
fd
,
i
;
Elf32_Off
secOff
;
funcInfo
info
;
if
(
argc
<
2
)
{
puts
(
"Usage: shellAdder2 libxxx.so .(section) function"
)
;
return
-
1
;
}
fd
=
open
(
argv
[
1
]
,
O_RDWR
)
;
if
(
fd
<
0
)
{
printf
(
"open %s failed\n"
,
argv
[
1
]
)
;
goto
_error
;
}
/*
根据section名称 找到section,返回值是section table的sh_offset
*/
secOff
=
findTargetSectionAddr
(
fd
,
secName
)
;
if
(
secOff
==
-
1
)
{
printf
(
"Find section %s failed\n"
,
secName
)
;
goto
_error
;
}
/*
根据函数名称,找到函数信息,返回的是一个结构体,包含函数数据的偏移地址和大小
*/
if
(
getTargetFuncInfo
(
fd
,
funcName
,
&info
)
==
-
1
)
{
printf
(
"Find function %s failed\n"
,
funcName
)
;
goto
_error
;
}
/*
根据函数的偏移地址和大小,去读取函数,然后做加密处理
*/
content
=
(
char
*
)
malloc
(
info
.
st_size
)
;
if
(
content
==
NULL
)
{
puts
(
"Malloc space failed"
)
;
goto
_error
;
}
lseek
(
fd
,
info
.
st_value
-
1
,
SEEK_SET
)
;
if
(
read
(
fd
,
content
,
info
.
st_size
)
!=
info
.
st_size
)
{
puts
(
"Malloc space failed"
)
;
goto
_error
;
}
/*
将函数数据加密处理
*/
for
(
i
=
0
;
i
<
info
.
st_size
-
1
;
i
++
)
{
content
[
i
]
=
~
content
[
i
]
;
}
/*
加密后,写回原位置
*/
lseek
(
fd
,
info
.
st_value
,
SEEK_SET
)
;
if
(
write
(
fd
,
content
,
info
.
st_size
)
!=
info
.
st_size
)
{
puts
(
"Write modified content to .so failed"
)
;
goto
_error
;
}
puts
(
"Complete!"
)
;
_error
:
free
(
content
)
;
close
(
fd
)
;
return
0
;
}
|
②、findTargetSectionAddr 函数,根据section名称找到对应的section位置,这里就不做详细介绍了,上一篇中已经详细提到过。
③、
getTargetFuncInfo 函数,根据函数名称,寻找函数地址及大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
static
char
getTargetFuncInfo
(
int
fd
,
const
char
*
funcName
,
funcInfo
*
info
)
{
char
flag
=
-
1
,
*
dynstr
;
int
i
;
Elf32_Sym
funSym
;
Elf32_Phdr
phdr
;
Elf32_Off
dyn_off
;
Elf32_Word
dyn_size
,
dyn_strsz
;
Elf32_Dyn
dyn
;
Elf32_Addr
dyn_symtab
,
dyn_strtab
,
dyn_hash
;
unsigned
funHash
,
nbucket
,
nchain
,
funIndex
;
lseek
(
fd
,
ehdr
.
e_phoff
,
SEEK_SET
)
;
//elf header中e_phoff表示的是程序头的偏移
/*
循环遍历,找到.dynamic这个section 并记录其大小和偏移
*/
for
(
i
=
0
;
i
<
ehdr
.
e_phnum
;
i
++
)
{
if
(
read
(
fd
,
&phdr
,
sizeof
(
Elf32_Phdr
)
)
!=
sizeof
(
Elf32_Phdr
)
)
{
//读取peogram header 保存在phdr中
puts
(
"Read segment failed"
)
;
goto
_error
;
}
if
(
phdr
.
p_type
==
PT_DYNAMIC
)
{
// PT_DYNAMIC 表示数组元素给出动态链接信息
dyn_size
=
phdr
.
p_filesz
;
// p_filesz 段在文件映像中所占的字节数
dyn_off
=
phdr
.
p_offset
;
// 从文件头到该段第一个字节的偏移
flag
=
0
;
printf
(
"Find section %s, size = 0x%x, addr = 0x%x\n"
,
".dynamic"
,
dyn_size
,
dyn_off
)
;
break
;
}
}
if
(
flag
)
{
puts
(
"Find .dynamic failed"
)
;
goto
_error
;
}
flag
=
0
;
lseek
(
fd
,
dyn_off
,
SEEK_SET
)
;
//定位到.dynamic
for
(
i
=
0
;
i
<
dyn_size
/
sizeof
(
Elf32_Dyn
)
;
i
++
)
{
if
(
read
(
fd
,
&dyn
,
sizeof
(
Elf32_Dyn
)
)
!=
sizeof
(
Elf32_Dyn
)
)
{
puts
(
"Read .dynamic information failed"
)
;
goto
_error
;
}
if
(
dyn
.
d_tag
==
DT_SYMTAB
)
{
//循环遍历,找到d_tag为DT_SYMATAB的一项, dyn此元素包含符号表的地址
dyn_symtab
=
dyn
.
d_un
.
d_ptr
;
//此 Elf32_Addr 对象代表程序的虚拟地址
flag
+=
1
;
printf
(
"Find .dynsym, addr = 0x%x\n"
,
dyn_symtab
)
;
}
if
(
dyn
.
d_tag
==
DT_HASH
)
{
//循环遍历,找到.hash
dyn_hash
=
dyn
.
d_un
.
d_ptr
;
flag
+=
2
;
printf
(
"Find .hash, addr = 0x%x\n"
,
dyn_hash
)
;
}
if
(
dyn
.
d_tag
==
DT_STRTAB
)
{
//找到.dynstr的位置
dyn_strtab
=
dyn
.
d_un
.
d_ptr
;
flag
+=
4
;
printf
(
"Find .dynstr, addr = 0x%x\n"
,
dyn_strtab
)
;
}
if
(
dyn
.
d_tag
==
DT_STRSZ
)
{
//找到.dynstr的大小
dyn_strsz
=
dyn
.
d_un
.
d_val
;
flag
+=
8
;
printf
(
"Find .dynstr size, size = 0x%x\n"
,
dyn_strsz
)
;
}
}
if
(
(
flag
&
0x0f
)
!=
0x0f
)
{
//判断是不是全部都找到了
puts
(
"Find needed .section failed\n"
)
;
goto
_error
;
}
dynstr
=
(
char
*
)
malloc
(
dyn_strsz
)
;
//根据大小申请内存空间
if
(
dynstr
==
NULL
)
{
puts
(
"Malloc .dynstr space failed"
)
;
goto
_error
;
}
lseek
(
fd
,
dyn_strtab
,
SEEK_SET
)
;
//定位到dyn_strtab 读取.dynstr里面的字符串值
if
(
read
(
fd
,
dynstr
,
dyn_strsz
)
!=
dyn_strsz
)
{
puts
(
"Read .dynstr failed"
)
;
goto
_error
;
}
// print_all(dynstr, dyn_strsz);
funHash
=
elfhash
(
funcName
)
;
//调用elf_hash函数,根据函数名计算一个索引值
printf
(
"Function %s hashVal = 0x%x\n"
,
funcName
,
funHash
)
;
lseek
(
fd
,
dyn_hash
,
SEEK_SET
)
;
//读取hash表中的nbucket
if
(
read
(
fd
,
&nbucket
,
4
)
!=
4
)
{
puts
(
"Read hash nbucket failed\n"
)
;
goto
_error
;
}
printf
(
"nbucket = %d\n"
,
nbucket
)
;
if
(
read
(
fd
,
&nchain
,
4
)
!=
4
)
{
//读取hash表中的nchain
puts
(
"Read hash nchain failed\n"
)
;
goto
_error
;
}
// printf("nchain = %d\n", nchain);
funHash
=
funHash
%
nbucket
;
printf
(
"funHash mod nbucket = %d \n"
,
funHash
)
;
lseek
(
fd
,
funHash
*
4
,
SEEK_CUR
)
;
if
(
read
(
fd
,
&funIndex
,
4
)
!=
4
)
{
puts
(
"Read funIndex failed\n"
)
;
goto
_error
;
}
lseek
(
fd
,
dyn_symtab
+
funIndex
*
sizeof
(
Elf32_Sym
)
,
SEEK_SET
)
;
//
if
(
read
(
fd
,
&funSym
,
sizeof
(
Elf32_Sym
)
)
!=
sizeof
(
Elf32_Sym
)
)
{
puts
(
"Read funSym failed"
)
;
goto
_error
;
}
if
(
strcmp
(
dynstr
+
funSym
.
st_name
,
funcName
)
!=
0
)
//循环,在chain表中查找需要的符号表
{
while
(
1
)
{
lseek
(
fd
,
dyn_hash
+
4
*
(
2
+
nbucket
+
funIndex
)
,
SEEK_SET
)
;
//在chain数组中定位
if
(
read
(
fd
,
&funIndex
,
4
)
!=
4
)
{
puts
(
"Read funIndex failed\n"
)
;
goto
_error
;
}
if
(
funIndex
==
0
)
{
puts
(
"Cannot find funtion!\n"
)
;
goto
_error
;
}
lseek
(
fd
,
dyn_symtab
+
funIndex
*
sizeof
(
Elf32_Sym
)
,
SEEK_SET
)
;
//根据索引,在syn_symtab中找到对应的结构体
if
(
read
(
fd
,
&funSym
,
sizeof
(
Elf32_Sym
)
)
!=
sizeof
(
Elf32_Sym
)
)
{
puts
(
"In FOR loop, Read funSym failed"
)
;
goto
_error
;
}
if
(
strcmp
(
dynstr
+
funSym
.
st_name
,
funcName
)
==
0
)
{
break
;
}
}
}
printf
(
"Find: %s, offset = 0x%x, size = 0x%x\n"
,
funcName
,
funSym
.
st_value
,
funSym
.
st_size
)
;
info
->
st_value
=
funSym
.
st_value
;
info
->
st_size
=
funSym
.
st_size
;
free
(
dynstr
)
;
return
0
;
_error
:
free
(
dynstr
)
;
return
-
1
;
}
|
该函数返回包含函数信息的结构体funcInfo
1
2
3
4
|
typedef
struct
_funcInfo
{
Elf32_Addr
st_value
;
Elf32_Word
st_size
;
}
funcInfo
;
|
st_value代表位置,
st_size
代表大小,
根据位置和大小 去读取函数的信息。
d、文字叙述:
根据
e_phoff找到第一个
program
header
1
2
3
4
5
6
7
8
9
10
11
|
typedef
struct
{
Elf32_Word
p_type
;
/*segment的类型:PT_LOAD = 1 可加载的段*/
Elf32_Off
p_offset
;
/*从文件头到该段第一个字节的偏移*/
Elf32_Addr
p_vaddr
;
/*该段第一个字节被放到内存中的虚拟地址*/
Elf32_Addr
p_paddr
;
/*在linux中这个成员没有任何意义,值与p_vaddr相同*/
Elf32_Word
p_filesz
;
/*该段在文件映像中所占的字节数*/
Elf32_Word
p_memsz
;
/*该段在内存映像中占用的字节数*/
Elf32_Word
p_flags
;
/*段标志*/
Elf32_Word
p_align
;
/*p_vaddr是否对齐*/
}
Elf32_phdr
;
|
根据
p_type 找到
p_type == PT_DYNAMIC 的这个
phdr [数组元素给出动态链接信息]
此段包含.
dynamic这个
section。
通过.
dynamic
这个里面的p_offset就能找到
Elf32_Dyn
这样一个结构体
1
2
3
4
5
6
7
|
typedef
struct
dynamic
{
Elf32_Sword
d_tag
;
//标识
union
{
Elf32_Sword
d_val
;
//表示一个整数值,可以有多种解释
Elf32_Addr
d_ptr
;
//代表程序的虚拟地址
}
d_un
;
}
Elf32_Dyn
;
|
根据
d_tag标识,找到标识为 d_tag == DT_SYMTAB、
d_tag == DT_HASH、
d_tag ==DT_STRTAB、
d_tag == DT_STRSZ、的这一系列section。
申请大小为d_uu.d_val的内存空间,为读取.dynstr做准备
可以借助readelf查看.dynstr这个节里的内容
可以知道.dynstr这个section中保存的正是所有函数的名称
通过函数名称,调用elfhash函数,计算出一个hash值
读取hash表里面的一些信息
读取.hash这个section的前四字节。保存在nbucket
读取.hash这个section的第4-8字节。保存在nchain
那么,这两个数值有什么用呢
这得从hash表的组织结构说起
hash表的结构如下:
nbucket
nchain
bucket[nbucket]
chain[nchain]
4部分,nbucket,nchain,bucket数组,chain数组
两个数组中都保存着符号表索引。
hash函数根据函数名称得到一个hash值,设为X,则bucket[X % nbucket]给出了一个索引,设为Y
该索引可用于符号表,也可用于chain表,如果符号表项不是所需要的,那么chain[Y]则给出了具有相同hash值的下一个符号表项
我们就可以根据chain这个类似于链的结构,一直向下搜索。直到找到所需的符号表项。或者chain项中包含值STN_UNDEF。
通过hash表,能够找到一系列的符号表
符号表结构如下
1
2
3
4
5
6
7
8
|
typedef
struct
{
Elf32_Word
st_name
;
//名称,索引到字符串表
Elf32_Addr
st_value
;
//给出相关联的符号的取值。依赖于具体的上下文.
Elf32_Word
st_size
;
//相关的尺寸大小
unsignedchar
st_info
;
//给出符号的类型和绑定属性.
unsignedchar
st_other
;
//该成员当前包含 0,其含义没有定义。
Elf32_Half
st_shndx
;
//给出相关的节区头部表索引。某些索引具有特殊含义。
}
Elf32_sym
;
|
通过st_name来判断找到的符号表正是我们需要的
所有有了下面这样一个循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
if
(
strcmp
(
dynstr
+
funSym
.
st_name
,
funcName
)
!=
0
)
{
while
(
1
)
{
lseek
(
fd
,
dyn_hash
+
4
*
(
2
+
nbucket
+
funIndex
)
,
SEEK_SET
)
;
if
(
read
(
fd
,
&
funIndex
,
4
)
!=
4
)
{
puts
(
"Read funIndex failed\n"
)
;
goto
_error
;
}
if
(
funIndex
==
0
)
{
puts
(
"Cannot find funtion!\n"
)
;
goto
_error
;
}
lseek
(
fd
,
dyn_symtab
+
funIndex
*
sizeof
(
Elf32_Sym
)
,
SEEK_SET
)
;
if
(
read
(
fd
,
&
funSym
,
sizeof
(
Elf32_Sym
)
)
!=
sizeof
(
Elf32_Sym
)
)
{
puts
(
"In FOR loop, Read funSym failed"
)
;
goto
_error
;
}
if
(
strcmp
(
dynstr
+
funSym
.
st_name
,
funcName
)
==
0
)
{
break
;
}
}
}
|
这个循环的发生是在bucket数组中没有找到我们需要的符号表才进入的。也就是说,这个过程其实是在遍历chain这个数组
lseek(fd, dyn_hash + 4 * (2 + nbucket + funIndex), SEEK_SET);
其中funIndex是函数的索引值
hash表前两项是
nbucket 和
nchain 两个int型数据。
还有一个int型数组bucket[] 长度是nbucket
所以,根据
dyn_hash + (2 + nbucket + funIndex) * 4 便实现了根据索引定位到chain数组中函数的位置
然后再去
dyn_symtab中找到对应的
Elf32_Sym结构
根据其中的
st_name来进行判断,循环查找
0×3
解密
a、解密的过程和加密过程基本上一致的,只是说我们必须得到内存中去寻找那一系列的地址。
大致过程是:
在内存中找到so文件被加载到的地址,记为base
寻找函数
修改内存页属性
解密函数数据
还原内存页属性
b、解密入口函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
void
init_getString
(
)
{
const
char
target_fun
[
]
=
"Java_com_example_shelldemo2_MainActivity_getString"
;
funcInfo
info
;
int
i
;
unsigned
int
npage
,
base
=
getLibAddr
(
)
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"base addr = 0x%x"
,
base
)
;
if
(
getTargetFuncInfo
(
base
,
target_fun
,
&info
)
==
-
1
)
{
print_debug
(
"Find Java_com_example_shelldemo2_MainActivity_getString failed"
)
;
return
;
}
npage
=
info
.
st_size
/
PAGE_SIZE
+
(
(
info
.
st_size
%
PAGE_SIZE
==
0
)
?
0
:
1
)
;
if
(
mprotect
(
(
void
*
)
(
(
base
+
info
.
st_value
)
/
PAGE_SIZE
*
PAGE_SIZE
)
,
npage
,
PROT_READ
|
PROT_EXEC
|
PROT_WRITE
)
!=
0
)
{
print_debug
(
"mem privilege change failed"
)
;
}
for
(
i
=
0
;
i
<
info
.
st_size
-
1
;
i
++
)
{
char
*
addr
=
(
char
*
)
(
base
+
info
.
st_value
-
1
+
i
)
;
*
addr
=
~
(
*
addr
)
;
}
if
(
mprotect
(
(
void
*
)
(
(
base
+
info
.
st_value
)
/
PAGE_SIZE
*
PAGE_SIZE
)
,
npage
,
PROT_READ
|
PROT_EXEC
)
!=
0
)
{
print_debug
(
"mem privilege change failed"
)
;
}
}
|
里面用到了getLibAddr,getTargetFuncInfo两个函数。
getLibAddr函数用来在程序的内存空间中找到so被加载到的位置。
getTargetFuncInfo函数是用来在程序内存空间中的so中找到需要解密的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
static
char
getTargetFuncInfo
(
unsigned
long
base
,
const
char
*
funcName
,
funcInfo
*
info
)
{
char
flag
=
-
1
,
*
dynstr
;
int
i
;
Elf32_Ehdr
*
ehdr
;
Elf32_Phdr
*
phdr
;
Elf32_Off
dyn_vaddr
;
Elf32_Word
dyn_size
,
dyn_strsz
;
Elf32_Dyn
*
dyn
;
Elf32_Addr
dyn_symtab
,
dyn_strtab
,
dyn_hash
;
Elf32_Sym
*
funSym
;
unsigned
funHash
,
nbucket
;
unsigned
*
bucket
,
*
chain
;
ehdr
=
(
Elf32_Ehdr
*
)
base
;
phdr
=
(
Elf32_Phdr
*
)
(
base
+
ehdr
->
e_phoff
)
;
// __android_log_print(ANDROID_LOG_INFO, "JNITag", "phdr = 0x%p, size = 0x%x\n", phdr, ehdr->e_phnum);
for
(
i
=
0
;
i
<
ehdr
->
e_phnum
;
++
i
)
{
// __android_log_print(ANDROID_LOG_INFO, "JNITag", "phdr = 0x%p\n", phdr);
if
(
phdr
->
p_type
==
PT_DYNAMIC
)
{
flag
=
0
;
print_debug
(
"Find .dynamic segment"
)
;
break
;
}
phdr
++
;
}
if
(
flag
)
goto
_error
;
dyn_vaddr
=
phdr
->
p_vaddr
+
base
;
dyn_size
=
phdr
->
p_filesz
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"dyn_vadd = 0x%x, dyn_size = 0x%x"
,
dyn_vaddr
,
dyn_size
)
;
flag
=
0
;
for
(
i
=
0
;
i
<
dyn_size
/
sizeof
(
Elf32_Dyn
)
;
++
i
)
{
dyn
=
(
Elf32_Dyn
*
)
(
dyn_vaddr
+
i
*
sizeof
(
Elf32_Dyn
)
)
;
if
(
dyn
->
d_tag
==
DT_SYMTAB
)
{
dyn_symtab
=
(
dyn
->
d_un
)
.
d_ptr
;
flag
+=
1
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find .dynsym section, addr = 0x%x\n"
,
dyn_symtab
)
;
}
if
(
dyn
->
d_tag
==
DT_HASH
)
{
dyn_hash
=
(
dyn
->
d_un
)
.
d_ptr
;
flag
+=
2
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find .hash section, addr = 0x%x\n"
,
dyn_hash
)
;
}
if
(
dyn
->
d_tag
==
DT_STRTAB
)
{
dyn_strtab
=
(
dyn
->
d_un
)
.
d_ptr
;
flag
+=
4
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find .dynstr section, addr = 0x%x\n"
,
dyn_strtab
)
;
}
if
(
dyn
->
d_tag
==
DT_STRSZ
)
{
dyn_strsz
=
(
dyn
->
d_un
)
.
d_val
;
flag
+=
8
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find strsz size = 0x%x\n"
,
dyn_strsz
)
;
}
}
if
(
(
flag
&
0x0f
)
!=
0x0f
)
{
print_debug
(
"Find needed .section failed\n"
)
;
goto
_error
;
}
dyn_symtab
+=
base
;
dyn_hash
+=
base
;
dyn_strtab
+=
base
;
dyn_strsz
+=
base
;
funHash
=
elfhash
(
funcName
)
;
funSym
=
(
Elf32_Sym
*
)
dyn_symtab
;
dynstr
=
(
char
*
)
dyn_strtab
;
nbucket
=
*
(
(
int
*
)
dyn_hash
)
;
bucket
=
(
int
*
)
(
dyn_hash
+
8
)
;
chain
=
(
unsigned
int
*
)
(
dyn_hash
+
4
*
(
2
+
nbucket
)
)
;
flag
=
-
1
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"hash = 0x%x, nbucket = 0x%x\n"
,
funHash
,
nbucket
)
;
for
(
i
=
bucket
[
funHash
%
nbucket
]
;
i
!=
0
;
i
=
chain
[
i
]
)
{
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find index = %d\n"
,
i
)
;
if
(
strcmp
(
dynstr
+
(
funSym
+
i
)
->
st_name
,
funcName
)
==
0
)
{
flag
=
0
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"Find %s\n"
,
funcName
)
;
break
;
}
}
if
(
flag
)
goto
_error
;
info
->
st_value
=
(
funSym
+
i
)
->
st_value
;
info
->
st_size
=
(
funSym
+
i
)
->
st_size
;
__android_log_print
(
ANDROID_LOG_INFO
,
"JNITag"
,
"st_value = %d, st_size = %d"
,
info
->
st_value
,
info
->
st_size
)
;
return
0
;
_error
:
return
-
1
;
}
|
可以看到,和加密过程中寻找函数地址是一样的。只是在定位过程中得计算偏移地址。
0×4
加密程序执行效果:
解密过程效果: