by devseed, 本贴论坛和我的博客同时发布
之前发的Galgame汉化中的逆向系列,大多是结合具体游戏来谈谈分析思路与汉化方法。大多数主机游戏可执行文件都是魔改的ELF
文件(比如说psv,ps4系统内核就是由FreeBSD修改而来的),不同于以x86
架构为主的pc平台,主机平台架构可谓是百花齐放:psp,ps2是mips
架构,ps3是powerpc
架构,ps4是x86
架构,psv是arm
架构,switch是arm64
架构。ps4的x86
架构修改方法和pc版类似,powerpc
架构的汇编暂时还没研究,mips
架构汇编相关的分析,今后有时间可能会更新教程。由于arm
指令一般情况下都是4字节定长(还有2字节的thumb
指令),mov
,ldw
等指令的立即数有范围限制,寻址方式多为相对于PC
的偏移寻址,这与x86
里立即数可以直接写VA地址(如FF 15
系列长跳转指令、b8
系列的mov
)有很大区别。因此这篇作为《galgame汉化中的逆向》系列教程的补充,来聊聊ELF结构、如何对ELF修改、Arm汇编寻址方法以及如何修改Arm汇编中字符串的指针。
一般来说主机游戏剧本、字库等都在资源文件(封包文件)中,可执行文件中只有少量的系统文本。这部分通常也不需要汉化比原字符长(因为系统文本一般没有太多含义,且汉语本身就比较简洁),因此直接替换二进制字符就行了。但是对于一些特列,即字库或剧本整体封在了可执行文件中,就要增加区段和用汇编修改指针把字符串重定向到新增区段中了。如psv、switch版的Gnosia,和psv、ps3版的WhiteAlbum2,修改eboot方法详见PSV版WA2汉化移植。
为了更直观地说明原理和看到运行结果,本篇教程将不以具体游戏为例,而是用arm
版helloword程序,讲解一下修改arm
、arm64
ELF
的方法。理论上适用于psv、swich、android平台的可执行文件或动态库,根据不同平台可能需要再转换为其对应的魔改ELF
格式。
本篇教程需要的工具:
参考网站:
https://www.mztn.org/dragon/arm6405str.html
http://armconverter.com/
安装脚本如下:
最简单的hello world程序代码:
交叉编译命令行:
运行命令行:
Linux系统的可执行文件和动态库为ELF
格式,与windows的pe
类似。里面的几个重要结构为:ELF header
、Section Header Table
‘、Program Header Table
。关于section
和segment
关系如下
ELF文件的段结构就是由段表决定的。
编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。
段表在ELF文件中的位置由ELF文件头的“e_shoff”成员决定的。
常用readelf
来查看elf结构,objdump
来查看反汇编,命令行如下:
关于添加新区段,可以自己手动修改ELF
文件头对应的索引增加区段,可以参见android so手动增加区段。我以前也写过windowsPE
增加区段的轮子simpledpack,这次就不再自己造轮子了,直接用lief
解析ELF
了。这个库逻辑挺清晰的:
lief.ELF.parse(path)-> LIEF::ELF::Binary
,解析elf,
lief.ELF.Section(name, type)
,创建新section
a. 添加区段并patch例子:
b. 扩容区段例子:
c. sym替换(类似于IAThook)
32位的arm
通常是通过当前PC
寄存器找到内存偏移表
地址,再通过表中偏移计算出最终地址,相当于两次对内存寻址找到最终地址。通常用add pc, Rd
或adr Rd, offset
,之后ldr Rd
来获得最终地址。内存偏移表
通常在一个函数的结尾处(BLX LR
函数返回后),每项存储4字节偏移。
thumb
指令为2字节定长,在psv的eboot和android的so里面会经常出现这种指令集。
这种情况下内存偏移表
存储的是相对于add Rd, pc
的偏移,即value_addr-pc-4
,下面通过实例来分析:
可见ida里面机器码为文件中的字节顺序,指令转义为了ldr
伪指令;而objdump
中机器码为小端整数,指令转换成直接对应汇编的样子,因此学习arm汇编看objdump
比较直观。
13f78:4825 ldr r0, [pc, #148]
为访问内存偏移表
内容指令,偏移表地址13f78h+4+#148(94h)=14010h
,即把偏移表存储的偏移34BF0h
载入r0
;偏移表里的内容为.text:00014010 F0 4B 03 00
,是相对于13f7c: 4478 add r0, pc
的偏移;13f7c:4478 add r0, pc
,最终地址为34BF0h+13F7Ch+4=48B70h
。
arm
4字节ldr
,adr
伪指令转换为add register, pc [#offset]
。机器码的存储格式如下:
同样有内存偏移表
,但此偏移表存储的偏移值为相对于偏移表地址的偏移。下面为编译的hello_arm
中_start
函数的反汇编内容:
658:f8df a024 ldr.w sl, [pc, #36]
为加载内存偏移表
内容指令,658h+#36(24h)+4=680h
为内存偏移表
地址,加载内存偏移表
存储的偏移值1092ch
到s1
寄存器;65c:a308 add r3, pc, #32
,紧接着将r3
载入内存偏移表
地址,即65ch+#32(20h)+4=680h
。65e:449a add sl, r3
得到最终地址1092ch+680h=10fach
,因此这里内存偏移表
内存储的偏移是最终地址相对于内存偏移表
地址的偏移。
由于arm
汇编是用PC
和内存偏移表
进行寻址,所以修改ldr
的最终读取的地址仅需要修改内存偏移表
中的偏移。
修改arm
汇编字符串地址的测试用例如下:用lief
增加区段,内容为"hooked str hello world!\n",capstone
反汇编解析ldr
的内存偏移表
地址,修改内存偏移表
内的值改为新增加区段的字符串相对偏移(注意偏移值是相对于add pc
处的,脚本里统一用VA
相减表示)
运行patch脚本后,qemu测试hook成功!
arm64
和arm
同样是定长指令和相对于PC
的寻址,只不过arm64
没有内存偏移表
了,用adrp @page
和add|ldr @pageoff
来寻址。
ADRP
指令是以页为单位的大范围的地址读取指令,P为page
, pageoff
为page offset
,这里填写的地址是最终地址的页基址,汇编会自动把opcode转换成相对于pc页基址的地址。
符号扩展一个21位的offset(immhi+immlo
)。向左移动12位,将PC
的值的低12位清零,然后把这两者相加,结果写入到Xd
寄存器,用来得到一块含有label的4KB对齐内存区域的base地址(也就是说label所在的地址,一定落在这个4KB的内存区域里,指令助记符里Page也就是这个意思), 可用来寻址 +/- 4GB的范围(2^33次幂)。
下面是几种指令的机器码比较:
下面代码说明了,adrp
的机器码immhi+immlo
存储的是相对于当前页面的页偏移0x22000,但是反汇编后显示的则是目标的VA
,即adrp x0, #0x25000
(当此指令在0x3000处时候,cs.disasm(code, 0x3000)
)。
adrp @page
找到目标页地址,add @pageoff
加上页内偏移。如果用ida的keypatch修改,需要去掉@page
,@pageoff
后缀,手动来计算目标页地址和页内偏移才行。ida和objdump
的反汇编代码如下:
下面的python脚本说明了如何用capstone
进行反汇编,如何手动求目标页地址
和页内偏移
,以及之后用keystone
进行汇编更新adrp @page
和add @pageoff
指令。
运行patch脚本后,qemu测试成功!
这篇教程虽然难度不高,本来以为很快就能写完了,但是写了好久。很多东西叙述起来很麻烦,我尽量结合实例清晰和有条理性地讲述,不知不觉中写了好多东西。目前关于主机系列的汉化教程非常少,以往很多的教程也都随着论坛的关闭等无法访问了。同样,比起x86
的汇编的修改,arm64 elf
之类的分析也不多。我系统地做《galgame汉化中的逆向》系列是想总结一下汉化方法,结合我自身对一些问题的思考和理解,以使汉化逆向这门技术今后能更好地传承下去。
sudo apt
-
get update
sudo apt
-
get install gcc
-
arm
-
linux
-
gnueabihf g
+
+
-
arm
-
linux
-
gnueabihf
sudo apt
-
get install gcc
-
aarch64
-
linux
-
gnu g
+
+
-
aarch64
-
linux
-
gnu
sudo apt
-
get install qemu
-
user
-
static binfmt
-
support
sudo apt
-
get install gdb
-
multiarch
pip3 install lief keystone
-
engine capstone
sudo apt
-
get update
sudo apt
-
get install gcc
-
arm
-
linux
-
gnueabihf g
+
+
-
arm
-
linux
-
gnueabihf
sudo apt
-
get install gcc
-
aarch64
-
linux
-
gnu g
+
+
-
aarch64
-
linux
-
gnu
sudo apt
-
get install qemu
-
user
-
static binfmt
-
support
sudo apt
-
get install gdb
-
multiarch
pip3 install lief keystone
-
engine capstone
using namespace std;
int
main()
{
cout<<
"hello world"
<<endl;
}
using namespace std;
int
main()
{
cout<<
"hello world"
<<endl;
}
cd build
arm
-
linux
-
gnueabihf
-
g
+
+
.
/
..
/
hello.cpp
-
o hello_arm
aarch64
-
linux
-
gnu
-
g
+
+
.
/
..
/
hello.cpp
-
o hello_arm64
cd build
arm
-
linux
-
gnueabihf
-
g
+
+
.
/
..
/
hello.cpp
-
o hello_arm
aarch64
-
linux
-
gnu
-
g
+
+
.
/
..
/
hello.cpp
-
o hello_arm64
qemu
-
arm
-
static
-
L
/
usr
/
arm
-
linux
-
gnueabihf
/
.
/
hello_arm
qemu
-
aarch64
-
static
-
L
/
usr
/
aarch64
-
linux
-
gnu
/
.
/
hello_arm64
qemu
-
arm
-
static
-
L
/
usr
/
arm
-
linux
-
gnueabihf
/
.
/
hello_arm
qemu
-
aarch64
-
static
-
L
/
usr
/
aarch64
-
linux
-
gnu
/
.
/
hello_arm64
struct Elf32_Ehdr {
unsigned char e_ident[EI_NIDENT];
/
/
ELF Identification bytes
Elf32_Half e_type;
/
/
Type
of
file
(see ET_
*
below)
Elf32_Half e_machine;
/
/
Required architecture
for
this
file
(see EM_
*
)
Elf32_Word e_version;
/
/
Must be equal to
1
Elf32_Addr e_entry;
/
/
Address to jump to
in
order to start program
Elf32_Off e_phoff;
/
/
Program header table's
file
offset,
in
bytes
Elf32_Off e_shoff;
/
/
Section header table's
file
offset,
in
bytes
Elf32_Word e_flags;
/
/
Processor
-
specific flags
Elf32_Half e_ehsize;
/
/
Size of ELF header,
in
bytes
Elf32_Half e_phentsize;
/
/
Size of an entry
in
the program header table
Elf32_Half e_phnum;
/
/
Number of entries
in
the program header table
Elf32_Half e_shentsize;
/
/
Size of an entry
in
the section header table
Elf32_Half e_shnum;
/
/
Number of entries
in
the section header table
Elf32_Half e_shstrndx;
/
/
Sect hdr table index of sect name string table
};
typedef struct {
Elf64
...
} Elf64_Ehdr;
struct Elf32_Ehdr {
unsigned char e_ident[EI_NIDENT];
/
/
ELF Identification bytes
Elf32_Half e_type;
/
/
Type
of
file
(see ET_
*
below)
Elf32_Half e_machine;
/
/
Required architecture
for
this
file
(see EM_
*
)
Elf32_Word e_version;
/
/
Must be equal to
1
Elf32_Addr e_entry;
/
/
Address to jump to
in
order to start program
Elf32_Off e_phoff;
/
/
Program header table's
file
offset,
in
bytes
Elf32_Off e_shoff;
/
/
Section header table's
file
offset,
in
bytes
Elf32_Word e_flags;
/
/
Processor
-
specific flags
Elf32_Half e_ehsize;
/
/
Size of ELF header,
in
bytes
Elf32_Half e_phentsize;
/
/
Size of an entry
in
the program header table
Elf32_Half e_phnum;
/
/
Number of entries
in
the program header table
Elf32_Half e_shentsize;
/
/
Size of an entry
in
the section header table
Elf32_Half e_shnum;
/
/
Number of entries
in
the section header table
Elf32_Half e_shstrndx;
/
/
Sect hdr table index of sect name string table
};
typedef struct {
Elf64
...
} Elf64_Ehdr;
typedef struct
{
Elf32_Word sh_name;
/
*
Section name (string tbl index)
*
/
Elf32_Word sh_type;
/
*
Section
type
*
/
Elf32_Word sh_flags;
/
*
Section flags
*
/
Elf32_Addr sh_addr;
/
*
Section virtual addr at execution
*
/
Elf32_Off sh_offset;
/
*
Section
file
offset
*
/
Elf32_Word sh_size;
/
*
Section size
in
bytes
*
/
Elf32_Word sh_link;
/
*
Link to another section
*
/
Elf32_Word sh_info;
/
*
Additional section information
*
/
Elf32_Word sh_addralign;
/
*
Section alignment
*
/
Elf32_Word sh_entsize;
/
*
Entry size
if
section holds table
*
/
} Elf32_Shdr;
typedef struct {
Elf64...
}Elf64_Shdr;
typedef struct
{
Elf32_Word sh_name;
/
*
Section name (string tbl index)
*
/
Elf32_Word sh_type;
/
*
Section
type
*
/
Elf32_Word sh_flags;
/
*
Section flags
*
/
Elf32_Addr sh_addr;
/
*
Section virtual addr at execution
*
/
Elf32_Off sh_offset;
/
*
Section
file
offset
*
/
Elf32_Word sh_size;
/
*
Section size
in
bytes
*
/
Elf32_Word sh_link;
/
*
Link to another section
*
/
Elf32_Word sh_info;
/
*
Additional section information
*
/
Elf32_Word sh_addralign;
/
*
Section alignment
*
/
Elf32_Word sh_entsize;
/
*
Entry size
if
section holds table
*
/
} Elf32_Shdr;
typedef struct {
Elf64...
}Elf64_Shdr;
typedef struct {
Elf32_Word p_type;
/
/
Type
of segment
Elf32_Off p_offset;
/
/
File
offset where segment
is
located,
in
bytes
Elf32_Addr p_vaddr;
/
/
Virtual address of beginning of segment
Elf32_Addr p_paddr;
/
/
Physical address of beginning of segment (OS
-
specific)
Elf32_Word p_filesz;
/
/
Num. of bytes
in
file
image of segment (may be zero)
Elf32_Word p_memsz;
/
/
Num. of bytes
in
mem image of segment (may be zero)
Elf32_Word p_flags;
/
/
Segment flags
Elf32_Word p_align;
/
/
Segment alignment constraint
} Elf32_Phdr;
typedef struct {
Elf64_Word
...
} Elf64_Phdr;
typedef struct {
Elf32_Word p_type;
/
/
Type
of segment
Elf32_Off p_offset;
/
/
File
offset where segment
is
located,
in
bytes
Elf32_Addr p_vaddr;
/
/
Virtual address of beginning of segment
Elf32_Addr p_paddr;
/
/
Physical address of beginning of segment (OS
-
specific)
Elf32_Word p_filesz;
/
/
Num. of bytes
in
file
image of segment (may be zero)
Elf32_Word p_memsz;
/
/
Num. of bytes
in
mem image of segment (may be zero)
Elf32_Word p_flags;
/
/
Segment flags
Elf32_Word p_align;
/
/
Segment alignment constraint
} Elf32_Phdr;
typedef struct {
Elf64_Word
...
} Elf64_Phdr;
readelf
-
h|
-
-
file
-
header elffile
/
/
查看elf头,可以与下面多个选项组合
readelf
-
S|
-
-
sections elffile
/
/
查看elf section头
readelf
-
l|
-
–segments elffile
/
/
查看elf segment头
readelf
-
x|
-
-
hexdump .sect_name|number elffile
/
/
通过数字或name hexdump section
readelf
-
r elffile
/
/
查看elf 重定位
readelf
-
d elffile
/
/
查看elf .dynamic段, needed
objdump
-
f|
-
-
file
-
headers elffile
/
/
显示obj类型
objdump
-
x|
-
-
all
-
headers libxxxxx.so | grep NEEDED
/
/
-
x 显示所可用的头信息
objdump
-
s [
-
j name] elf
/
/
-
s 对section反汇编,
-
j 显示section的信息, 如.data
objdump
-
d [
-
-
start
-
address
=
address] [
-
-
stop
-
address
=
address] elffile
/
/
反编译
hexdump [
-
s|
-
-
skip skip_offset] [
-
n|
-
-
length size] <
file
>
-
C 显示asci和
hex
-
d|x 两字节
10
进制,
16
进制显示
readelf
-
h|
-
-
file
-
header elffile
/
/
查看elf头,可以与下面多个选项组合
readelf
-
S|
-
-
sections elffile
/
/
查看elf section头
readelf
-
l|
-
–segments elffile
/
/
查看elf segment头
readelf
-
x|
-
-
hexdump .sect_name|number elffile
/
/
通过数字或name hexdump section
readelf
-
r elffile
/
/
查看elf 重定位
readelf
-
d elffile
/
/
查看elf .dynamic段, needed
objdump
-
f|
-
-
file
-
headers elffile
/
/
显示obj类型
objdump
-
x|
-
-
all
-
headers libxxxxx.so | grep NEEDED
/
/
-
x 显示所可用的头信息
objdump
-
s [
-
j name] elf
/
/
-
s 对section反汇编,
-
j 显示section的信息, 如.data
objdump
-
d [
-
-
start
-
address
=
address] [
-
-
stop
-
address
=
address] elffile
/
/
反编译
hexdump [
-
s|
-
-
skip skip_offset] [
-
n|
-
-
length size] <
file
>
-
C 显示asci和
hex
-
d|x 两字节
10
进制,
16
进制显示
libsdl_main_jni
=
lief.parse(
"hello"
)
sec_hookstr
=
lief.ELF.Section(
".hookstr"
, lief.ELF.SECTION_TYPES.PROGBITS)
sec_hookstr
+
=
lief.ELF.SECTION_FLAGS.EXECINSTR
sec_hookstr.alignment
=
4
sec_hookstr.content
=
list
(bytes(
'hooked str hello world!\n'
, encoding
=
'ansi'
))
sec_hookstr
=
libsdl_main_jni.add(sec_hookstr)
libsdl_main_jni.patch_address(
0x15D0
, [
0X36
,
0X5A
])
libsdl_main_jni.write(
"hello_hook"
)
libsdl_main_jni
=
lief.parse(
"hello"
)
sec_hookstr
=
lief.ELF.Section(
".hookstr"
, lief.ELF.SECTION_TYPES.PROGBITS)
sec_hookstr
+
=
lief.ELF.SECTION_FLAGS.EXECINSTR
sec_hookstr.alignment
=
4
sec_hookstr.content
=
list
(bytes(
'hooked str hello world!\n'
, encoding
=
'ansi'
))
sec_hookstr
=
libsdl_main_jni.add(sec_hookstr)
libsdl_main_jni.patch_address(
0x15D0
, [
0X36
,
0X5A
])
libsdl_main_jni.write(
"hello_hook"
)
for
section
in
libsdl_main_jni.sections:
if
section.name
=
=
'.rodata'
:
print
(
hex
(section.offset), section.size)
byte_arr
=
list
(bytes(
'/data/data/cn.natdon.onscripterv2yuri/lib/libapp_%s.so\0'
, encoding
=
'ansi'
))
arr_offset
=
section.offset
+
section.size
section.size
+
=
len
(byte_arr)
libsdl_main_jni.segments[
1
].virtual_size
+
=
len
(byte_arr)
libsdl_main_jni.segments[
1
].physical_size
+
=
len
(byte_arr)
libsdl_main_jni.patch_address(arr_offset, byte_arr)
libsdl_main_jni.patch_address(
0x21C8
,
list
((arr_offset
-
0x2104
).to_bytes(
2
,
'little'
)))
for
section
in
libsdl_main_jni.sections:
if
section.name
=
=
'.rodata'
:
print
(
hex
(section.offset), section.size)
byte_arr
=
list
(bytes(
'/data/data/cn.natdon.onscripterv2yuri/lib/libapp_%s.so\0'
, encoding
=
'ansi'
))
arr_offset
=
section.offset
+
section.size
section.size
+
=
len
(byte_arr)
libsdl_main_jni.segments[
1
].virtual_size
+
=
len
(byte_arr)
libsdl_main_jni.segments[
1
].physical_size
+
=
len
(byte_arr)
libsdl_main_jni.patch_address(arr_offset, byte_arr)
libsdl_main_jni.patch_address(
0x21C8
,
list
((arr_offset
-
0x2104
).to_bytes(
2
,
'little'
)))
fopen_sym
=
next
(
filter
(
lambda
e : e.name
=
=
"fopen"
, libxxx.imported_symbols))
fopen_sym.name
=
"fopen_saf"
fopen_sym
=
next
(
filter
(
lambda
e : e.name
=
=
"fopen"
, libxxx.imported_symbols))
fopen_sym.name
=
"fopen_saf"
/
/
ida libapp_onscripter
-
32bpp
.so
.text:
00013F78
25
48
LDR R0,
=
(aUsageOnscripte
-
0x13F80
) ; "Usage: onscripter ...
;aUsageOnscripte
00048B70
,
0X13F7C
+
4
=
0x13F80
,
0X48B70
-
0x13F80
=
34BF0
, 相对于add pc偏移
.text:
00013F7A
08
B5 PUSH {R3,LR}
.text:
00013F7C
78
44
ADD R0, PC ;
"Usage: onscripter [option ...]\n"
.text:
00013F7E
FF F7 E5 FF BL sub_13F4C
....
.text:
00014010
F0
4B
03
00
off_14010 DCD aUsageOnscripte
-
0x13F80
/
/
arm
-
linux
-
gnueabihf
-
objdump
-
d
-
-
start
-
address
0x13f78
-
-
stop
-
address
0x14014
libapp_onscripter
-
32bpp
.so
13f78
:
4825
ldr r0, [pc,
13f7a
: b508 push {r3, lr}
13f7c
:
4478
add r0, pc
13f7e
: f7ff ffe5 bl
13f4c
<__gnu_Unwind_Find_exidx@plt
+
0x34
>
/
/
ida libapp_onscripter
-
32bpp
.so
.text:
00013F78
25
48
LDR R0,
=
(aUsageOnscripte
-
0x13F80
) ; "Usage: onscripter ...
;aUsageOnscripte
00048B70
,
0X13F7C
+
4
=
0x13F80
,
0X48B70
-
0x13F80
=
34BF0
, 相对于add pc偏移
.text:
00013F7A
08
B5 PUSH {R3,LR}
.text:
00013F7C
78
44
ADD R0, PC ;
"Usage: onscripter [option ...]\n"
.text:
00013F7E
FF F7 E5 FF BL sub_13F4C
....
.text:
00014010
F0
4B
03
00
off_14010 DCD aUsageOnscripte
-
0x13F80
/
/
arm
-
linux
-
gnueabihf
-
objdump
-
d
-
-
start
-
address
0x13f78
-
-
stop
-
address
0x14014
libapp_onscripter
-
32bpp
.so
13f78
:
4825
ldr r0, [pc,
13f7a
: b508 push {r3, lr}
13f7c
:
4478
add r0, pc
13f7e
: f7ff ffe5 bl
13f4c
<__gnu_Unwind_Find_exidx@plt
+
0x34
>
31
27
26
25
24
05
00
LDR32
0
0
0
1
1
0
0
0
imm19 (
4
倍されて, ±
1MB
) Rt(
5bit
)
31
27
26
25
24
05
00
LDR32
0
0
0
1
1
0
0
0
imm19 (
4
倍されて, ±
1MB
) Rt(
5bit
)
.text:
00000648
_start
.text:
00000648
4F
F0
00
0B
MOV.W R11,
.text:
0000064C
4F
F0
00
0E
MOV.W LR,
.text:
00000650
02
BC POP {R1} ; argc
.text:
00000652
6A
46
MOV R2, SP ; ubp_av
.text:
00000654
04
B4 PUSH {R2} ; stack_end
.text:
00000656
01
B4 PUSH {R0} ; rtld_fini
.text:
00000658
DF F8
24
A0 LDR.W R10,
=
($_GLOBAL_OFFSET_TABLE_
-
0x680
)
; $_GLOBAL_OFFSET_TABLE_
10FAC
=
680
+
1092c
相对于偏移表的偏移。
.text:
0000065C
08
A3 ADR R3, off_680
.text:
0000065E
9A
44
ADD R10, R3 ; $_GLOBAL_OFFSET_TABLE_
.text:
00000660
DF F8
20
C0 LDR.W R12,
=
(__libc_csu_fini_ptr
-
0x10FAC
)
.text:
00000664
5A
F8
0C
C0 LDR.W R12, [R10,R12] ; __libc_csu_fini
.text:
00000668
4D
F8
04
CD PUSH.W {R12} ; fini
.text:
0000066C
06
4B
LDR R3,
=
(__libc_csu_init_ptr
-
0x10FAC
)
.text:
0000066E
5A
F8
03
30
LDR.W R3, [R10,R3] ; __libc_csu_init ; init
.text:
00000672
06
48
LDR R0,
=
(main_ptr
-
0x10FAC
)
.text:
00000674
5A
F8
00
00
LDR.W R0, [R10,R0] ; main ; main
.text:
00000678
FF F7 D4 EF BLX __libc_start_main
.text:
0000067C
FF F7 BA EF BLX abort
.text:
00000680
2C
09
01
00
off_680 DCD $_GLOBAL_OFFSET_TABLE_
-
0x680
.text:
00000684
40
00
00
00
off_684 DCD __libc_csu_fini_ptr
-
0x10FAC
.text:
00000688
38
00
00
00
off_688 DCD __libc_csu_init_ptr
-
0x10FAC
.text:
0000068C
2C
00
00
00
off_68C DCD main_ptr
-
0x10FAC
/
/
objdump
-
d hello_arm
00000648
<_start>:
648
: f04f
0b00
mov.w fp,
64c
: f04f
0e00
mov.w lr,
650
: bc02 pop {r1}
652
:
466a
mov r2, sp
654
: b404 push {r2}
656
: b401 push {r0}
658
: f8df a024 ldr.w sl, [pc,
65c
: a308 add r3, pc,
65e
:
449a
add sl, r3
660
: f8df c020 ldr.w ip, [pc,
664
: f85a c00c ldr.w ip, [sl, ip]
668
: f84d cd04
str
.w ip, [sp,
66c
:
4b06
ldr r3, [pc,
66e
: f85a
3003
ldr.w r3, [sl, r3]
672
:
4806
ldr r0, [pc,
674
: f85a
0000
ldr.w r0, [sl, r0]
678
: f7ff efd4 blx
624
<__libc_start_main@plt>
67c
: f7ff efba blx
5f4
<abort@plt>
680
:
0001092c
.word
0x0001092c
684
:
00000040
.word
0x00000040
688
:
00000038
.word
0x00000038
68c
:
0000002c
.word
0x0000002c
.text:
00000648
_start
.text:
00000648
4F
F0
00
0B
MOV.W R11,
.text:
0000064C
4F
F0
00
0E
MOV.W LR,
.text:
00000650
02
BC POP {R1} ; argc
.text:
00000652
6A
46
MOV R2, SP ; ubp_av
.text:
00000654
04
B4 PUSH {R2} ; stack_end
.text:
00000656
01
B4 PUSH {R0} ; rtld_fini
.text:
00000658
DF F8
24
A0 LDR.W R10,
=
($_GLOBAL_OFFSET_TABLE_
-
0x680
)
; $_GLOBAL_OFFSET_TABLE_
10FAC
=
680
+
1092c
相对于偏移表的偏移。
.text:
0000065C
08
A3 ADR R3, off_680
.text:
0000065E
9A
44
ADD R10, R3 ; $_GLOBAL_OFFSET_TABLE_
.text:
00000660
DF F8
20
C0 LDR.W R12,
=
(__libc_csu_fini_ptr
-
0x10FAC
)
.text:
00000664
5A
F8
0C
C0 LDR.W R12, [R10,R12] ; __libc_csu_fini
.text:
00000668
4D
F8
04
CD PUSH.W {R12} ; fini
.text:
0000066C
06
4B
LDR R3,
=
(__libc_csu_init_ptr
-
0x10FAC
)
.text:
0000066E
5A
F8
03
30
LDR.W R3, [R10,R3] ; __libc_csu_init ; init
.text:
00000672
06
48
LDR R0,
=
(main_ptr
-
0x10FAC
)
.text:
00000674
5A
F8
00
00
LDR.W R0, [R10,R0] ; main ; main
.text:
00000678
FF F7 D4 EF BLX __libc_start_main
.text:
0000067C
FF F7 BA EF BLX abort
.text:
00000680
2C
09
01
00
off_680 DCD $_GLOBAL_OFFSET_TABLE_
-
0x680
.text:
00000684
40
00
00
00
off_684 DCD __libc_csu_fini_ptr
-
0x10FAC
.text:
00000688
38
00
00
00
off_688 DCD __libc_csu_init_ptr
-
0x10FAC
.text:
0000068C
2C
00
00
00
off_68C DCD main_ptr
-
0x10FAC
/
/
objdump
-
d hello_arm
00000648
<_start>:
648
: f04f
0b00
mov.w fp,
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!