-
-
基于linker实现so加壳补充-------从dex中加载so
-
发表于: 2021-10-12 17:48 25670
-
之前的文章给大家展示了一个so文件加壳的整体方法,而且给了一个比较简单的例子,后来我实现了一下将so写在dex中然后通过hook关键函数的方式得到so的地址。但是在实际使用过程中我发现了一个问题,就是有些got表中的变量,通过plt调用使用了页对其指令,这就要求我们的so必须在一块对其的内存上,如下图,并且inline hook也好难实现,而且dex是以只读的方式加载到内存中的,我还需要hook掉关键函数改掉它的属性,基于这三个问题,写了这篇补充
1:so地址页对其&第一个PT_LOAD和第二个PT_LOAD之间有多余占位数据
2:实现libart.so中的inlinehook
3:hook dex加载函数更改只读权限
4: 修正getsoinfo函数
由于像上面那种变量或者函数(例如strlen函数)在使用过程中,存在需要取页开始的情况,我们就必须保证so在内存中的偏移%0x1000等于0,所以我之前设想的直接在dex末尾接so内容的方式就不可取了,而且我们附在dex末尾的so是linking view,数据都在相对于文件的偏移上面,而so的执行需要Execution View,代码中lr这种函数跳转用的都是相对于so起地址的物理地址偏移,所以强行用静态的so文件会使调用函数的地址不对,而我又不想再次使用mmap将已经加载到内存中的so,再搞出来单独装载,所以想了一个折中的办法,利用系统通过ElfReader::Load结束的soinfo直接将此so的Execution View 整体dump出来,这一步可以用frida也可以用ida,或者用我上篇文章写的装载函数(当然这种更好因为不会触发任何反调试,因为我没有调用init_arry和JNI_Onloade当中的函数),在演示demo中我采用了第一种方式(不想写装载了有点累...),用idc脚本从ida里面dump一段内存
搞下来的so直接复制粘贴到dex末尾即可,由于需要对其,所以需要补0,当然如果想不让人看出来就补乱码,后面so也应该加密,只有在使用的时候再解密,我这里没有加密,只是补0和正常的复制,最后把filesize和checksum等一系列参数补充完整即可
由于Execution View后面都是节头去掉的部分所以都是0.这样就能解决对齐的问题嘛?答案是可以的,因为在DexClassloader加载dex的路上的函数MapFileAtAddress,是用的页对其,所以我们补完0之后so也一定是页对其的
搞定完这里,把so和插件dex都写入一个b.txt,然后直接用DexClassLoader加载起来就好,期间我模仿之前寒冰老师出的ctf题的写法,将b.txt搞到了资源目录然后使用的时候再复制到cache目录。
这样这部分的准备工作就完成了。
那么同时又产生了第二个问题,就是如何定位我的真正so的首地址。既然我将so加到了dex的末尾,那么我就可以通过dex+偏移的方式来访问我的so,那么问题又来了如何搞到dex的首地址呢?这里有2种办法,第一种就是从maps文件里面搜索,但是这样又不是很优雅,最终没有采纳。我最后是借鉴了寒冰老师的脱壳的思路,在LoadClass的关键路径上hook来搞定,我选择了LoadMethod函数,它的第一个参数就是c层的DexFile对象,那么hook搞定呢,用现成的hook框架?感觉特征太多而且就失去了学习的价值,还是自己写一个来的实在,首先想一个,既然是hook LoadMethod那么是不是用got表hook,来简单的搞定,扫了好几眼发现got表种没有这个函数555...,那么就只能挑战linline hook了。
由于LoadMethod函数是导出函数,所以我们可以从libart.so的导出表里面找到它的地址,通过类型为PT_DYNAMIC的段就能找到,这里由于上篇文章已经介绍了elf的文件格式所以这里只贴一点代码不做过多的介绍
很容易就得到了导出表、符号表、字符串表的信息,接下来只需要模仿安卓源码中soinfo::gnu_lookup函数的写法来,获取我们的导出符号的函数地址就好,这里我直接将symbol_name.gnu_hash函数抽出来实现了一下,他这种计算hash的方式很简单,也就5行代码
这样最终得到了我们传入符号在符号表当中的索引,那么只要拿到当中的st_value就是地址偏移了
由于水平太低有点看不懂大佬的思路.......,只能先自己思考一下如何实现inline hook了,由此提出以下几个问题
1.首先构想一个整体的思路,如何实现每个我们需要hook的函数都能跳转到我们自己的函数执行?
这个问题可以用一条指令就是b一个地址,但是又有一个问题,这个地址是有范围要求的,如果不在同一个so当中很难满足这个界限要求,这一点在https://armconverter.com/ 中可以得到验证,所以这种方式是不可以的,那么就只剩一种方式了br或blr一个寄存器的值,根据网上资料(实在太菜大佬的文章只能看懂这一点)x17和x16是不经常使用的(我只在安卓源码art_jni_dlsym_lookup_stub中看到了使用),那么就需要赋值和br来替换函数的前几条指令代码如下,一共用了16位来跳转到我自己的代码,所以要把这四条指令保存下来,这里保存指令我用的全局变量不知道有没有更好的方式,这里面我用的blr因为我还要跳回来所以不能用br,用br不会保存x30寄存器,就会直接跳回上一级函数,就不会执行原函数了,但是这样又有一个问题,就是我这4条指令可能会覆盖x30的入栈指令那么在之后的x30恢复的时候就会无法恢复x30,所以我决定在x30的入栈指令之后执行我的操作,具体做法是扫描指令如果满足STX29, X30, [SP,#0x80]
这种类似的,才在他的后一位写入我们的跳转指令 ,如果直接扫到了最后,那么没办法了只能从开头开始,16进制和汇编的转换都可以从https://armconverter.com/ 当中找到这里就不在计算了,如下图
成功的跳到我写的函数,那么现在就构思如何写自己的壳函数了,有一个问题,就是arm64不能操作pc,所以我要如何控制回来的时候跳到哪里呢,我把目光盯向了x30,执行完RET这条汇编之后,pc会跳到x30处(也就是32位下的lr),那么只要我指定x30就好了,由于上面用了4条指令而我是在第2条指令跳转的,所以要把x30+8,这一段我发现如果不用裸函数写,他会自动加上对x30寄存器的保护,所以我选择了裸函数,之后就是对寄存器的保护就好了.
这里就是下一个问题了,我们如何执行我们自己的函数,又如何将原来的4条指令执行一遍,这里我选择了再做一层包装,再写一个跳转,这会就不用裸函数了,而且指令直接用b 相对地址
就可以了,因为是在同一个so当中,由于我不知道在内存中他俩谁在前面,所以写了个判断,而且恢复占用指令的代码也写在了这个包装函数里面
在这个包装函数中,主要有2方面的内容,于是简单的代码就出来了
1.执行我们在外面传入的hook函数
2.恢复占用的指令
这样一个简单的inlinehook框架就写完了,就在我迫不及待的去试一下的时候,发现有个大问题,就是单个hook是没啥问题,但是要同时hook2个函数就不行了,因为我用了大量的全局变量,新的输入会覆盖旧的输入,5555...,而我的需求正好是hook 2个函数,气死我了,那么只能做一个思考了,如何完善这个框架,而且尽量做到不大改,因为上面太多了,那么既用了全局变量,就只能以数组的形式存储使用了,直接用一个全局变量ns存储我们的索引。
那么在使用的时候又有一个问题,就是调用顺序可能和我们自己写的hook的顺序不太一样,所以如何确定我们调用的函数属于哪一个索引又是一个问题,这里我直接用之前的x30来确定,因为x30来自与原函数,所以与原函数不会差距太大,用这个来判断调用的函数属于哪个索引
那么这里我们的框架就完善了,其实还有个问题我没有解决,就是如何更改参数,我这里只能先用栈来解决更改参数的问题(没想好如何解决是个败笔),那么就可以完成我们的hook来获得dex首地址了,hook写在init里面就好
那么现在就只剩下一个问题了,就是如何将dex加载进内存中,这里模仿寒冰老师的ctf赛题,java代码直接拿过来用就好,就是上面第一节的代码,但是这里有一个问题,DexClassLoader是以只读的方式将dex文件加载到内存中,我们需要可读可写可执行,所以需要改权限,但是这里又遇到了一个问题,调用mprotect的时候直接报错,Permission denied
,我也不知道为啥在这里卡了好久,最后在 https://blog.csdn.net/earbao/article/details/120308836 中找到了答案,这里就要求我们取hook MapFileAtAddress将其第三个函数改为PROT_WRITE|PROT_READ|PROT_EXEC
,这就是我刚才要优化我的inlinehook 框架的原因,我hook了2个函数一个是LoadMethod和MapFileAtAddress,更改代码如下
这样程序就设计完成了,我试没有任何问题,我兴高采烈地去找大佬试一下,没想到打脸了,直接就崩溃了,得知他是bullhead之后,我就拿起了我的82年的Nexus 5x,调试了一下,发现获得soinfo指针那里直接返回了一个特别大的值,哎,看来不同型号的手机linker不一样,我打开ida易看果然,不同型号的手机linker中,__dl_g_soinfo_handles_map
偏移不一样,我上篇文章用的是偏移,所以会有这个问题,如下图,左面是0xfa460右面是0xfd460
那么这就很烦了,好难受,上篇文章的内容就有局限性了,只能针对某一型号的手机,那是不可以的,我要解决问题,就还是从soinfo_from_handle这个inline函数入手,这会就看一看他是如何实现的,eee...,看不懂,还是看汇编吧,还是汇编友好一点。
还是汇编简单一点,这部分内容就很容易理解了,只要拿到最后的x0就好,那么他做了什么呢
1.首先取了dl_g_soinfo_handles_map的值
2.然后将其中的值与他后面一个字节取余操作
3.取上一步结果乘8然后取首先取了dl_g_soinfo_handles_map的值中的值加这个偏移
4.取2次地址中的值并且加0x18
5.最后再取一次它其中地址的值就是soninfo指针了
翻译成最终c代码如下,这样就能解决刚才的不同手机的适配问题了
那么又产生了下一个问题,就是如何拿到dl_g_soinfo_handles_map。dl_g_soinfo_handles_map是linker中的符号的值,但不是导出符号的值,基于此我没有在动态段中找到它对应的的符号,如下图只有寥寥的几个符号
那怎么办呢,我想到了可以通过节头表索引,直接打开/system/bin/linker64文件读它的节头表,比读段简单了许多,代码如下
这样就完成了上面4个问题的解决,比较基础,主要是对elf文件格式的一个解析,我只是提供了一个例子,没有对数据进行加密,正常情况下我觉得还应该对dex中的so信息进行加密保护效果才更强,而且我的hook貌似不稳定,又崩溃的几率,感谢大家观看
static main(void)
{
auto fp, begin, end, dexbyte;
fp
=
fopen(
"d:\\1.so"
,
"wb"
);
begin
=
0x0000007366280000
;
end
=
begin
+
0x0036000
;
for
( dexbyte
=
begin; dexbyte < end;dexbyte
+
+
)
{
fputc(Byte(dexbyte), fp);
}
}
static main(void)
{
auto fp, begin, end, dexbyte;
fp
=
fopen(
"d:\\1.so"
,
"wb"
);
begin
=
0x0000007366280000
;
end
=
begin
+
0x0036000
;
for
( dexbyte
=
begin; dexbyte < end;dexbyte
+
+
)
{
fputc(Byte(dexbyte), fp);
}
}
copyAssetAndWrite(
"b.txt"
,getApplicationContext());
....
DexClassLoader loader
=
new DexClassLoader(path,
"/sdcard"
,
"/sdcard"
,context.getClassLoader());
copyAssetAndWrite(
"b.txt"
,getApplicationContext());
....
DexClassLoader loader
=
new DexClassLoader(path,
"/sdcard"
,
"/sdcard"
,context.getClassLoader());
char line[
1024
];
int
*
startr;
int
*
end;
int
n
=
1
;
FILE
*
fp
=
fopen(
"/proc/self/maps"
,
"r"
);
while
(fgets(line, sizeof(line), fp)) {
/
/
从maps种扫描libart.so的地址
if
(strstr(line, libname) ) {
__android_log_print(
6
,
"r0ysue"
,"");
if
(n
=
=
1
){
startr
=
reinterpret_cast<
int
*
>(strtoul(strtok(line,
"-"
), NULL,
16
));
end
=
reinterpret_cast<
int
*
>(strtoul(strtok(NULL,
" "
), NULL,
16
));
}
else
{
strtok(line,
"-"
);
end
=
reinterpret_cast<
int
*
>(strtoul(strtok(NULL,
" "
), NULL,
16
));
}
n
+
+
;
}
}
size_t gnu_nbucket_
=
0
;
/
/
skip symndx
uint32_t gnu_maskwords_
=
0
;
uint32_t gnu_shift2_
=
0
;
ElfW(Addr)
*
gnu_bloom_filter_
=
nullptr;
uint32_t
*
gnu_bucket_
=
nullptr;
uint32_t
*
gnu_chain_
=
nullptr;
/
/
导出表
4
项
int
phof
=
0
;
Elf64_Ehdr header;
memcpy(&header,startr,sizeof(Elf64_Ehdr));
uint64 rel
=
0
;
size_t size
=
0
;
long
*
plt
=
nullptr;
char
*
strtab_
=
nullptr;
Elf64_Sym
*
symtab_
=
nullptr;
Elf64_Phdr cc;
memcpy (&cc,((char
*
)(startr)
+
header.e_phoff),sizeof(Elf64_Phdr));
for
(
int
y
=
0
;y<header.e_phnum;y
+
+
){
memcpy(&cc, (char
*
) (startr)
+
header.e_phoff
+
sizeof(Elf64_Phdr)
*
y, sizeof(Elf64_Phdr));
if
(cc.p_type
=
=
6
) {
/
/
程序头偏移
phof
=
cc.p_paddr
-
cc.p_offset;
}
}
for
(
int
y
=
0
;y<header.e_phnum;y
+
+
){
memcpy(&cc, (char
*
) (startr)
+
header.e_phoff
+
sizeof(Elf64_Phdr)
*
y, sizeof(Elf64_Phdr));
if
(cc.p_type
=
=
2
) {
/
/
p_type
=
2
代表找到了动态段
Elf64_Dyn dd;
for
(y
=
0
;y
=
=
0
||dd.d_tag!
=
0
;y
+
+
) {
memcpy(&dd, (char
*
) (startr)
+
cc.p_offset
+
y
*
sizeof(Elf64_Dyn)
+
0x1000
,
sizeof(Elf64_Dyn));
if
(dd.d_tag
=
=
0x6ffffef5
){
/
/
找到了 DT_GNU_HASH段也就是导出表
gnu_nbucket_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
0
];
/
/
skip symndx
gnu_maskwords_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
2
];
gnu_shift2_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
3
];
gnu_bloom_filter_
=
reinterpret_cast<ElfW(Addr)
*
>((char
*
)startr
+
dd.d_un.d_ptr
+
16
-
phof);
gnu_bucket_
=
reinterpret_cast<uint32_t
*
>(gnu_bloom_filter_
+
gnu_maskwords_);
/
/
amend chain
for
symndx
=
header[
1
]
gnu_chain_
=
reinterpret_cast<uint32_t
*
>( gnu_bucket_
+
gnu_nbucket_
-
reinterpret_cast<uint32_t
*
>(
(char
*
) startr
+
dd.d_un.d_ptr
-
phof)[
1
]);
}
if
(dd.d_tag
=
=
5
){
/
/
得到字符串表的首地址
strtab_
=
reinterpret_cast< char
*
>((char
*
) startr
+
dd.d_un.d_ptr
-
phof);
}
if
(dd.d_tag
=
=
6
){
/
/
得到符号表的首地址
symtab_
=
reinterpret_cast<Elf64_Sym
*
>((
(char
*
) startr
+
dd.d_un.d_ptr
-
phof));
}
}
}
}
char line[
1024
];
int
*
startr;
int
*
end;
int
n
=
1
;
FILE
*
fp
=
fopen(
"/proc/self/maps"
,
"r"
);
while
(fgets(line, sizeof(line), fp)) {
/
/
从maps种扫描libart.so的地址
if
(strstr(line, libname) ) {
__android_log_print(
6
,
"r0ysue"
,"");
if
(n
=
=
1
){
startr
=
reinterpret_cast<
int
*
>(strtoul(strtok(line,
"-"
), NULL,
16
));
end
=
reinterpret_cast<
int
*
>(strtoul(strtok(NULL,
" "
), NULL,
16
));
}
else
{
strtok(line,
"-"
);
end
=
reinterpret_cast<
int
*
>(strtoul(strtok(NULL,
" "
), NULL,
16
));
}
n
+
+
;
}
}
size_t gnu_nbucket_
=
0
;
/
/
skip symndx
uint32_t gnu_maskwords_
=
0
;
uint32_t gnu_shift2_
=
0
;
ElfW(Addr)
*
gnu_bloom_filter_
=
nullptr;
uint32_t
*
gnu_bucket_
=
nullptr;
uint32_t
*
gnu_chain_
=
nullptr;
/
/
导出表
4
项
int
phof
=
0
;
Elf64_Ehdr header;
memcpy(&header,startr,sizeof(Elf64_Ehdr));
uint64 rel
=
0
;
size_t size
=
0
;
long
*
plt
=
nullptr;
char
*
strtab_
=
nullptr;
Elf64_Sym
*
symtab_
=
nullptr;
Elf64_Phdr cc;
memcpy (&cc,((char
*
)(startr)
+
header.e_phoff),sizeof(Elf64_Phdr));
for
(
int
y
=
0
;y<header.e_phnum;y
+
+
){
memcpy(&cc, (char
*
) (startr)
+
header.e_phoff
+
sizeof(Elf64_Phdr)
*
y, sizeof(Elf64_Phdr));
if
(cc.p_type
=
=
6
) {
/
/
程序头偏移
phof
=
cc.p_paddr
-
cc.p_offset;
}
}
for
(
int
y
=
0
;y<header.e_phnum;y
+
+
){
memcpy(&cc, (char
*
) (startr)
+
header.e_phoff
+
sizeof(Elf64_Phdr)
*
y, sizeof(Elf64_Phdr));
if
(cc.p_type
=
=
2
) {
/
/
p_type
=
2
代表找到了动态段
Elf64_Dyn dd;
for
(y
=
0
;y
=
=
0
||dd.d_tag!
=
0
;y
+
+
) {
memcpy(&dd, (char
*
) (startr)
+
cc.p_offset
+
y
*
sizeof(Elf64_Dyn)
+
0x1000
,
sizeof(Elf64_Dyn));
if
(dd.d_tag
=
=
0x6ffffef5
){
/
/
找到了 DT_GNU_HASH段也就是导出表
gnu_nbucket_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
0
];
/
/
skip symndx
gnu_maskwords_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
2
];
gnu_shift2_
=
reinterpret_cast<uint32_t
*
>((char
*
)startr
+
dd.d_un.d_ptr
-
phof)[
3
];
gnu_bloom_filter_
=
reinterpret_cast<ElfW(Addr)
*
>((char
*
)startr
+
dd.d_un.d_ptr
+
16
-
phof);
gnu_bucket_
=
reinterpret_cast<uint32_t
*
>(gnu_bloom_filter_
+
gnu_maskwords_);
/
/
amend chain
for
symndx
=
header[
1
]
gnu_chain_
=
reinterpret_cast<uint32_t
*
>( gnu_bucket_
+
gnu_nbucket_
-
reinterpret_cast<uint32_t
*
>(
(char
*
) startr
+
dd.d_un.d_ptr
-
phof)[
1
]);
}
if
(dd.d_tag
=
=
5
){
/
/
得到字符串表的首地址
strtab_
=
reinterpret_cast< char
*
>((char
*
) startr
+
dd.d_un.d_ptr
-
phof);
}
if
(dd.d_tag
=
=
6
){
/
/
得到符号表的首地址
symtab_
=
reinterpret_cast<Elf64_Sym
*
>((
(char
*
) startr
+
dd.d_un.d_ptr
-
phof));
}
}
}
}
char
*
name_
=
symname;
uint32_t h
=
5381
;
const uint8_t
*
name
=
reinterpret_cast<const uint8_t
*
>(name_);
while
(
*
name !
=
0
) {
h
+
=
(h <<
5
)
+
*
name
+
+
;
/
/
h
*
33
+
c
=
h
+
h
*
32
+
c
=
h
+
h <<
5
+
c
}
/
/
实现symbol_name.gnu_hash
int
index
=
0
;
uint32_t h2
=
h >> gnu_shift2_;
uint32_t bloom_mask_bits
=
sizeof(ElfW(Addr))
*
8
;
uint32_t word_num
=
(h
/
bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word
=
gnu_bloom_filter_[word_num];
n
=
gnu_bucket_[h
%
gnu_nbucket_];
/
/
模仿安卓源码直接抄
do {
Elf64_Sym
*
s
=
symtab_
+
n;
char
*
sb
=
strtab_
+
s
-
>st_name;
if
(strcmp(sb ,reinterpret_cast<const char
*
>(name_))
=
=
0
) {
break
;
}
}
while
((gnu_chain_[n
+
+
] &
1
)
=
=
0
);
char
*
name_
=
symname;
uint32_t h
=
5381
;
const uint8_t
*
name
=
reinterpret_cast<const uint8_t
*
>(name_);
while
(
*
name !
=
0
) {
h
+
=
(h <<
5
)
+
*
name
+
+
;
/
/
h
*
33
+
c
=
h
+
h
*
32
+
c
=
h
+
h <<
5
+
c
}
/
/
实现symbol_name.gnu_hash
int
index
=
0
;
uint32_t h2
=
h >> gnu_shift2_;
uint32_t bloom_mask_bits
=
sizeof(ElfW(Addr))
*
8
;
uint32_t word_num
=
(h
/
bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word
=
gnu_bloom_filter_[word_num];
n
=
gnu_bucket_[h
%
gnu_nbucket_];
/
/
模仿安卓源码直接抄
do {
Elf64_Sym
*
s
=
symtab_
+
n;
char
*
sb
=
strtab_
+
s
-
>st_name;
if
(strcmp(sb ,reinterpret_cast<const char
*
>(name_))
=
=
0
) {
break
;
}
}
while
((gnu_chain_[n
+
+
] &
1
)
=
=
0
);
void
*
mysym
=
(char
*
)startr
+
sb
-
>st_value
-
phof
void
*
mysym
=
(char
*
)startr
+
sb
-
>st_value
-
phof
int
s
=
0
;
for
(n
=
0
;
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n)!
=
0xd65f03c0
;n
=
n
+
4
){
int
code
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n);
if
(code>>
32
=
=
0xa9
&&(code&
0xfff
)
=
=
0xbfd
){
__android_log_print(
6
,
"r0ysue"
,
"%x"
,n);
s
=
1
;
break
;
}
}
if
(s
=
1
){
n
=
n
+
4
;
}
else
{
n
=
0
;
}
one
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n);
two
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
4
);
three[ns
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
8
);
four[]
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
12
);
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n)
=
0x58000051
;
/
/
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
4
)
=
0xd63f0220
;
*
(
long
*
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
8
)
=
reinterpret_cast<
long
*
>(myloadmethod);
/
/
自己函数的地址
int
s
=
0
;
for
(n
=
0
;
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n)!
=
0xd65f03c0
;n
=
n
+
4
){
int
code
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n);
if
(code>>
32
=
=
0xa9
&&(code&
0xfff
)
=
=
0xbfd
){
__android_log_print(
6
,
"r0ysue"
,
"%x"
,n);
s
=
1
;
break
;
}
}
if
(s
=
1
){
n
=
n
+
4
;
}
else
{
n
=
0
;
}
one
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n);
two
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
4
);
three[ns
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
8
);
four[]
=
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
12
);
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n)
=
0x58000051
;
/
/
*
(
int
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
4
)
=
0xd63f0220
;
*
(
long
*
*
)((char
*
)startr
+
sb
-
>st_value
-
phof
+
n
+
8
)
=
reinterpret_cast<
long
*
>(myloadmethod);
/
/
自己函数的地址
void __attribute((naked)) myloadmethod(){
asm(
"add x30,x30,8"
);
/
/
指定跳回去的位置
asm(
"sub SP, SP, #0x100"
);
/
/
申请栈
asm(
"stp X29, X30, [SP,#0x10]"
);
/
/
保护寄存器
asm(
"stp X0, X1, [SP,#0x20]"
);
asm(
"stp X2, X3, [SP,#0x30]"
);
asm(
"stp X4, X5, [SP,#0x40]"
);
asm(
"stp X6, X7, [SP,#0x50]"
);
asm(
"stp X8, X9, [SP,#0x60]"
);
asm(
"stp X10, X11, [SP,#0x70]"
);
asm(
"stp X12, X13, [SP,#0x80]"
);
asm(
"stp X14, X15, [SP,#0x90]"
);
asm(
"ldp X16, X17, [SP,#0x10]"
);
/
/
保存x30到x17,当然这里用mov或者ldr都行我复制粘贴省事了
asm(
"mov X16,SP"
);
/
/
这里参数处理我们想好就先用栈替代
asm(
"mov x0,x0"
);
/
/
占位,会改成BL _Z4pltsv,也就是我们自己写的第二个函数
asm(
"ldp X8, X9, [SP,#0x60]"
);
/
/
寄存器恢复
asm(
"ldp X10, X11, [SP,#0x70]"
);
asm(
"ldp X12, X13, [SP,#0x80]"
);
asm(
"ldp X14, X15, [SP,#0x90]"
);
asm(
"ldp X0, X1, [SP,#0x20]"
);
asm(
"ldp X2, X3, [SP,#0x30]"
);
asm(
"ldp X4, X5, [SP,#0x40]"
);
asm(
"ldp X6, X7, [SP,#0x50]"
);
asm(
"ldp X29, X30, [SP,#0x10]"
);
asm(
"add SP, SP, #0x100"
);
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
1
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
2
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
3
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
4
条指令
asm(
"RET"
);
}
void __attribute((naked)) myloadmethod(){
asm(
"add x30,x30,8"
);
/
/
指定跳回去的位置
asm(
"sub SP, SP, #0x100"
);
/
/
申请栈
asm(
"stp X29, X30, [SP,#0x10]"
);
/
/
保护寄存器
asm(
"stp X0, X1, [SP,#0x20]"
);
asm(
"stp X2, X3, [SP,#0x30]"
);
asm(
"stp X4, X5, [SP,#0x40]"
);
asm(
"stp X6, X7, [SP,#0x50]"
);
asm(
"stp X8, X9, [SP,#0x60]"
);
asm(
"stp X10, X11, [SP,#0x70]"
);
asm(
"stp X12, X13, [SP,#0x80]"
);
asm(
"stp X14, X15, [SP,#0x90]"
);
asm(
"ldp X16, X17, [SP,#0x10]"
);
/
/
保存x30到x17,当然这里用mov或者ldr都行我复制粘贴省事了
asm(
"mov X16,SP"
);
/
/
这里参数处理我们想好就先用栈替代
asm(
"mov x0,x0"
);
/
/
占位,会改成BL _Z4pltsv,也就是我们自己写的第二个函数
asm(
"ldp X8, X9, [SP,#0x60]"
);
/
/
寄存器恢复
asm(
"ldp X10, X11, [SP,#0x70]"
);
asm(
"ldp X12, X13, [SP,#0x80]"
);
asm(
"ldp X14, X15, [SP,#0x90]"
);
asm(
"ldp X0, X1, [SP,#0x20]"
);
asm(
"ldp X2, X3, [SP,#0x30]"
);
asm(
"ldp X4, X5, [SP,#0x40]"
);
asm(
"ldp X6, X7, [SP,#0x50]"
);
asm(
"ldp X29, X30, [SP,#0x10]"
);
asm(
"add SP, SP, #0x100"
);
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
1
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
2
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
3
条指令
asm(
"mov x0,x0"
);
/
/
之前在原函数中占位的第
4
条指令
asm(
"RET"
);
}
int
code;
if
((
long
)myloadmethod>(
long
)plts){
int
off
=
(
long
)myloadmethod
-
(
long
)plts
+
48
;
code
=
0x97ffffff
-
off
/
4
;
}
else
{
int
off
=
(
long
)plts
-
(
long
)myloadmethod
-
48
;
code
=
off
/
4
|
0x94000000
;
}
*
(
int
*
)((char
*
)myloadmethod
+
52
)
=
reinterpret_cast<
int
>(code);
int
code;
if
((
long
)myloadmethod>(
long
)plts){
int
off
=
(
long
)myloadmethod
-
(
long
)plts
+
48
;
code
=
0x97ffffff
-
off
/
4
;
}
else
{
int
off
=
(
long
)plts
-
(
long
)myloadmethod
-
48
;
code
=
off
/
4
|
0x94000000
;
}
*
(
int
*
)((char
*
)myloadmethod
+
52
)
=
reinterpret_cast<
int
>(code);
赞赏
- [原创]Base64 编码原理 && 实现 23389
- [原创]Lsposed 技术原理探讨 && 基本安装使用 26341
- [原创]Unidbg-Linker部分源码分析(下) 28647
- [原创]Unidbg-Linker部分源码分析(上) 26135