简单分享一下学习到的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];
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!