首页
社区
课程
招聘
[原创]记录学到三种的so加固方式
发表于: 2025-5-18 16:53 2285

[原创]记录学到三种的so加固方式

2025-5-18 16:53
2285

简单分享一下学习到的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_offsetELF Header的偏移,一般标准ELF文件的偏移都是0,所以这里直接置0,而自定义的ELF的ELF Header偏移则用origin_file_size_保存一下,后面有用

在这里传入的参数file_offsetELF 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_vaddrp_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没有实现,有兴趣的朋友可以自己实现一下。具体完整代码我就不贴出来了,里面各种各样调试信息绕来绕去没删除,写得太乱了。

// bionic/libdl/libdl.cpp
void* dlsym(void* handle, const char* symbol) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_dlsym(handle, symbol, caller_addr);
}
// bionic/libdl/libdl.cpp
void* dlsym(void* handle, const char* symbol) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_dlsym(handle, symbol, caller_addr);
}
// bionic/linker/dlfcn.cpp
void* __loader_dlsym(void* handle, const char* symbol, const void* caller_addr) {
  return dlsym_impl(handle, symbol, nullptr, caller_addr);
}
// bionic/linker/dlfcn.cpp
void* __loader_dlsym(void* handle, const char* symbol, const void* caller_addr) {
  return dlsym_impl(handle, symbol, nullptr, caller_addr);
}
// bionic/linker/dlfcn.cpp
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;
}
// bionic/linker/dlfcn.cpp
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;
}
// bionic/linker/linker.cpp
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) {
        // For a TLS symbol, dlsym returns the address of the current thread's
        // copy of the symbol.
        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, /*should_alloc=*/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;
}
// bionic/linker/linker.cpp
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) {
        // For a TLS symbol, dlsym returns the address of the current thread's
        // copy of the symbol.
        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, /*should_alloc=*/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;
}
// linker/linker.cpp
static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
                                            soinfo** found,
                                            const char* name,
                                            const version_info* vi) {
  // According to man dlopen(3) and posix docs in the case when si is handle
  // of the main executable we need to search not only in the executable and its
  // dependencies but also in all libraries loaded with RTLD_GLOBAL.
  //
  // Since RTLD_GLOBAL is always set for the main executable and all dt_needed shared
  // libraries and they are loaded in breath-first (correct) order we can just execute
  // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup.
  if (si == solist_get_somain()) {
    return dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
  }
 
  SymbolName symbol_name(name);
  // note that the namespace is not the namespace associated with caller_addr
  // we use ns associated with root si intentionally here. Using caller_ns
  // causes problems when user uses dlopen_ext to open a library in the separate
  // namespace and then calls dlsym() on the handle.
  return dlsym_handle_lookup_impl(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
}
// linker/linker.cpp
static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
                                            soinfo** found,
                                            const char* name,
                                            const version_info* vi) {
  // According to man dlopen(3) and posix docs in the case when si is handle
  // of the main executable we need to search not only in the executable and its
  // dependencies but also in all libraries loaded with RTLD_GLOBAL.
  //
  // Since RTLD_GLOBAL is always set for the main executable and all dt_needed shared
  // libraries and they are loaded in breath-first (correct) order we can just execute
  // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup.
  if (si == solist_get_somain()) {
    return dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
  }
 
  SymbolName symbol_name(name);
  // note that the namespace is not the namespace associated with caller_addr
  // we use ns associated with root si intentionally here. Using caller_ns
  // causes problems when user uses dlopen_ext to open a library in the separate
  // namespace and then calls dlsym() on the handle.
  return dlsym_handle_lookup_impl(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
}
// bionic/linker/linker.cpp
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;
}
// bionic/linker/linker.cpp
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;
}
// bionic/linker/linker_soinfo.cpp
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);
}
// bionic/linker/linker_soinfo.cpp
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);
}
// bionic/linker/linker_soinfo.cpp
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));
 
  // test against bloom filter
  if ((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 0) {
    return nullptr;
  }
 
  // bloom test says "probably yes"...
  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;
}
// bionic/linker/linker_soinfo.cpp
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));
 
  // test against bloom filter
  if ((1 & (bloom_word >> h1) & (bloom_word >> h2)) == 0) {
    return nullptr;
  }
 
  // bloom test says "probably yes"...
  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;
}
// bionic/linker/linker_soinfo.cpp
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;
}
// bionic/linker/linker_soinfo.cpp
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;
}
// bionic/linker/linker.cpp
case DT_GNU_HASH:
        gnu_nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
        // skip symndx
        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_);
        // amend chain for symndx = header[1]
        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;
