简单分享一下学习到的So加固的方案,前前后后实现差不多用了两周的时间,实践下来也学到了很多关于So的知识。如果文章中有讲述错误的地方,欢迎各位大佬斧正,谢谢。本文章的代码基于关于SO加密对抗的两种实现方式
在看本篇文章之前,最好需要了解一下ELF文件格式,以及So的加载流程,这里推荐oacia大佬的两篇文章。ELF结构分析及ElfReader和安卓so加载流程源码分析。
下面是编译为libmathlib.so前的源代码,我们将要加密int mymyadd(int a, int b)
未加密前

加密后,IDA自然无法正确识别出函数

加密函数,首先自然要从ELF文件中找到函数的位置以及函数的大小。
这里看一下源码中dlsym函数怎么处理的。
调用了__loader_dlsym(handle, symbol, caller_addr)
调用了dlsym_impl(handle, symbol, nullptr, caller_addr);
调用了do_dlsym(handle, symbol, version, caller_addr, &result)
if (handle == RTLD_DEFAULT || handle == RTLD_NEXT)
是判断特殊句柄,所以我们的情况走的是else
语句。那么调用了dlsym_handle_lookup(si, &found, sym_name, vi)
进入if语句也是调用了dlsym_handle_lookup_impl
调用了current_soinfo->find_symbol_by_name(symbol_name, vi);
这里根据设置了FLAG_GNU_HASH
标志位选择使用GNU哈希查找
还是ELF哈希查找
。GNU哈希是一种更现代、更高效的符号查找方法,特别针对大型库进行了优化。
这里我将两个函数都粘贴出来,我们选择学习GNU哈希查找
根据函数得知,需要计算得到gnu_hash
以及其他例如布隆过滤器掩码字数gnu_maskwords_
,布隆过滤器数组gnu_bloom_filter_
,符号表地址symtab_
等。
其中gnu_hash
值简单,只需要按照下面的函数计算即可
至于gnu_maskwords_
,gnu_bloom_filter_
,符号表地址symtab_
等
按照源码中的提示,就更好解决了。通过ELF Header
定位到Program Header Table
,遍历Program Header Table
,找到类型为PT_DYNAMIC
的动态段的偏移量和大小,这个段包含了动态链接的关键信息。GNU哈希表地址和符号表地址都可以通过分析动态段中的条目得到
具体分析已经完毕,实现代码如下
注意这里原作者定义了FUNC_SIZE
常量,如果不想这么做就得跟加密一样去解析ELF文件找动态段来遍历获取函数大小。
1.dlopen
打开库文件用dlsym
获取函数地址
2.修改内存权限,解密覆盖函数地址范围,恢复内存权限
3.调用函数运行
4.修改内存权限,加密覆盖函数地址范围,恢复内存权限

在方式一的基础上,对每个段头的type进行了异或处理。
加密代码前面已经贴出来了,就是当传入参数mode = MODE_DUPLEX
时参与的加密。该方法具体的调用流程可以看原作者的文章,自定义linker对so加载分析得非常好。
只是这种方式的加密下,使用IDA仍能打开so文件静态分析,只修改了Program Header Table
,而没有修改Section Header Table
,所以IDA仍然能通过节头表获取大部分需要的信息。因此我在这里分享第三种so加密方法,自定义ELF文件格式,并且根据android源码自定义linker加载。
该方法加密后的so文件IDA肯定是无法解析的。

左边部分为原本标准格式未加密so文件,右边为自定义格式且RC4加密后so文件

左边部分为原本标准格式未加密so文件,右边为自定义格式且RC4解密后so文件

其中自定义的文件头Custom_Elf64_Ehdr
自然可以不用与标准的文件头一样,这里我为了方便实现就没有变动。实际情况中可以调换顺序,调用时用计算的偏移,或者直接删除没有用到的部分都可以。e_ident
也可以不只改魔数头,其他删掉都可以,毕竟linker都有我们自己实现了,也没必要叫校验了。
那么现在有两个选择,一种是恢复为标准ELF文件格式后加载,另一种则是直接从自定义ELF文件格式加载。我选择从自定义ELF文件格式加载。
但是如果想学习自定义ELF文件格式加载,建议先恢复为标准ELF文件格式,加载成功后,对照修改。
按照android加载so的流程来
这里传入的file_offset
是ELF Header
的偏移,一般标准ELF文件的偏移都是0,所以这里直接置0,而自定义的ELF的ELF Header
偏移则用origin_file_size_
保存一下,后面有用
在这里传入的参数file_offset
是ELF Header
的偏移,正常so文件的偏移都是0,所以我直接置0,这样可以保证后面映射到内存时不出错,而把自定义的格式ELF Header
的偏移使用origin_file_size_
保存了起来。
在这里就可以把刚才保存的origin_file_size_
使用起来,使得能够正确的读到so文件头。
跟源码差不多,只是ELF的魔数头需要替换为我们自定义的.csf
,并且这个校验步骤完全可以省略掉。
代码部分跟源码一样
其中主要说一下FindPhdr()
,在这个程序中有一个很坑的点在于后面的一步si_->phdr = elfreader->loaded_phdr();
。在Program Header Table
中如果有p_type == PT_PHDR
的段,那么该段类型的数组元素如果存在的话,则给出了程序头部表自身的大小和位置,既包括在文件中也包括在内存中的信息。也就是说段中的p_vaddr
和p_offset
的值是原来Program Header Table
的偏移值,这就会导致si_->phdr = elfreader->loaded_phdr();
指向的是错误的内存。这里我的解决办法是直接判断是不是自定义格式的ELF文件,如果是则让loaded_phdr_ = phdr_table_
。
我们用dlopen打开一个空壳so,然后填入我们自己so的信息,达到替换的操作。这里的关键点在于实现soinfo_from_handle
函数,这个函数由于并没有导出,所以需要自己实现。
我们仔细看一下这个函数的源码,发现其实只用到了g_soinfo_handles_map
这个全局变量是不知道的。再参考原文使用的是偏移地址,那么我们完善一下,解析linker64
这个ELF文件,就可以适用于不同的安卓版本了。
后面的预链接
与链接
过程则与原文一样了。
调用日志


