int
harden_handle(
char
*src_path,
char
*name,
char
*dst_path,
int
mode)
{
Elf64_Ehdr header_;
Elf64_Phdr phdr_;
Elf64_Dyn dyn_;
int
dyn_off;
int
dyn_size;
int
dyn_count;
Elf64_Addr dyn_symtab;
Elf64_Addr dyn_strtab;
Elf64_Addr dyn_gnuhash;
int
dyn_strsz;
uint32_t symndex;
size_t
gnu_nbucket_;
uint32_t* gnu_bucket_;
uint32_t* gnu_chain_;
uint32_t gnu_maskwords_;
uint32_t gnu_shift2_;
Elf64_Addr *gnu_bloom_filter_;
int
fd = open(src_path, O_RDONLY);
if
(fd == -1) {
LOGI(
"error opening file"
);
return
-1;
}
int
ret = read(fd, &header_,
sizeof
(header_));
if
(ret < 0) {
LOGI(
"error read file!"
);
}
if
(header_.e_ident[EI_MAG0] != ELFMAG0 ||
header_.e_ident[EI_MAG1] != ELFMAG1 ||
header_.e_ident[EI_MAG2] != ELFMAG2 ||
header_.e_ident[EI_MAG3] != ELFMAG3) {
LOGI(
"\"%s\" has bad ELF magic"
, src_path);
return
-1;
}
if
(header_.e_ident[EI_CLASS] != ELFCLASS64) {
LOGI(
"\"%s\" not 32-bit: %d"
, src_path, header_.e_ident[EI_CLASS]);
return
-1;
}
if
(header_.e_ident[EI_DATA] != ELFDATA2LSB) {
LOGI(
"\"%s\" not little-endian: %d"
, src_path, header_.e_ident[EI_DATA]);
return
-1;
}
if
(header_.e_type != ET_DYN) {
LOGI(
"\"%s\" has unexpected e_type: %d"
, src_path, header_.e_type);
return
-1;
}
if
(header_.e_version != EV_CURRENT) {
LOGI(
"\"%s\" has unexpected e_version: %d"
, src_path, header_.e_version);
return
-1;
}
lseek(fd, header_.e_phoff, SEEK_SET);
for
(
int
i = 0; i < header_.e_phnum; i++) {
ret = read(fd, &phdr_,
sizeof
(phdr_));
if
(ret < 0) {
LOGI(
"error read file!"
);
}
LOGI(
"phdr_.p_type:%d\n"
, phdr_.p_type);
if
(phdr_.p_type != PT_DYNAMIC) {
continue
;
}
dyn_off = phdr_.p_offset;
dyn_size = phdr_.p_filesz;
dyn_count = phdr_.p_memsz / (8 * 2);
}
lseek(fd, dyn_off, SEEK_SET);
for
(
int
i = 0; i < dyn_count; i++) {
ret = read(fd, &dyn_,
sizeof
(dyn_));
if
(ret < 0) {
LOGI(
"error read file!"
);
}
switch
(dyn_.d_tag) {
case
DT_SONAME:
break
;
case
DT_GNU_HASH:
dyn_gnuhash = dyn_.d_un.d_ptr;
break
;
case
DT_HASH:
break
;
case
DT_SYMTAB:
dyn_symtab = dyn_.d_un.d_ptr;
break
;
case
DT_SYMENT:
break
;
case
DT_STRTAB:
dyn_strtab = dyn_.d_un.d_ptr;
break
;
case
DT_STRSZ:
dyn_strsz = dyn_.d_un.d_val;
break
;
}
}
char
*dynstr = (
char
*)
malloc
(dyn_strsz);
if
(dynstr == NULL){
LOGI(
"malloc failed"
);
}
lseek(fd, dyn_strtab, SEEK_SET);
ret = read(fd, dynstr, dyn_strsz);
if
(ret < 0) {
LOGI(
"read .dynstr failed"
);
}
lseek(fd, dyn_gnuhash, SEEK_SET);
ret = read(fd, &gnu_nbucket_, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
gnu_nbucket_ = gnu_nbucket_ & 0xffff;
lseek(fd, dyn_gnuhash + 4, SEEK_SET);
ret = read(fd, &symndex, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
lseek(fd, dyn_gnuhash + 8, SEEK_SET);
ret = read(fd, &gnu_maskwords_, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
lseek(fd, dyn_gnuhash + 8, SEEK_SET);
ret = read(fd, &gnu_maskwords_, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
lseek(fd, dyn_gnuhash + 12, SEEK_SET);
ret = read(fd, &gnu_shift2_, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
gnu_bloom_filter_ =
reinterpret_cast
<Elf64_Addr *>(dyn_gnuhash + 16);
gnu_bucket_ =
reinterpret_cast
<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - symndex;
uint32_t hash = gnu_hash(name);
uint32_t h2 = hash >> gnu_shift2_;
uint32_t bloom_mask_bits =
sizeof
(Elf64_Addr) * 8;
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
uint32_t val = hash % gnu_nbucket_;
uint32_t n;
lseek(fd,
reinterpret_cast
<uint64_t> (gnu_bucket_ + val), SEEK_SET);
ret = read(fd, &n, 4);
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
Elf64_Sym s;
uint32_t chain;
do
{
lseek(fd, dyn_symtab + n *
sizeof
(Elf64_Sym), SEEK_SET);
ret = read(fd, &s,
sizeof
(Elf64_Sym));
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
LOGI(
"name = %d %s"
, s.st_name, dynstr + s.st_name);
lseek(fd,
reinterpret_cast
<uint64_t>(gnu_chain_ + n), SEEK_SET);
ret = read(fd, &chain,
sizeof
(chain));
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
if
(((chain ^ hash) >> 1) == 0 &&
strcmp
(dynstr + s.st_name, name) == 0) {
LOGI(
"found function(%s) at %p(%zd)"
, name,
reinterpret_cast
<
void
*>(s.st_value),
static_cast
<
size_t
>(s.st_size));
break
;
}
n++;
lseek(fd,
reinterpret_cast
<uint64_t>(gnu_chain_ + n), SEEK_SET);
ret = read(fd, &chain,
sizeof
(chain));
if
(ret < 0) {
LOGI(
"read gnuhash failed"
);
}
}
while
((chain & 1) == 0);
uint32_t size = get_file_size(src_path);
char
*file_buf = (
char
*)
malloc
(size);
if
(file_buf == NULL) {
LOGI(
"file buf malloc failed"
);
}
lseek(fd, 0, SEEK_SET);
ret = read(fd, file_buf, size);
if
(ret < 0) {
LOGI(
"read file buf failed"
);
}
close(fd);
char
save_path[128] = {0};
fd = open(dst_path, O_RDWR | O_CREAT);
char
*encrypt_buf = (
char
*)
malloc
(s.st_size);
encrypt((unsigned
char
*) RC4_KEY,
reinterpret_cast
<unsigned
char
*>(encrypt_buf),
reinterpret_cast
<unsigned
char
*>(&file_buf[s.st_value]), s.st_size);
memcpy
(&file_buf[s.st_value], encrypt_buf, s.st_size);
if
(mode == MODE_DUPLEX) {
for
(
int
i = 0; i < header_.e_phnum *
sizeof
(phdr_); i++) {
file_buf[header_.e_phoff + i] = file_buf[header_.e_phoff + i] ^ XOR_MAGIC;
}
}
write(fd, file_buf, size);
free
(encrypt_buf);
close(fd);
return
0;
}