第一、前言
从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域。作为一名Android开发者,每个人都想去探索这个领域,因为一旦你破解了别人的内容,成就感肯定爆棚,不过相反的是,我们不仅要研究破解之道,也要研究加密之道,因为加密和破解是相生相克的。但是我们在破解的过程中可能最头疼的是native层,也就是so文件的破解。所以我们先来详细了解一下so文件的内容下面就来看看我们今天所要介绍的内容。今天我们先来介绍一下elf文件的格式,因为我们知道Android中的so文件就是elf文件,所以需要了解so文件,必须先来了解一下elf文件的格式,对于如何详细了解一个elf文件,就是手动的写一个工具类来解析一个elf文件。
第二、准备资料
我们需要了解elf文件的格式,关于elf文件格式详解,网上已经有很多介绍资料了。这里我也不做太多的解释了。不过有两个资料还是需要介绍一下的,因为网上的内容真的很多,很杂。这两个资料是最全的,也是最好的。我就是看这两个资料来操作的:
第一个资料是非虫大哥的经典之作:
看吧,是不是超级详细?后面我们用Java代码来解析elf文件的时候,就是按照这张图来的。但是这张图有些数据结构解释的还不是很清楚,所以第二个资料来了。
第二个资料:北京大学实验室出的标准版
http://download.csdn.net/detail/jiangwei0910410003/9204051
这里就不对这个文件做详细解释了,后面在做解析工作的时候,会截图说明。
关于上面的这两个资料,这里还是多数两句:一定要仔细认真的阅读。这个是经典之作。也是后面工作的基础。
第三、工具
当然这里还需要介绍一个工具,因为这个工具在我们下面解析elf文件的时候,也非常有用,而且是检查我们解析elf文件的模板。
就是很出名的:readelf命令
不过Window下这个命令不能用,因为这个命令是Linux的,所以我们还得做个工作就是安装Cygwin。关于这个工具的安装,大家可以看看这篇文章:
http://blog.csdn.net/jiangwei0910410003/article/details/17710243
不过在下载的过程中,我担心小朋友们会遇到挫折,所以很贴心的,放到的云盘里面:
http://pan.baidu.com/s/1C1Zci
下载下来之后,需要改一个东西才能用:
该一下这个文件:
这个路径要改成你本地cygwin64中的bin目录的路径,不然运行错误的。改好之后,直接运行Cygwin.bat就可以了。
关于readelf工具我们这里不做太详细的介绍,只介绍我们要用到的命令:
1、readelf -h xxx.so
查看so文件的头部信息
2、readelf -S xxx.so
查看so文件的段(Section)头的信息
3、readelf -l xxx.so
查看so文件的程序段头信息(Program)
4、readelf -a xxx.so
查看so文件的全部内容
还有很多命令用法,这里就不在细说了,网上有很多介绍的~~
第四、实际操作解析Elf文件(Java代码&C++代码)
上面我们介绍了elf文件格式资料,elf文件的工具,那么下面我们就来实际操作一下,来用Java代码手把手的解析一个libhello-jni.so文件。关于这个libhello-jni.so文件的下载地址:
http://download.csdn.net/detail/jiangwei0910410003/9204087
1、首先定义elf文件中各个结构体内容
这个我们需要参考elf.h这个头文件的格式了。这个文件网上也是有的,这里还是给个下载链接吧:
http://download.csdn.net/detail/jiangwei0910410003/9204081
我们看看Java中定义的elf文件的数据结构类:
|
package
com.demo.parseso;
import
java.util.ArrayList;
public
class
ElfType32 {
public
elf32_rel rel;
public
elf32_rela rela;
public
ArrayList<elf32_sym> symList =
new
ArrayList<elf32_sym>();
public
elf32_hdr hdr;
//elf头部信息
public
ArrayList<elf32_phdr> phdrList =
new
ArrayList<elf32_phdr>();
//可能会有多个程序头
public
ArrayList<elf32_shdr> shdrList =
new
ArrayList<elf32_shdr>();
//可能会有多个段头
public
ArrayList<elf32_strtb> strtbList =
new
ArrayList<elf32_strtb>();
//可能会有多个字符串值
public
ElfType32() {
rel =
new
elf32_rel();
rela =
new
elf32_rela();
hdr =
new
elf32_hdr();
}
/**
* typedef struct elf32_rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
*
*/
public
class
elf32_rel {
public
byte
[] r_offset =
new
byte
[
4
];
public
byte
[] r_info =
new
byte
[
4
];
@Override
public
String toString(){
return
r_offset:+Utils.bytes2HexString(r_offset)+;r_info:+Utils.bytes2HexString(r_info);
}
}
/**
* typedef struct elf32_rela{
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
*/
public
class
elf32_rela{
public
byte
[] r_offset =
new
byte
[
4
];
public
byte
[] r_info =
new
byte
[
4
];
public
byte
[] r_addend =
new
byte
[
4
];
@Override
public
String toString(){
return
r_offset:+Utils.bytes2HexString(r_offset)+;r_info:+Utils.bytes2HexString(r_info)+;r_addend:+Utils.bytes2HexString(r_info);
}
}
/**
* typedef struct elf32_sym{
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
*/
public
static
class
Elf32_Sym{
public
byte
[] st_name =
new
byte
[
4
];
public
byte
[] st_value =
new
byte
[
4
];
public
byte
[] st_size =
new
byte
[
4
];
public
byte
st_info;
public
byte
st_other;
public
byte
[] st_shndx =
new
byte
[
2
];
@Override
public
String toString(){
return
st_name:+Utils.bytes2HexString(st_name)
+
st_value:+Utils.bytes2HexString(st_value)
+
st_size:+Utils.bytes2HexString(st_size)
+
st_info:+(st_info/
16
)
+
st_other:+(((
short
)st_other) &
0xF
)
+
st_shndx:+Utils.bytes2HexString(st_shndx);
}
}
public
void
printSymList(){
for
(
int
i=
0
;i<symlist.size();i++){ the=
""
symbol=
""
stt_section=
"3;"
stt_object=
"1;"
stt_notype=
"0;"
stt_func=
"2;"
stt_file=
"4;"
stb_weak=
"2;"
stb_local=
"0;"
stb_global=
"1;"
static
=
""
public
=
""
int
=
""
final
=
""
define=
""
>>
4
)
#define ELF_ST_TYPE(x) (((unsigned
int
) x) &
0xf
)
*/
/**
* typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; // Entry point
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
*/
public
class
elf32_hdr{
public
byte
[] e_ident =
new
byte
[
16
];
public
byte
[] e_type =
new
byte
[
2
];
public
byte
[] e_machine =
new
byte
[
2
];
public
byte
[] e_version =
new
byte
[
4
];
public
byte
[] e_entry =
new
byte
[
4
];
public
byte
[] e_phoff =
new
byte
[
4
];
public
byte
[] e_shoff =
new
byte
[
4
];
public
byte
[] e_flags =
new
byte
[
4
];
public
byte
[] e_ehsize =
new
byte
[
2
];
public
byte
[] e_phentsize =
new
byte
[
2
];
public
byte
[] e_phnum =
new
byte
[
2
];
public
byte
[] e_shentsize =
new
byte
[
2
];
public
byte
[] e_shnum =
new
byte
[
2
];
public
byte
[] e_shstrndx =
new
byte
[
2
];
@Override
public
String toString(){
return
magic:+ Utils.bytes2HexString(e_ident)
+
e_type:+Utils.bytes2HexString(e_type)
+
e_machine:+Utils.bytes2HexString(e_machine)
+
e_version:+Utils.bytes2HexString(e_version)
+
e_entry:+Utils.bytes2HexString(e_entry)
+
e_phoff:+Utils.bytes2HexString(e_phoff)
+
e_shoff:+Utils.bytes2HexString(e_shoff)
+
e_flags:+Utils.bytes2HexString(e_flags)
+
e_ehsize:+Utils.bytes2HexString(e_ehsize)
+
e_phentsize:+Utils.bytes2HexString(e_phentsize)
+
e_phnum:+Utils.bytes2HexString(e_phnum)
+
e_shentsize:+Utils.bytes2HexString(e_shentsize)
+
e_shnum:+Utils.bytes2HexString(e_shnum)
+
e_shstrndx:+Utils.bytes2HexString(e_shstrndx);
}
}
/**
* typedef struct elf32_phdr{
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
*/
public
static
class
elf32_phdr{
public
byte
[] p_type =
new
byte
[
4
];
public
byte
[] p_offset =
new
byte
[
4
];
public
byte
[] p_vaddr =
new
byte
[
4
];
public
byte
[] p_paddr =
new
byte
[
4
];
public
byte
[] p_filesz =
new
byte
[
4
];
public
byte
[] p_memsz =
new
byte
[
4
];
public
byte
[] p_flags =
new
byte
[
4
];
public
byte
[] p_align =
new
byte
[
4
];
@Override
public
String toString(){
return
p_type:+ Utils.bytes2HexString(p_type)
+
p_offset:+Utils.bytes2HexString(p_offset)
+
p_vaddr:+Utils.bytes2HexString(p_vaddr)
+
p_paddr:+Utils.bytes2HexString(p_paddr)
+
p_filesz:+Utils.bytes2HexString(p_filesz)
+
p_memsz:+Utils.bytes2HexString(p_memsz)
+
p_flags:+Utils.bytes2HexString(p_flags)
+
p_align:+Utils.bytes2HexString(p_align);
}
}
public
void
printPhdrList(){
for
(
int
i=
0
;i<phdrlist.size();i++){ the=
""
static
=
""
public
=
""
int
=
""
final
=
""
void
=
""
typedef=
""
struct=
""
string=
""
str_name=
""
sht_symtab=
"2;"
sht_strtab=
"3;"
sht_shlib=
"10;"
sht_rela=
"4;"
sht_rel=
"9;"
sht_progbits=
"1;"
sht_num=
"12;"
sht_null=
"0;"
sht_note=
"7;"
sht_nobits=
"8;"
sht_mips_ucode=
"0x70000004;"
sht_mips_list=
"0x70000000;"
sht_mips_gptab=
"0x70000003;"
sht_mips_conflict=
"0x70000002;"
sht_louser=
"0x80000000;"
sht_loproc=
"0x70000000;"
sht_hiuser=
"0xffffffff;"
sht_hiproc=
"0x7fffffff;"
sht_hash=
"5;"
sht_dynsym=
"11;"
sht_dynamic=
"6;"
shf_write=
"0x1;"
shf_mips_gprel=
"0x10000000;"
shf_maskproc=
"0xf0000000;"
shf_execinstr=
"0x4;"
shf_alloc=
"0x2;"
sh_type=
"new"
sh_size=
"new"
sh_offset=
"new"
sh_name=
"new"
sh_link=
"new"
sh_info=
"new"
sh_flags=
"new"
sh_entsize=
"new"
sh_addralign=
"new"
sh_addr=
"new"
section=
""
return
=
""
program=
""
pre=
""
override=
""
i=
"0;i<shdrList.size();i++){"
elf32_word=
""
elf32_shdr=
""
elf32_off=
""
elf32_addr=
""
>这个没什么问题,也没难度,就是在看elf.h文件中定义的数据结构的时候,要记得每个字段的占用字节数就可以了。<p> </p><p> </p><p>有了结构定义,下面就来看看如何解析吧。</p><p>在解析之前我们需要将so文件读取到
byte
[]中,定义一个数据结构类型</p><p> </p><pre
class
=
"brush:java;"
>
public
static
ElfType32 type_32 =
new
ElfType32();
byte
[] fileByteArys = Utils.readFile(so/libhello-jni.so);
if
(fileByteArys ==
null
){
System.out.println(read file
byte
failed...);
return
;
}</pre>
<br>
<p> </p>
<h2><strong>
2
、解析elf文件的头部信息</strong></h2>
<p><img style=
"width: 529.78px; height: 560px; display: block;"
alt=
"/"
src=
"https://www.2cto.com/uploadfile/Collfiles/20151023/2015102308251612.png"
></p>
<p>关于这些字段的解释,要看上面提到的那个pdf文件中的描述</p>
<p>这里我们介绍几个重要的字段,也是我们后面修改so文件的时候也会用到:</p>
<p><strong>
1
)、e_phoff</strong></p>
<p>这个字段是程序头(Program Header)内容在整个文件的偏移值,我们可以用这个偏移值来定位程序头的开始位置,用于解析程序头信息</p>
<p><strong>
2
)、e_shoff</strong></p>
<p>这个字段是段头(Section Header)内容在这个文件的偏移值,我们可以用这个偏移值来定位段头的开始位置,用于解析段头信息</p>
<p><strong>
3
)、e_phnum</strong></p>
<p>这个字段是程序头的个数,用于解析程序头信息</p>
<p><strong>
4
)、e_shnum</strong></p>
<p>这个字段是段头的个数,用于解析段头信息</p>
<p><strong>
5
)、e_shstrndx</strong></p>
<p>这个字段是String段在整个段列表中的索引值,这个用于后面定位String段的位置</p>
<p> </p>
<p>按照上面的图我们就可以很容易的解析</p>
<p> </p>
<pre
class
=
"brush:java;"
>
/**
* 解析Elf的头部信息
* @param header
*/
private
static
void
parseHeader(
byte
[] header,
int
offset){
if
(header ==
null
){
System.out.println(header is
null
);
return
;
}
/**
* public byte[] e_ident = new byte[16];
public short e_type;
public short e_machine;
public int e_version;
public int e_entry;
public int e_phoff;
public int e_shoff;
public int e_flags;
public short e_ehsize;
public short e_phentsize;
public short e_phnum;
public short e_shentsize;
public short e_shnum;
public short e_shstrndx;
*/
type_32.hdr.e_ident = Utils.copyBytes(header,
0
,
16
);
//魔数
type_32.hdr.e_type = Utils.copyBytes(header,
16
,
2
);
type_32.hdr.e_machine = Utils.copyBytes(header,
18
,
2
);
type_32.hdr.e_version = Utils.copyBytes(header,
20
,
4
);
type_32.hdr.e_entry = Utils.copyBytes(header,
24
,
4
);
type_32.hdr.e_phoff = Utils.copyBytes(header,
28
,
4
);
type_32.hdr.e_shoff = Utils.copyBytes(header,
32
,
4
);
type_32.hdr.e_flags = Utils.copyBytes(header,
36
,
4
);
type_32.hdr.e_ehsize = Utils.copyBytes(header,
40
,
2
);
type_32.hdr.e_phentsize = Utils.copyBytes(header,
42
,
2
);
type_32.hdr.e_phnum = Utils.copyBytes(header,
44
,
2
);
type_32.hdr.e_shentsize = Utils.copyBytes(header,
46
,
2
);
type_32.hdr.e_shnum = Utils.copyBytes(header,
48
,
2
);
type_32.hdr.e_shstrndx = Utils.copyBytes(header,
50
,
2
);
}</pre>
按照对应的每个字段的字节个数,读取
byte
就可以了。
<p> </p>
<p> </p>
<h2><strong>
3
、解析段头(Section Header)信息</strong></h2>
<p><img style=
"width: 630px; height: 430.31px; display: block;"
alt=
"/"
src=
"https://www.2cto.com/uploadfile/Collfiles/20151023/2015102308251613.png"
><br>
<br>
这个结构中字段见pdf中的描述吧,这里就不做解释了。后面我们会手动的构造这样的一个数据结构,到时候在详细说明每个字段含义。</p>
<p>按照这个结构。我们解析也简单了:</p>
<p> </p>
<pre
class
=
"brush:java;"
>
/**
* 解析段头信息内容
*/
public
static
void
parseSectionHeaderList(
byte
[] header,
int
offset){
int
header_size =
40
;
//40个字节
int
header_count = Utils.byte2Short(type_32.hdr.e_shnum);
//头部的个数
byte
[] des =
new
byte
[header_size];
for
(
int
i=
0
;i<header_count;i++){
static
=
""
public
=
""
sh_type=
"new"
sh_size=
"new"
sh_offset=
"new"
sh_name=
"new"
sh_link=
"new"
sh_info=
"new"
sh_flags=
"new"
sh_entsize=
"new"
sh_addralign=
"new"
sh_addr=
"new"
return
=
""
pre=
""
elf32_shdr=
""
shdr.sh_type=
"Utils.copyBytes(header,"
shdr.sh_size=
"Utils.copyBytes(header,"
shdr.sh_offset=
"Utils.copyBytes(header,"
shdr.sh_name=
"Utils.copyBytes(header,"
shdr.sh_link=
"Utils.copyBytes(header,"
shdr.sh_info=
"Utils.copyBytes(header,"
shdr.sh_flags=
"Utils.copyBytes(header,"
shdr.sh_entsize=
"Utils.copyBytes(header,"
shdr.sh_addralign=
"Utils.copyBytes(header,"
shdr.sh_addr=
"Utils.copyBytes(header,"
shdr=
"new"
private
=
""
header_size=
""
elftype32.elf32_shdr=
""
>这里需要注意的是,我们看到的Section Header一般都是多个的,这里用一个List来保存<p> </p><p> </p><h2><strong>
4
、解析程序头(Program Header)信息</strong></h2><p><img style=
"width: 630px; height: 416.19px; display: block;"
alt=
"/"
src=
"https://www.2cto.com/uploadfile/Collfiles/20151023/2015102308251614.png"
>
这里的字段,这里也不做解释了,看pdf文档。</p><p>我们按照这个结构来进行解析:</p><p> </p><pre
class
=
"brush:java;"
>
/**
* 解析程序头信息
* @param header
*/
public
static
void
parseProgramHeaderList(
byte
[] header,
int
offset){
int
header_size =
32
;
//32个字节
int
header_count = Utils.byte2Short(type_32.hdr.e_phnum);
//头部的个数
byte
[] des =
new
byte
[header_size];
for
(
int
i=
0
;i<header_count;i++){
static
=
""
public
=
""
int
=
""
return
=
""
pre=
""
private
=
""
header_size=
""
phdr.p_vaddr=
"Utils.copyBytes(header,"
phdr.p_type=
"Utils.copyBytes(header,"
phdr.p_paddr=
"Utils.copyBytes(header,"
phdr.p_offset=
"Utils.copyBytes(header,"
phdr.p_memsz=
"Utils.copyBytes(header,"
phdr.p_flags=
"Utils.copyBytes(header,"
phdr.p_filesz=
"Utils.copyBytes(header,"
phdr.p_align=
"Utils.copyBytes(header,"
phdr=
"new"
elftype32.elf32_phdr=
""
elf32_phdr=
""
><p> </p><p> </p>当然还有其他结构的解析工作,这里就不在一一介绍了,因为这些结构我们在后面的介绍中不会用到,但是也是需要了解的,详细参见pdf文档。<p> </p><h2><strong>
5
、验证解析结果</strong></h2><p>那么上面我们的解析工作做完了,为了验证我们的解析工作是否正确,我们需要给每个结构定义个打印函数,也就是从写toString方法即可。</p><p><img style=
"width: 447.8px; height: 560px; display: block;"
alt=
"/"
src=
"https://www.2cto.com/uploadfile/Collfiles/20151023/2015102308251615.png"
></p><p>然后我们在使用readelf工具来查看so文件的各个结构内容,对比就可以知道解析的是否成功了。</p><p> </p><p><strong>解析代码下载地址:http://download.csdn.net/detail/jiangwei0910410003/
9204119
</strong></p><p> </p><p>上面我们用的是Java代码来进行解析的,为了照顾广大程序猿,所以给出一个C++版本的解析类:</p><p> </p><pre
class
=
"brush:java;"
>#include<iostream.h>
#include<string.h>
#include<stdio.h>
#include elf.h
/**
非常重要的一个宏,功能很简单:
P:需要对其的段地址
ALIGNBYTES:对其的字节数
功能:将P值补充到时ALIGNBYTES的整数倍
这个函数也叫:页面对其函数
eg: 0x3e45/0x1000 == >0x4000
*/
#define ALIGN(P, ALIGNBYTES) ( ((unsigned
long
)P + ALIGNBYTES -
1
)&~(ALIGNBYTES-
1
) )
int
addSectionFun(
char
*,
char
*, unsigned
int
);
int
main()
{
addSectionFun(D:libhello-jni.so, .jiangwei,
0x1000
);
return
0
;
}
int
addSectionFun(
char
*lpPath,
char
*szSecname, unsigned
int
nNewSecSize)
{
char
name[
50
];
FILE *fdr, *fdw;
char
*base = NULL;
Elf32_Ehdr *ehdr;
Elf32_Phdr *t_phdr, *load1, *load2, *dynamic;
Elf32_Shdr *s_hdr;
int
flag =
0
;
int
i =
0
;
unsigned mapSZ =
0
;
unsigned nLoop =
0
;
unsigned
int
nAddInitFun =
0
;
unsigned
int
nNewSecAddr =
0
;
unsigned
int
nModuleBase =
0
;
memset(name,
0
, sizeof(name));
if
(nNewSecSize ==
0
)
{
return
0
;
}
fdr = fopen(lpPath, rb);
strcpy(name, lpPath);
if
(strchr(name,
'.'
))
{
strcpy(strchr(name,
'.'
), _new.so);
}
else
{
strcat(name, _new);
}
fdw = fopen(name, wb);
if
(fdr == NULL || fdw == NULL)
{
printf(Open file failed);
return
1
;
}
fseek(fdr,
0
, SEEK_END);
mapSZ = ftell(fdr);
//源文件的长度大小
printf(mapSZ:0x%x
, mapSZ);
base = (
char
*)malloc(mapSZ *
2
+ nNewSecSize);
//2*源文件大小+新加的Section size
printf(base 0x%x
, base);
memset(base,
0
, mapSZ *
2
+ nNewSecSize);
fseek(fdr,
0
, SEEK_SET);
fread(base,
1
, mapSZ, fdr);
//拷贝源文件内容到base
if
(base == (
void
*) -
1
)
{
printf(fread fd failed);
return
2
;
}
//判断Program Header
ehdr = (Elf32_Ehdr*) base;
t_phdr = (Elf32_Phdr*)(base + sizeof(Elf32_Ehdr));
for
(i=
0
;i<ehdr->e_phnum;i++)
{
if
(t_phdr->p_type == PT_LOAD)
{
//这里的flag只是一个标志位,去除第一个LOAD的Segment的值
if
(flag ==
0
)
{
load1 = t_phdr;
flag =
1
;
nModuleBase = load1->p_vaddr;
printf(load1 = %p, offset = 0x%x
, load1, load1->p_offset);
}
else
{
load2 = t_phdr;
printf(load2 = %p, offset = 0x%x
, load2, load2->p_offset);
}
}
if
(t_phdr->p_type == PT_DYNAMIC)
{
dynamic = t_phdr;
printf(dynamic = %p, offset = 0x%x
, dynamic, dynamic->p_offset);
}
t_phdr ++;
}
//section header
s_hdr = (Elf32_Shdr*)(base + ehdr->e_shoff);
//获取到新加section的位置,这个是重点,需要进行页面对其操作
printf(addr:0x%x
,load2->p_paddr);
nNewSecAddr = ALIGN(load2->p_paddr + load2->p_memsz - nModuleBase, load2->p_align);
printf(
new
section add:%x
, nNewSecAddr);
if
(load1->p_filesz < ALIGN(load2->p_paddr + load2->p_memsz, load2->p_align) )
{
printf(offset:%x
,(ehdr->e_shoff + sizeof(Elf32_Shdr) * ehdr->e_shnum));
//注意这里的代码的执行条件,这里其实就是判断section header是不是在文件的末尾
if
( (ehdr->e_shoff + sizeof(Elf32_Shdr) * ehdr->e_shnum) != mapSZ)
{
if
(mapSZ + sizeof(Elf32_Shdr) * (ehdr->e_shnum +
1
) > nNewSecAddr)
{
printf(无法添加节
);
return
3
;
}
else
{
memcpy(base + mapSZ, base + ehdr->e_shoff, sizeof(Elf32_Shdr) * ehdr->e_shnum);
//将Section Header拷贝到原来文件的末尾
ehdr->e_shoff = mapSZ;
mapSZ += sizeof(Elf32_Shdr) * ehdr->e_shnum;
//加上Section Header的长度
s_hdr = (Elf32_Shdr*)(base + ehdr->e_shoff);
printf(ehdr_offset:%x,ehdr->e_shoff);
}
}
}
else
{
nNewSecAddr = load1->p_filesz;
}
printf(还可添加 %d 个节
, (nNewSecAddr - ehdr->e_shoff) / sizeof(Elf32_Shdr) - ehdr->e_shnum -
1
);
int
nWriteLen = nNewSecAddr + ALIGN(strlen(szSecname) +
1
,
0x10
) + nNewSecSize;
//添加section之后的文件总长度:原来的长度 + section name + section size
printf(write len %x
,nWriteLen);
char
*lpWriteBuf = (
char
*)malloc(nWriteLen);
//nWriteLen :最后文件的总大小
memset(lpWriteBuf,
0
, nWriteLen);
//ehdr->e_shstrndx是section name的string表在section表头中的偏移值,修改string段的大小
s_hdr[ehdr->e_shstrndx].sh_size = nNewSecAddr - s_hdr[ehdr->e_shstrndx].sh_offset + strlen(szSecname) +
1
;
strcpy(lpWriteBuf + nNewSecAddr, szSecname);
//添加section name
//以下代码是构建一个Section Header
Elf32_Shdr newSecShdr = {
0
};
newSecShdr.sh_name = nNewSecAddr - s_hdr[ehdr->e_shstrndx].sh_offset;
newSecShdr.sh_type = SHT_PROGBITS;
newSecShdr.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
nNewSecAddr += ALIGN(strlen(szSecname) +
1
,
0x10
);
newSecShdr.sh_size = nNewSecSize;
newSecShdr.sh_offset = nNewSecAddr;
newSecShdr.sh_addr = nNewSecAddr + nModuleBase;
newSecShdr.sh_addralign =
4
;
//修改Program Header信息
load1->p_filesz = nWriteLen;
load1->p_memsz = nNewSecAddr + nNewSecSize;
load1->p_flags =
7
;
//可读 可写 可执行
//修改Elf header中的section的count值
ehdr->e_shnum++;
memcpy(lpWriteBuf, base, mapSZ);
//从base中拷贝mapSZ长度的字节到lpWriteBuf
memcpy(lpWriteBuf + mapSZ, &newSecShdr, sizeof(Elf32_Shdr));
//将新加的Section Header追加到lpWriteBuf末尾
//写文件
fseek(fdw,
0
, SEEK_SET);
fwrite(lpWriteBuf,
1
, nWriteLen, fdw);
fclose(fdw);
fclose(fdr);
free(base);
free(lpWriteBuf);
return
0
;
}</ehdr-></stdio.h></string.h></iostream.h></pre>
<p> </p>
<p> </p>
看了C++代码解析之后,这里不得不多说两句了,看看C++中的代码多么简单,原因很简单:在做文件字节操作的时候,C++中的指针真的很牛逼的,这个也是Java望成莫及的。。
<p> </p>
<h1>第五、总结</h1>
<p>关于Elf文件的格式,就介绍到这里,通过自己写一个解析类的话,可以很深刻的了解elf文件的格式,所以我们在以后遇到一个文件格式的了解过程中,最好的方式就是手动的写一个工具类就好了。那么这篇文章是逆向之旅的第一篇,也是以后篇章的基础,下面一篇文章我们会介绍如何来手动的在elf中添加一个段数据结构,尽情期待~~</p>
<p> </p>
<p> </p>
</header_count;i++){></pre>
</header_count;i++){></pre>
</phdrlist.size();i++){></symlist.size();i++){></elf32_strtb></elf32_strtb></elf32_shdr></elf32_shdr></elf32_phdr></elf32_phdr></elf32_sym></elf32_sym>
|