我们使用libjoke.so
作为我们的壳,所以我们自然要在soinfolist中找的是libjoke.so,使用frida打印一下。红线上面部分是soinfolist遍历的结构,下面部分是打印的是ELF文件的前64字节数据,正是我们之前3.1节
图中使用RC4解密后传入的数据。

这里三种方法可以并不止局限于一种,可以搭配使用。既然都是自定义linker加载了,自然so可以放在更隐蔽的地方。在这里call_constructors
没有实现,有兴趣的朋友可以自己实现一下。具体完整代码我就不贴出来了,里面各种各样调试信息绕来绕去没删除,写得太乱了。
void
* dlsym(
void
* handle,
const
char
* symbol) {
const
void
* caller_addr = __builtin_return_address(0);
return
__loader_dlsym(handle, symbol, caller_addr);
}
void
* dlsym(
void
* handle,
const
char
* symbol) {
const
void
* caller_addr = __builtin_return_address(0);
return
__loader_dlsym(handle, symbol, caller_addr);
}
void
* __loader_dlsym(
void
* handle,
const
char
* symbol,
const
void
* caller_addr) {
return
dlsym_impl(handle, symbol, nullptr, caller_addr);
}
void
* __loader_dlsym(
void
* handle,
const
char
* symbol,
const
void
* caller_addr) {
return
dlsym_impl(handle, symbol, nullptr, caller_addr);
}
void
* dlsym_impl(
void
* handle,
const
char
* symbol,
const
char
* version,
const
void
* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
g_linker_logger.ResetState();
void
* result;
if
(!do_dlsym(handle, symbol, version, caller_addr, &result)) {
__bionic_format_dlerror(linker_get_error_buffer(), nullptr);
return
nullptr;
}
return
result;
}
void
* dlsym_impl(
void
* handle,
const
char
* symbol,
const
char
* version,
const
void
* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
g_linker_logger.ResetState();
void
* result;
if
(!do_dlsym(handle, symbol, version, caller_addr, &result)) {
__bionic_format_dlerror(linker_get_error_buffer(), nullptr);
return
nullptr;
}
return
result;
}
bool
do_dlsym(
void
* handle,
const
char
* sym_name,
const
char
* sym_ver,
const
void
* caller_addr,
void
** symbol) {
ScopedTrace trace(
"dlsym"
);
#if !defined(__LP64__)
if
(handle == nullptr) {
DL_SYM_ERR(
"dlsym failed: library handle is null"
);
return
false
;
}
#endif
soinfo* found = nullptr;
const
ElfW(Sym)* sym = nullptr;
soinfo* caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);
soinfo* si = nullptr;
if
(handle != RTLD_DEFAULT && handle != RTLD_NEXT) {
si = soinfo_from_handle(handle);
}
LD_LOG(kLogDlsym,
"dlsym(handle=%p(\"%s\"), sym_name=\"%s\", sym_ver=\"%s\", caller=\"%s\", caller_ns=%s@%p) ..."
,
handle,
si != nullptr ? si->get_realpath() :
"n/a"
,
sym_name,
sym_ver,
caller == nullptr ?
"(null)"
: caller->get_realpath(),
ns == nullptr ?
"(null)"
: ns->get_name(),
ns);
auto
failure_guard = android::base::make_scope_guard(
[&]() { LD_LOG(kLogDlsym,
"... dlsym failed: %s"
, linker_get_error_buffer()); });
if
(sym_name == nullptr) {
DL_SYM_ERR(
"dlsym failed: symbol name is null"
);
return
false
;
}
version_info vi_instance;
version_info* vi = nullptr;
if
(sym_ver != nullptr) {
vi_instance.name = sym_ver;
vi_instance.elf_hash = calculate_elf_hash(sym_ver);
vi = &vi_instance;
}
if
(handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle);
}
else
{
if
(si == nullptr) {
DL_SYM_ERR(
"dlsym failed: invalid handle: %p"
, handle);
return
false
;
}
sym = dlsym_handle_lookup(si, &found, sym_name, vi);
}
if
(sym != nullptr) {
uint32_t bind = ELF_ST_BIND(sym->st_info);
uint32_t type = ELF_ST_TYPE(sym->st_info);
if
((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
if
(type == STT_TLS) {
const
soinfo_tls* tls_module = found->get_tls();
if
(tls_module == nullptr) {
DL_SYM_ERR(
"TLS symbol \"%s\" in solib \"%s\" with no TLS segment"
,
sym_name, found->get_realpath());
return
false
;
}
void
* tls_block = get_tls_block_for_this_thread(tls_module,
true
);
*symbol =
static_cast
<
char
*>(tls_block) + sym->st_value;
}
else
{
*symbol =
reinterpret_cast
<
void
*>(found->resolve_symbol_address(sym));
}
failure_guard.Disable();
LD_LOG(kLogDlsym,
"... dlsym successful: sym_name=\"%s\", sym_ver=\"%s\", found in=\"%s\", address=%p"
,
sym_name, sym_ver, found->get_soname(), *symbol);
return
true
;
}
DL_SYM_ERR(
"symbol \"%s\" found but not global"
, symbol_display_name(sym_name, sym_ver).c_str());
return
false
;
}
DL_SYM_ERR(
"undefined symbol: %s"
, symbol_display_name(sym_name, sym_ver).c_str());
return
false
;
}
bool
do_dlsym(
void
* handle,
const
char
* sym_name,
const
char
* sym_ver,
const
void
* caller_addr,
void
** symbol) {
ScopedTrace trace(
"dlsym"
);
#if !defined(__LP64__)
if
(handle == nullptr) {
DL_SYM_ERR(
"dlsym failed: library handle is null"
);
return
false
;
}
#endif
soinfo* found = nullptr;
const
ElfW(Sym)* sym = nullptr;
soinfo* caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);
soinfo* si = nullptr;
if
(handle != RTLD_DEFAULT && handle != RTLD_NEXT) {
si = soinfo_from_handle(handle);
}
LD_LOG(kLogDlsym,
"dlsym(handle=%p(\"%s\"), sym_name=\"%s\", sym_ver=\"%s\", caller=\"%s\", caller_ns=%s@%p) ..."
,
handle,
si != nullptr ? si->get_realpath() :
"n/a"
,
sym_name,
sym_ver,
caller == nullptr ?
"(null)"
: caller->get_realpath(),
ns == nullptr ?
"(null)"
: ns->get_name(),
ns);
auto
failure_guard = android::base::make_scope_guard(
[&]() { LD_LOG(kLogDlsym,
"... dlsym failed: %s"
, linker_get_error_buffer()); });
if
(sym_name == nullptr) {
DL_SYM_ERR(
"dlsym failed: symbol name is null"
);
return
false
;
}
version_info vi_instance;
version_info* vi = nullptr;
if
(sym_ver != nullptr) {
vi_instance.name = sym_ver;
vi_instance.elf_hash = calculate_elf_hash(sym_ver);
vi = &vi_instance;
}
if
(handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle);
}
else
{
if
(si == nullptr) {
DL_SYM_ERR(
"dlsym failed: invalid handle: %p"
, handle);
return
false
;
}
sym = dlsym_handle_lookup(si, &found, sym_name, vi);
}
if
(sym != nullptr) {
uint32_t bind = ELF_ST_BIND(sym->st_info);
uint32_t type = ELF_ST_TYPE(sym->st_info);
if
((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
if
(type == STT_TLS) {
const
soinfo_tls* tls_module = found->get_tls();
if
(tls_module == nullptr) {
DL_SYM_ERR(
"TLS symbol \"%s\" in solib \"%s\" with no TLS segment"
,
sym_name, found->get_realpath());
return
false
;
}
void
* tls_block = get_tls_block_for_this_thread(tls_module,
true
);
*symbol =
static_cast
<
char
*>(tls_block) + sym->st_value;
}
else
{
*symbol =
reinterpret_cast
<
void
*>(found->resolve_symbol_address(sym));
}
failure_guard.Disable();
LD_LOG(kLogDlsym,
"... dlsym successful: sym_name=\"%s\", sym_ver=\"%s\", found in=\"%s\", address=%p"
,
sym_name, sym_ver, found->get_soname(), *symbol);
return
true
;
}
DL_SYM_ERR(
"symbol \"%s\" found but not global"
, symbol_display_name(sym_name, sym_ver).c_str());
return
false
;
}
DL_SYM_ERR(
"undefined symbol: %s"
, symbol_display_name(sym_name, sym_ver).c_str());
return
false
;
}
static
const
ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
soinfo** found,
const
char
* name,
const
version_info* vi) {
if
(si == solist_get_somain()) {
return
dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
}
SymbolName symbol_name(name);
return
dlsym_handle_lookup_impl(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
}
static
const
ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
soinfo** found,
const
char
* name,
const
version_info* vi) {
if
(si == solist_get_somain()) {
return
dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
}
SymbolName symbol_name(name);
return
dlsym_handle_lookup_impl(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
}
static
const
ElfW(Sym)* dlsym_handle_lookup_impl(android_namespace_t* ns,
soinfo* root,
soinfo* skip_until,
soinfo** found,
SymbolName& symbol_name,
const
version_info* vi) {
const
ElfW(Sym)* result = nullptr;
bool
skip_lookup = skip_until != nullptr;
walk_dependencies_tree(root, [&](soinfo* current_soinfo) {
if
(skip_lookup) {
skip_lookup = current_soinfo != skip_until;
return
kWalkContinue;
}
if
(!ns->is_accessible(current_soinfo)) {
return
kWalkSkip;
}
result = current_soinfo->find_symbol_by_name(symbol_name, vi);
if
(result != nullptr) {
*found = current_soinfo;
return
kWalkStop;
}
return
kWalkContinue;
});
return
result;
}
static
const
ElfW(Sym)* dlsym_handle_lookup_impl(android_namespace_t* ns,
soinfo* root,
soinfo* skip_until,
soinfo** found,
SymbolName& symbol_name,
const
version_info* vi) {
const
ElfW(Sym)* result = nullptr;
bool
skip_lookup = skip_until != nullptr;
walk_dependencies_tree(root, [&](soinfo* current_soinfo) {
if
(skip_lookup) {
skip_lookup = current_soinfo != skip_until;
return
kWalkContinue;
}
if
(!ns->is_accessible(current_soinfo)) {
return
kWalkSkip;
}
result = current_soinfo->find_symbol_by_name(symbol_name, vi);
if
(result != nullptr) {
*found = current_soinfo;
return
kWalkStop;
}
return
kWalkContinue;
});
return
result;
}
const
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name,
const
version_info* vi)
const
{
return
is_gnu_hash() ? gnu_lookup(symbol_name, vi) : elf_lookup(symbol_name, vi);
}
const
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name,
const
version_info* vi)
const
{
return
is_gnu_hash() ? gnu_lookup(symbol_name, vi) : elf_lookup(symbol_name, vi);
}
const
ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name,
const
version_info* vi)
const
{
const
uint32_t hash = symbol_name.gnu_hash();
constexpr uint32_t kBloomMaskBits =
sizeof
(ElfW(Addr)) * 8;
const
uint32_t word_num = (hash / kBloomMaskBits) & gnu_maskwords_;
const
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
const
uint32_t h1 = hash % kBloomMaskBits;
const
uint32_t h2 = (hash >> gnu_shift2_) % kBloomMaskBits;
LD_DEBUG(lookup,
"SEARCH %s in %s@%p (gnu)"
,
symbol_name.get_name(), get_realpath(),
reinterpret_cast
<
void
*>(base));
if
((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 0) {
return
nullptr;
}
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if
(n == 0) {
return
nullptr;
}
const
ElfW(Versym) verneed = find_verdef_version_index(
this
, vi);
const
ElfW(Versym)* versym = get_versym_table();
do
{
ElfW(Sym)* s = symtab_ + n;
if
(((gnu_chain_[n] ^ hash) >> 1) == 0 &&
check_symbol_version(versym, n, verneed) &&
strcmp
(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(
this
, s)) {
return
symtab_ + n;
}
}
while
((gnu_chain_[n++] & 1) == 0);
return
nullptr;
}
const
ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name,
const
version_info* vi)
const
{
const
uint32_t hash = symbol_name.gnu_hash();
constexpr uint32_t kBloomMaskBits =
sizeof
(ElfW(Addr)) * 8;
const
uint32_t word_num = (hash / kBloomMaskBits) & gnu_maskwords_;
const
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
const
uint32_t h1 = hash % kBloomMaskBits;
const
uint32_t h2 = (hash >> gnu_shift2_) % kBloomMaskBits;
LD_DEBUG(lookup,
"SEARCH %s in %s@%p (gnu)"
,
symbol_name.get_name(), get_realpath(),
reinterpret_cast
<
void
*>(base));
if
((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 0) {
return
nullptr;
}
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if
(n == 0) {
return
nullptr;
}
const
ElfW(Versym) verneed = find_verdef_version_index(
this
, vi);
const
ElfW(Versym)* versym = get_versym_table();
do
{
ElfW(Sym)* s = symtab_ + n;
if
(((gnu_chain_[n] ^ hash) >> 1) == 0 &&
check_symbol_version(versym, n, verneed) &&
strcmp
(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(
this
, s)) {
return
symtab_ + n;
}
}
while
((gnu_chain_[n++] & 1) == 0);
return
nullptr;
}
const
ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name,
const
version_info* vi)
const
{
uint32_t hash = symbol_name.elf_hash();
LD_DEBUG(lookup,
"SEARCH %s in %s@%p h=%x(elf) %zd"
,
symbol_name.get_name(), get_realpath(),
reinterpret_cast
<
void
*>(base), hash, hash % nbucket_);
const
ElfW(Versym) verneed = find_verdef_version_index(
this
, vi);
const
ElfW(Versym)* versym = get_versym_table();
for
(uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym)* s = symtab_ + n;
if
(check_symbol_version(versym, n, verneed) &&
strcmp
(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(
this
, s)) {
return
symtab_ + n;
}
}
return
nullptr;
}
const
ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name,
const
version_info* vi)
const
{
uint32_t hash = symbol_name.elf_hash();
LD_DEBUG(lookup,
"SEARCH %s in %s@%p h=%x(elf) %zd"
,
symbol_name.get_name(), get_realpath(),
reinterpret_cast
<
void
*>(base), hash, hash % nbucket_);
const
ElfW(Versym) verneed = find_verdef_version_index(
this
, vi);
const
ElfW(Versym)* versym = get_versym_table();
for
(uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym)* s = symtab_ + n;
if
(check_symbol_version(versym, n, verneed) &&
strcmp
(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(
this
, s)) {
return
symtab_ + n;
}
}
return
nullptr;
}
static
uint32_t gnu_hash(
const
char
*s0)
{
const
unsigned
char
*s = (
void
*)s0;
uint_fast32_t h = 5381;
for
(; *s; s++)
h += h*32 + *s;
return
h;
}
static
uint32_t gnu_hash(
const
char
*s0)
{
const
unsigned
char
*s = (
void
*)s0;
uint_fast32_t h = 5381;
for
(; *s; s++)
h += h*32 + *s;
return
h;
}
case
DT_GNU_HASH:
gnu_nbucket_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
gnu_maskwords_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[2];
gnu_shift2_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[3];
gnu_bloom_filter_ =
reinterpret_cast
<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16);
gnu_bucket_ =
reinterpret_cast
<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
gnu_chain_ = gnu_bucket_ + gnu_nbucket_ -
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
if
(!powerof2(gnu_maskwords_)) {
DL_ERR(
"invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two"
,
gnu_maskwords_, get_realpath());
return
false
;
}
--gnu_maskwords_;
flags_ |= FLAG_GNU_HASH;
break
;
case
DT_GNU_HASH:
gnu_nbucket_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
gnu_maskwords_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[2];
gnu_shift2_ =
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[3];
gnu_bloom_filter_ =
reinterpret_cast
<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16);
gnu_bucket_ =
reinterpret_cast
<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
gnu_chain_ = gnu_bucket_ + gnu_nbucket_ -
reinterpret_cast
<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
if
(!powerof2(gnu_maskwords_)) {
DL_ERR(
"invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two"
,
gnu_maskwords_, get_realpath());
return
false
;
}
--gnu_maskwords_;
flags_ |= FLAG_GNU_HASH;
break
;
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;
uint32_t gnu_nbucket_;
uint32_t gnu_maskwords_;
uint32_t gnu_shift2_;
Elf64_Addr *gnu_bloom_filter_;
uint32_t* gnu_bucket_;
uint32_t* gnu_chain_;
bool
has_gnu_hash =
false
;
int
fd = open(src_path, O_RDONLY);
if
(fd == -1) {
LOGE(
"error opening source file"
);
return
-1;
}
int
ret = read(fd, &header_,
sizeof
(header_));
if
(ret < 0) {
LOGE(
"error read file!"
);
}
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) {
LOGE(
"error read file!"
);
}
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_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){
LOGE(
"malloc failed"
);
}
lseek(fd, dyn_strtab, SEEK_SET);
ret = read(fd, dynstr, dyn_strsz);
if
(ret < 0) {
LOGE(
"read .dynstr failed"
);
}
gnu_nbucket_ = dyn_gnuhash[0];
uint32_t symndx = dyn_gnuhash[1];
gnu_maskwords_ = dyn_gnuhash[2];
gnu_shift2_ = dyn_gnuhash[3];
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_ - symndx;
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 = gnu_bucket_[hash % gnu_nbucket_];
if
(n == 0) {
printf
(
"符号'%s'所在哈希桶为空\n"
, name);
return
false
;
}
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, 0644);
if
(fd == -1) {
LOGI(
"error opening file, %s"
, dst_path);
return
-1;
}
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]), FUNC_SIZE);
memcpy
(&file_buf[s.st_value], encrypt_buf, FUNC_SIZE);
if
(mode == MODE_DUPLEX) {
for
(
int
i = 0; i < header_.e_phnum; i++) {
file_buf[header_.e_phoff + i *
sizeof
(phdr_)] = file_buf[header_.e_phoff + i *
sizeof
(phdr_)] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 1] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 1] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 2] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 2] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 3] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 3] ^ XOR_MAGIC;
}
}
write(fd, file_buf, size);
free
(encrypt_buf);
close(fd);
return
0;
}
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;
uint32_t gnu_nbucket_;
uint32_t gnu_maskwords_;
uint32_t gnu_shift2_;
Elf64_Addr *gnu_bloom_filter_;
uint32_t* gnu_bucket_;
uint32_t* gnu_chain_;
bool
has_gnu_hash =
false
;
int
fd = open(src_path, O_RDONLY);
if
(fd == -1) {
LOGE(
"error opening source file"
);
return
-1;
}
int
ret = read(fd, &header_,
sizeof
(header_));
if
(ret < 0) {
LOGE(
"error read file!"
);
}
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) {
LOGE(
"error read file!"
);
}
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_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){
LOGE(
"malloc failed"
);
}
lseek(fd, dyn_strtab, SEEK_SET);
ret = read(fd, dynstr, dyn_strsz);
if
(ret < 0) {
LOGE(
"read .dynstr failed"
);
}
gnu_nbucket_ = dyn_gnuhash[0];
uint32_t symndx = dyn_gnuhash[1];
gnu_maskwords_ = dyn_gnuhash[2];
gnu_shift2_ = dyn_gnuhash[3];
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_ - symndx;
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 = gnu_bucket_[hash % gnu_nbucket_];
if
(n == 0) {
printf
(
"符号'%s'所在哈希桶为空\n"
, name);
return
false
;
}
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, 0644);
if
(fd == -1) {
LOGI(
"error opening file, %s"
, dst_path);
return
-1;
}
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]), FUNC_SIZE);
memcpy
(&file_buf[s.st_value], encrypt_buf, FUNC_SIZE);
if
(mode == MODE_DUPLEX) {
for
(
int
i = 0; i < header_.e_phnum; i++) {
file_buf[header_.e_phoff + i *
sizeof
(phdr_)] = file_buf[header_.e_phoff + i *
sizeof
(phdr_)] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 1] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 1] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 2] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 2] ^ XOR_MAGIC;
file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 3] = file_buf[header_.e_phoff + i *
sizeof
(phdr_) + 3] ^ XOR_MAGIC;
}
}
write(fd, file_buf, size);
free
(encrypt_buf);
close(fd);
return
0;
}
extern
"C"
JNIEXPORT
void
JNICALL
Java_com_example_mylinkerwith21_MainActivity_loadSO(JNIEnv *env, jobject thiz,
jstring jEncryptedFileDirectoryPath) {
void
*lib;
uint64_t start;
uint64_t end;
ssize_t page;
FUNC lib_func = NULL;
RC4_CTX ctx;
char
buf[512] = {0};
int
result;
LOGI(
"loadSO called!"
);
const
char
*encryptedFileDirectoryPath = env->GetStringUTFChars(jEncryptedFileDirectoryPath, nullptr);
if
(encryptedFileDirectoryPath == nullptr) {
LOGE(
"Failed to get string characters for encrypted file directory path"
);
return
;
}
const
char
* encryptedFileName =
"libmathlib_encrypt.so"
;
size_t
requiredPathSize =
strlen
(encryptedFileDirectoryPath) + 1 +
strlen
(encryptedFileName) + 1;
char
* encryptedLoadPath = (
char
*)
malloc
(requiredPathSize);
if
(encryptedLoadPath == nullptr) {
LOGE(
"Failed to allocate memory for encrypted load path"
);
env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
return
;
}
sprintf
(encryptedLoadPath,
"%s/%s"
, encryptedFileDirectoryPath, encryptedFileName);
LOGI(
"Loading from path: %s"
, encryptedLoadPath);
lib = dlopen(encryptedLoadPath, RTLD_LAZY);
free
(encryptedLoadPath);
env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
if
(lib == NULL) {
LOGE(
"%s dlopen failed: %s\n"
, encryptedFileName, dlerror());
return
;
}
lib_func =
reinterpret_cast
<FUNC>(dlsym(lib,
"mymyadd"
));
if
(!lib_func) {
LOGI(
"can't find module symbol 'mymyadd': %s\n"
, dlerror());
dlclose(lib);
return
;
}
LOGI(
"loadso lib_func = %p"
, lib_func);
parse_maps_for_lib(
"libmathlib_encrypt.so"
, &start, &end);
if
(start == 0 && end == 0) {
LOGI(
"Failed to find memory map for libmathlib_encrypt.so"
);
dlclose(lib);
return
;
}
page = end - start;
if
(mprotect((
void
*)start, page, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_WRITE | PROT_EXEC: %s"
,
strerror
(
errno
));
dlclose(lib);
return
;
}
rc4_init(&ctx, (unsigned
char
*) RC4_KEY,
strlen
(
reinterpret_cast
<
const
char
*>(RC4_KEY)));
rc4_run(&ctx,
reinterpret_cast
<unsigned
char
*>(buf),
reinterpret_cast
<unsigned
char
*>(lib_func), FUNC_SIZE);
memcpy
((
void
*)lib_func, buf, FUNC_SIZE);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_EXEC: %s"
,
strerror
(
errno
));
}
result = lib_func(1, 2);
LOGI(
"loadso add result = %d"
, result);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_WRITE | PROT_EXEC during re-encryption: %s"
,
strerror
(
errno
));
}
else
{
rc4_init(&ctx, (unsigned
char
*) RC4_KEY,
strlen
(
reinterpret_cast
<
const
char
*>(RC4_KEY)));
rc4_run(&ctx,
reinterpret_cast
<unsigned
char
*>(buf),
reinterpret_cast
<unsigned
char
*>(lib_func), FUNC_SIZE);
memcpy
((
void
*)lib_func, buf, FUNC_SIZE);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_EXEC after re-encryption: %s"
,
strerror
(
errno
));
}
}
dlclose(lib);
}
extern
"C"
JNIEXPORT
void
JNICALL
Java_com_example_mylinkerwith21_MainActivity_loadSO(JNIEnv *env, jobject thiz,
jstring jEncryptedFileDirectoryPath) {
void
*lib;
uint64_t start;
uint64_t end;
ssize_t page;
FUNC lib_func = NULL;
RC4_CTX ctx;
char
buf[512] = {0};
int
result;
LOGI(
"loadSO called!"
);
const
char
*encryptedFileDirectoryPath = env->GetStringUTFChars(jEncryptedFileDirectoryPath, nullptr);
if
(encryptedFileDirectoryPath == nullptr) {
LOGE(
"Failed to get string characters for encrypted file directory path"
);
return
;
}
const
char
* encryptedFileName =
"libmathlib_encrypt.so"
;
size_t
requiredPathSize =
strlen
(encryptedFileDirectoryPath) + 1 +
strlen
(encryptedFileName) + 1;
char
* encryptedLoadPath = (
char
*)
malloc
(requiredPathSize);
if
(encryptedLoadPath == nullptr) {
LOGE(
"Failed to allocate memory for encrypted load path"
);
env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
return
;
}
sprintf
(encryptedLoadPath,
"%s/%s"
, encryptedFileDirectoryPath, encryptedFileName);
LOGI(
"Loading from path: %s"
, encryptedLoadPath);
lib = dlopen(encryptedLoadPath, RTLD_LAZY);
free
(encryptedLoadPath);
env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
if
(lib == NULL) {
LOGE(
"%s dlopen failed: %s\n"
, encryptedFileName, dlerror());
return
;
}
lib_func =
reinterpret_cast
<FUNC>(dlsym(lib,
"mymyadd"
));
if
(!lib_func) {
LOGI(
"can't find module symbol 'mymyadd': %s\n"
, dlerror());
dlclose(lib);
return
;
}
LOGI(
"loadso lib_func = %p"
, lib_func);
parse_maps_for_lib(
"libmathlib_encrypt.so"
, &start, &end);
if
(start == 0 && end == 0) {
LOGI(
"Failed to find memory map for libmathlib_encrypt.so"
);
dlclose(lib);
return
;
}
page = end - start;
if
(mprotect((
void
*)start, page, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_WRITE | PROT_EXEC: %s"
,
strerror
(
errno
));
dlclose(lib);
return
;
}
rc4_init(&ctx, (unsigned
char
*) RC4_KEY,
strlen
(
reinterpret_cast
<
const
char
*>(RC4_KEY)));
rc4_run(&ctx,
reinterpret_cast
<unsigned
char
*>(buf),
reinterpret_cast
<unsigned
char
*>(lib_func), FUNC_SIZE);
memcpy
((
void
*)lib_func, buf, FUNC_SIZE);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_EXEC: %s"
,
strerror
(
errno
));
}
result = lib_func(1, 2);
LOGI(
"loadso add result = %d"
, result);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_WRITE | PROT_EXEC during re-encryption: %s"
,
strerror
(
errno
));
}
else
{
rc4_init(&ctx, (unsigned
char
*) RC4_KEY,
strlen
(
reinterpret_cast
<
const
char
*>(RC4_KEY)));
rc4_run(&ctx,
reinterpret_cast
<unsigned
char
*>(buf),
reinterpret_cast
<unsigned
char
*>(lib_func), FUNC_SIZE);
memcpy
((
void
*)lib_func, buf, FUNC_SIZE);
if
(mprotect((
void
*)start, page, PROT_READ | PROT_EXEC) == -1) {
LOGI(
"mprotect failed for PROT_READ | PROT_EXEC after re-encryption: %s"
,
strerror
(
errno
));
}
}
dlclose(lib);
}
#pragma pack(push, 1)
typedef
struct
custom_elf64_file{
Elf64_Off elf_header_off;
Elf64_Half elf_header_size;
Elf64_Off elf_program_header_table_off;
Elf64_Half elf_program_header_table_num;
Elf64_Half elf_program_header_table_size;
}Custom_Elf64_File;
#pragma pack(pop)
typedef
struct
{
unsigned
char
e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Custom_Elf64_Ehdr;
int
do_pack(
char
*inputfile_buffer,
size_t
inputfile_size,
char
*outfile_buffer,
size_t
outfile_size)
{
if
(NULL == inputfile_buffer || 0 == inputfile_size || NULL == outfile_buffer) {
return
-1;
}
Elf64_Ehdr* orig_ehdr = (Elf64_Ehdr *)inputfile_buffer;
if
(
memcmp
(orig_ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
return
-2;
}
size_t
phdr_table_size = orig_ehdr->e_phnum * orig_ehdr->e_phentsize;
size_t
enc_ehdr_offset = inputfile_size;
size_t
enc_phdr_offset = enc_ehdr_offset + orig_ehdr->e_ehsize;
size_t
final_size = enc_phdr_offset + phdr_table_size;
if
(final_size > outfile_size) {
return
-3;
}
memcpy
(outfile_buffer, inputfile_buffer, inputfile_size);
Custom_Elf64_File custom_file = {0};
custom_file.elf_header_off = enc_ehdr_offset;
custom_file.elf_header_size = orig_ehdr->e_ehsize;
custom_file.elf_program_header_table_off = enc_phdr_offset;
custom_file.elf_program_header_table_num = orig_ehdr->e_phnum;
custom_file.elf_program_header_table_size = phdr_table_size;
Custom_Elf64_Ehdr custom_ehdr = {0};
memcpy
(&custom_ehdr, orig_ehdr,
sizeof
(Elf64_Ehdr));
memcpy
(custom_ehdr.e_ident,
".csf"
, 4);
reinterpret_cast
<unsigned
char
*>(outfile_buffer + enc_ehdr_offset),
reinterpret_cast
<unsigned
char
*>(&custom_ehdr),
orig_ehdr->e_ehsize);
encrypt((unsigned
char
*)RC4_KEY, RC4_KEY_LEN,
reinterpret_cast
<unsigned
char
*>(outfile_buffer + enc_phdr_offset),
reinterpret_cast
<unsigned
char
*>(inputfile_buffer + orig_ehdr->e_phoff),
phdr_table_size);
Elf64_Phdr *phdr = (Elf64_Phdr *)(inputfile_buffer + orig_ehdr->e_phoff);
for
(
int
i = 0; i < orig_ehdr->e_phnum; i++) {
if
(phdr[i].p_type == PT_LOAD) {
if
(phdr[i].p_offset + phdr[i].p_filesz > inputfile_size) {
return
-4;
}
LOGI(
"第%d个PT_LOAD段,偏移值为%llu,大小为%llu"
, i, phdr[i].p_offset, phdr[i].p_filesz);
encrypt((unsigned
char
*)RC4_KEY, RC4_KEY_LEN,
reinterpret_cast
<unsigned
char
*>(outfile_buffer + phdr[i].p_offset),
reinterpret_cast
<unsigned
char
*>(outfile_buffer + phdr[i].p_offset),
phdr[i].p_filesz);
}
}
memset
(outfile_buffer, 0,
sizeof
(Elf64_Ehdr));
memset
(outfile_buffer + orig_ehdr->e_phoff, 0, phdr_table_size);
memcpy
(outfile_buffer, &custom_file,
sizeof
(Custom_Elf64_File));
return
0;
}
int
unpack(
char
*elf_pack_data,
size_t
file_size, Custom_Elf64_File my_elf64_file)
{
if
(!elf_pack_data) {
return
-1;
}
if
(my_elf64_file.elf_header_size == 0 ||
my_elf64_file.elf_program_header_table_size == 0 ||
my_elf64_file.elf_program_header_table_num == 0) {
return
-2;
}
if
(my_elf64_file.elf_header_off >= file_size ||
my_elf64_file.elf_program_header_table_off >= file_size) {
return
-3;
}
Custom_Elf64_File original_custom_file;
memcpy
(&original_custom_file, elf_pack_data,
sizeof
(Custom_Elf64_File));
char
*decrypted_ehdr = (
char
*)
malloc
(my_elf64_file.elf_header_size);
if
(!decrypted_ehdr) {
return
-4;
}
char
*decrypted_phdr = (
char
*)
malloc
(my_elf64_file.elf_program_header_table_size);
if
(!decrypted_phdr) {
free
(decrypted_ehdr);
return
-5;
}
decrypt((unsigned
char
*)RC4_KEY, RC4_KEY_LEN,
reinterpret_cast
<unsigned
char
*>(decrypted_ehdr),
reinterpret_cast
<unsigned
char
*>(elf_pack_data + my_elf64_file.elf_header_off),
my_elf64_file.elf_header_size);
Elf64_Ehdr *decrypted_elf_header = (Elf64_Ehdr *)decrypted_ehdr;
if
(
memcmp
(decrypted_elf_header->e_ident,
".csf"
, 4) != 0) {
free
(decrypted_ehdr);
free
(decrypted_phdr);
return
-6;
}
decrypt((unsigned
char
*)RC4_KEY, RC4_KEY_LEN,
reinterpret_cast
<unsigned
char
*>(decrypted_phdr),
reinterpret_cast
<unsigned
char
*>(elf_pack_data + my_elf64_file.elf_program_header_table_off),
my_elf64_file.elf_program_header_table_size);
Elf64_Phdr *decrypted_ph_table = (Elf64_Phdr *)decrypted_phdr;
if
(decrypted_elf_header->e_phnum != my_elf64_file.elf_program_header_table_num) {
free
(decrypted_ehdr);
free
(decrypted_phdr);
return
-7;
}
for
(
int
i = 0; i < my_elf64_file.elf_program_header_table_num; i++) {
Elf64_Phdr *current_phdr = &decrypted_ph_table[i];
if
(current_phdr->p_type == PT_LOAD) {
if
(current_phdr->p_offset + current_phdr->p_filesz > file_size) {
free
(decrypted_ehdr);
free
(decrypted_phdr);
return
-8;
}
}
}
for
(
int
i = 0; i < my_elf64_file.elf_program_header_table_num; i++) {
Elf64_Phdr *current_phdr = &decrypted_ph_table[i];
if
(current_phdr->p_type == PT_LOAD && current_phdr->p_filesz > 0) {
decrypt((unsigned
char
*)RC4_KEY, RC4_KEY_LEN,
reinterpret_cast
<unsigned
char
*>(elf_pack_data + current_phdr->p_offset),
reinterpret_cast
<unsigned
char
*>(elf_pack_data + current_phdr->p_offset),
current_phdr->p_filesz);
}
}
memcpy
(elf_pack_data + my_elf64_file.elf_header_off, decrypted_ehdr, my_elf64_file.elf_header_size);
memcpy
(elf_pack_data + my_elf64_file.elf_program_header_table_off, decrypted_phdr, my_elf64_file.elf_program_header_table_size);
memcpy
(elf_pack_data, &original_custom_file,
sizeof
(Custom_Elf64_File));
free
(decrypted_ehdr);
free
(decrypted_phdr);
return
0;
}
int
restore_elf(
char
*decrypted_data,
size_t
file_size, Custom_Elf64_File my_elf64_file,
char
**restored_data,
size_t
*restored_size)
{
if
(!decrypted_data || !restored_data || !restored_size) {
return
-1;
}
Elf64_Ehdr *decrypted_elf_header = (Elf64_Ehdr *)(decrypted_data + my_elf64_file.elf_header_off);
Elf64_Phdr *decrypted_phdr_table = (Elf64_Phdr *)(decrypted_data + my_elf64_file.elf_program_header_table_off);
if
(
memcmp
(decrypted_elf_header->e_ident,
".csf"
, 4) != 0) {
return
-2;
}
size_t
max_segment_end = 0;
for
(
int
i = 0; i < my_elf64_file.elf_program_header_table_num; i++) {
Elf64_Phdr *current_phdr = &decrypted_phdr_table[i];
size_t
segment_end = current_phdr->p_offset + current_phdr->p_filesz;
if
(segment_end > max_segment_end) {
max_segment_end = segment_end;
}
}
size_t
section_table_end = 0;
if
(decrypted_elf_header->e_shoff > 0 && decrypted_elf_header->e_shnum > 0) {
section_table_end = decrypted_elf_header->e_shoff +
decrypted_elf_header->e_shnum * decrypted_elf_header->e_shentsize;
}
size_t
new_file_size = (max_segment_end > section_table_end) ? max_segment_end : section_table_end;
size_t
min_size = decrypted_elf_header->e_phoff +
decrypted_elf_header->e_phnum * decrypted_elf_header->e_phentsize;
if
(new_file_size < min_size) {
new_file_size = min_size;
}
if
(new_file_size > my_elf64_file.elf_header_off && my_elf64_file.elf_header_off > min_size) {
new_file_size = my_elf64_file.elf_header_off;
}
*restored_data = (
char
*)
malloc
(new_file_size);
if
(!*restored_data) {
return
-3;
}
memcpy
(*restored_data, decrypted_data, new_file_size);
Elf64_Ehdr restored_ehdr = {0};
memcpy
(&restored_ehdr, decrypted_elf_header,
sizeof
(Elf64_Ehdr));
memcpy
(restored_ehdr.e_ident, ELFMAG, SELFMAG);
memcpy
(*restored_data, &restored_ehdr,
sizeof
(Elf64_Ehdr));
Elf64_Off phdr_restore_offset = restored_ehdr.e_phoff;
if
(phdr_restore_offset <
sizeof
(Elf64_Ehdr) || phdr_restore_offset >= new_file_size) {
phdr_restore_offset =
sizeof
(Elf64_Ehdr);
((Elf64_Ehdr*)(*restored_data))->e_phoff = phdr_restore_offset;
}
size_t
phdr_table_size = decrypted_elf_header->e_phnum * decrypted_elf_header->e_phentsize;
if
(phdr_restore_offset + phdr_table_size > new_file_size) {
free
(*restored_data);
*restored_data = NULL;
*restored_size = 0;
return
-4;
}
memcpy
(*restored_data + phdr_restore_offset, decrypted_phdr_table, phdr_table_size);
*restored_size = new_file_size;
Elf64_Ehdr *final_ehdr = (Elf64_Ehdr *)(*restored_data);
if
(
memcmp
(final_ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
free
(*restored_data);
*restored_data = NULL;
*restored_size = 0;
return
-5;
}
return
0;
}
#pragma pack(push, 1)
typedef
struct
custom_elf64_file{
Elf64_Off elf_header_off;
Elf64_Half elf_header_size;
Elf64_Off elf_program_header_table_off;
Elf64_Half elf_program_header_table_num;
Elf64_Half elf_program_header_table_size;
}Custom_Elf64_File;
#pragma pack(pop)
typedef
struct
{
unsigned
char
e_ident[EI_NIDENT];
[培训]科锐逆向工程师培训第53期2025年7月8日开班!