// bionic/linker/linker.cpp
case DT_GNU_HASH:
        gnu_nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
        // skip symndx
        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_);
        // amend chain for symndx = header[1]
        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;
    // GNU哈希表相关变量
    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;
    }
 
    //读elf头
    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 Hash桶查找法
    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_);//bucket起始地址
    gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - symndx;//chain起始地址减去index偏差
 
    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);
 
    //so修改与保存
    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;
    // GNU哈希表相关变量
    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;
    }
 
    //读elf头
    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 Hash桶查找法
    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_);//bucket起始地址
    gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - symndx;//chain起始地址减去index偏差
 
    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);
 
    //so修改与保存
    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";
    // 计算所需缓冲区大小: 目录路径长度 + 1 (斜杠) + 文件名长度 + 1 (null terminator)
    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);
 
 
    // 使用构建的私有目录路径下的加密文件路径进行 dlopen
    lib = dlopen(encryptedLoadPath, RTLD_LAZY);
 
    // 释放动态分配的内存
    free(encryptedLoadPath);
 
    // 释放JNI字符串资源
    env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
 
 
    if (lib == NULL) {
        LOGE("%s dlopen failed: %s\n", encryptedFileName, dlerror());
        return; // dlopen 失败,直接返回
    }
 
    // --- 后面的逻辑与原始代码类似,操作dlopen加载的库 ---
 
    //获取库函数
    lib_func = reinterpret_cast<FUNC>(dlsym(lib, "mymyadd"));
    if (!lib_func) {
        LOGI("can't find module symbol 'mymyadd': %s\n", dlerror());
        dlclose(lib); // dlsym 失败,关闭库
        return; // dlsym 失败,返回
    }
 
    LOGI("loadso lib_func = %p", lib_func);
 
    //修改内存读写权限
    // 注意:parse_maps_for_lib 函数需要根据加载的库名来查找,这里是 "libmathlib_encrypt.so"
    // 确保 parse_maps_for_lib 能够正确找到加载的库的内存映射
    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;
    // 确保 page 是页对齐的,mprotect 要求地址和大小都是页对齐的
    // 简单的示例省略了页对齐处理,实际生产代码需要考虑
    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); // mprotect 失败,关闭库
        return; // mprotect 失败,返回
    }
 
    //解密覆盖
    // 确保 RC4_KEY 和 FUNC_SIZE 在某个头文件中定义且可访问
    // 确保 buf 大小足够 FUNC_SIZE
    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";
    // 计算所需缓冲区大小: 目录路径长度 + 1 (斜杠) + 文件名长度 + 1 (null terminator)
    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);
 
 
    // 使用构建的私有目录路径下的加密文件路径进行 dlopen
    lib = dlopen(encryptedLoadPath, RTLD_LAZY);
 
    // 释放动态分配的内存
    free(encryptedLoadPath);
 
    // 释放JNI字符串资源
    env->ReleaseStringUTFChars(jEncryptedFileDirectoryPath, encryptedFileDirectoryPath);
 
 
    if (lib == NULL) {
        LOGE("%s dlopen failed: %s\n", encryptedFileName, dlerror());
        return; // dlopen 失败,直接返回
    }
 
    // --- 后面的逻辑与原始代码类似,操作dlopen加载的库 ---
 
    //获取库函数
    lib_func = reinterpret_cast<FUNC>(dlsym(lib, "mymyadd"));
    if (!lib_func) {
        LOGI("can't find module symbol 'mymyadd': %s\n", dlerror());
        dlclose(lib); // dlsym 失败,关闭库
        return; // dlsym 失败,返回
    }
 
    LOGI("loadso lib_func = %p", lib_func);
 
    //修改内存读写权限
    // 注意:parse_maps_for_lib 函数需要根据加载的库名来查找,这里是 "libmathlib_encrypt.so"
    // 确保 parse_maps_for_lib 能够正确找到加载的库的内存映射
    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;
    // 确保 page 是页对齐的,mprotect 要求地址和大小都是页对齐的
    // 简单的示例省略了页对齐处理,实际生产代码需要考虑
    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); // mprotect 失败,关闭库
        return; // mprotect 失败,返回
    }
 
    //解密覆盖
    // 确保 RC4_KEY 和 FUNC_SIZE 在某个头文件中定义且可访问
    // 确保 buf 大小足够 FUNC_SIZE
    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];    /* Magic number and other info 16 bytes*/ 
    Elf64_Half  e_type;          /* Object file type 2 bytes*/ 
    Elf64_Half  e_machine;    /* Architecture 2 bytes*/ 
    Elf64_Word  e_version;    /* Object file version 4 bytes*/ 
    Elf64_Addr  e_entry;      /* Entry point virtual address 8 bytes*/ 
    Elf64_Off   e_phoff;      /* Program header table file offset 8 bytes*/ 
    Elf64_Off   e_shoff;      /* Section header table file offset */ 
    Elf64_Word  e_flags;      /* Processor-specific flags */ 
    Elf64_Half  e_ehsize;     /* ELF header size in bytes */ 
    Elf64_Half  e_phentsize;      /* Program header table entry size */ 
    Elf64_Half  e_phnum;      /* Program header table entry count */ 
    Elf64_Half  e_shentsize;      /* Section header table entry size */ 
    Elf64_Half  e_shnum;      /* Section header table entry count */ 
    Elf64_Half  e_shstrndx;       /* Section header string table index */ 
} 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; 
    
   
    // Validate input file is an ELF file 
    Elf64_Ehdr* orig_ehdr = (Elf64_Ehdr *)inputfile_buffer; 
   
    // 验证ELF魔数 
    if (memcmp(orig_ehdr->e_ident, ELFMAG, SELFMAG) != 0) { 
        return -2; 
    
   
    // Calculate sizes and offsets 
    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; 
   
    // Check if output buffer is large enough 
    if (final_size > outfile_size) { 
        return -3; 
    
   
    // 1. Copy original file to output buffer 
    memcpy(outfile_buffer, inputfile_buffer, inputfile_size); 
   
    // 2. Create custom ELF file header 
    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; 
   
    // 3. Create modified ELF header based on original 
    Custom_Elf64_Ehdr custom_ehdr = {0}; 
    memcpy(&custom_ehdr, orig_ehdr, sizeof(Elf64_Ehdr)); 
   
    // 只修改魔数头,保留其他e_ident字段 
    memcpy(custom_ehdr.e_ident, ".csf", 4); // Change magic number 
   
    // 4. Encrypt and store the modified ELF header at end of file    encrypt((unsigned char *)RC4_KEY, RC4_KEY_LEN, 
            reinterpret_cast<unsigned char *>(outfile_buffer + enc_ehdr_offset), 
            reinterpret_cast<unsigned char *>(&custom_ehdr), 
            orig_ehdr->e_ehsize); 
   
    // 5. Encrypt and store the original program header table at end of file 
    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); 
   
    // 6. Find and encrypt all PT_LOAD segments in place 
    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) { 
            // Validate segment boundaries 
            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); 
        
    
   
    // 7. Zero out original ELF header in output buffer 
    memset(outfile_buffer, 0, sizeof(Elf64_Ehdr)); 
   
    // 8. Zero out original program header table in output buffer 
    memset(outfile_buffer + orig_ehdr->e_phoff, 0, phdr_table_size); 
   
    // 9. Write custom file header structure at beginning of output file 
    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; 
    
   
    // 0. 保存Custom_Elf64_File原始数据副本 
    Custom_Elf64_File original_custom_file; 
    memcpy(&original_custom_file, elf_pack_data, sizeof(Custom_Elf64_File)); 
   
    // 1. 分配解密缓冲区 
    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; 
    
   
    // 2. 解密ELF头 
    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); 
   
    // 验证解密后的ELF头 
    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; 
    
   
    // 3. 解密程序头表 
    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); 
   
    // 4. 分析程序头表,找出所有LOAD段 
    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; 
    
   
    // 5. 验证程序头表 
    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; 
            
        
    
   
    // 6. 解密所有LOAD段 
    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); 
        
    
   
    // 7. 将解密后的ELF头和程序头表存储在原位置,供restore函数使用 
    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); 
   
    // 8. 恢复原始的Custom_Elf64_File数据 
    memcpy(elf_pack_data, &original_custom_file, sizeof(Custom_Elf64_File)); 
   
    // 9. 清理资源 
    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; 
    
   
    // 获取解密后的ELF头和程序头表 
    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); 
   
    // 验证解密后的ELF头 
    if (memcmp(decrypted_elf_header->e_ident, ".csf", 4) != 0) { 
        return -2; 
    
   
    // 计算恢复后的文件大小 
    // 1. 考虑所有段的结尾 
    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; 
        
    
   
    // 2. 考虑节表的结尾 (如果存在) 
    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; 
   
    // 确保文件大小至少包含ELF头和程序头表 
    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; 
    
   
    // 分配新的内存来存储恢复的ELF文件 
    *restored_data = (char *)malloc(new_file_size); 
    if (!*restored_data) { 
        return -3; 
    
   
    // 复制基本数据(不包括末尾额外数据) 
    memcpy(*restored_data, decrypted_data, new_file_size); 
   
    // 创建完整的ELF头 
    Elf64_Ehdr restored_ehdr = {0}; 
    memcpy(&restored_ehdr, decrypted_elf_header, sizeof(Elf64_Ehdr)); 
   
    // 只恢复标准ELF魔数,保留其他e_ident字段原值 
    memcpy(restored_ehdr.e_ident, ELFMAG, SELFMAG);  // 标准魔数: 0x7F ELF 
   
    // 写入ELF头 
    memcpy(*restored_data, &restored_ehdr, sizeof(Elf64_Ehdr)); 
   
    // 确定程序头表的位置和大小 
    Elf64_Off phdr_restore_offset = restored_ehdr.e_phoff; 
   
    // 如果e_phoff不合理,使用标准位置 
    if (phdr_restore_offset < sizeof(Elf64_Ehdr) || phdr_restore_offset >= new_file_size) { 
        phdr_restore_offset = sizeof(Elf64_Ehdr); // 通常是64字节 
        // 更新ELF头中的程序头表偏移 
        ((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; 
   
    // 最后验证恢复的ELF文件 
    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];    /* Magic number and other info 16 bytes*/ 

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 35
支持
分享
最新回复 (18)
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2025-5-18 18:45
0
雪    币: 439
活跃值: (1903)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-5-19 08:03
0
雪    币: 30
活跃值: (1735)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
666
2025-5-19 08:36
0
雪    币: 5248
活跃值: (3329)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
666
2025-5-19 09:47
0
雪    币: 3056
活跃值: (2704)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
继续学习。
2025-5-19 10:49
0
雪    币: 104
活跃值: (5701)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
666666666
2025-5-19 14:17
0
雪    币: 1827
活跃值: (3669)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
6666
2025-5-19 21:33
0
雪    币: 119
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
666
2025-5-20 16:34
0
雪    币: 212
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
太6666666666
2025-5-21 11:49
0
雪    币: 1057
活跃值: (840)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
6666666666
2025-5-21 15:42
0
雪    币: 102
活跃值: (2785)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
mark
2025-5-22 10:56
0
雪    币: 1449
活跃值: (2716)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
6666666666
2025-5-22 11:25
0
雪    币: 119
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-5-23 14:14
0
雪    币: 7162
活跃值: (22756)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
15
看看
2025-5-23 14:44
0
雪    币: 300
活跃值: (396)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
666
2025-5-26 10:42
0
雪    币: 18
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
1
2025-5-29 21:00
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
2025-6-2 11:04
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
111
2025-6-9 16:12
0
游客
登录 | 注册 方可回帖
返回