dl_iterate_phdr

http://www.helplib.net/s/linux.die/65_1099/man-3-dl-iterate-phdr.shtml

dl_iterate_phdr(3)linux man page

名称

dl_iterate_phdr 遍历共享对象列表

staging 模块

# define  _gnu_source /*看到feature_test_macros(7)*/
# include<<a href="http://linux.die.net/include/link.h" rel="nofollow">link.h>
 int  dl_iterate_phdr( 
int(*回调)(struct dl_phdr_info  *info ,
 size_t 大小, void *数据),
 void *数据);

描述

在dl_iterate_phdr()函数允许在运行一个查询时间来找出哪个已加载的共享对象.

在dl_iterate_phdr()通过一个共享的对象列表函数教每个对象,并调用函数回调后 ,, 直到所有共享对象已被处理或回调返回一个非零值.

每个回调接收三个参数 :info ,它是指向一个结构包含共享信息的对象;的结构的大小,这个大小是通过信息;和数据,而这是作为参数传递的任何值由调用程序的副本(还可以访问数据)在调用 dl_iterate_phdr().

info 参数是以下类型的结构:

 struct  dl_phdr_info{ 
 elfw(addr)dlpi_addr  ;/ *基对象的地址 */ 
 const  char  *dlpi_name  ;/  *(null 结尾)名称
对象 */ 
 const  elfw(phdr)*dlpi_phdr  ;/ *指针数组
 elf 程序头
对于此对象 */ 
elfw(半)dlpi_phnum ;/  *# 在 dlpi_phdr  */ 
 }; 
(在elfw()宏定义打开它的参数到适合的硬件架构的 elf 数据类型的名称.例如,在一个 32 位平台上,elfw(addr)产生的数据类型名称 elf32_addr .这种类型的进一步信息,可在和头文件 .)
dlpi_addr 字段指示基地址共享对象(即,的共享内存的虚拟地址之间的差异.和该对象在它加载的偏移).在 dlpi_name 字段是 null 结尾字符串给定路径名从其中加载的共享对象.
为了理解 dlpi_phdr 和 dlpi_phnum 字段的含义,我们需要知道 elf 对象共享由一个线段数目,每个成员都有一个相应的程序头描述该线段.此共享的程序头的字段是一个指针数组 dlpi_phdr 对象.在 dlpi_phnum 字段指示此数组的大小.
这些程序头结构采用以下格式:
 typedef  struct{ 
 elf32_word  p_type  ;/ *线段类型* ./ 
 elf32_off  p_offset  ;/ *段文件偏移 */ 
 elf32_addr  p_vaddr  ;/ *段虚拟地址 */ 
 elf32_addr  p_paddr  ;/ *段物理地址 */ 
 elf32_word  p_filesz  ;/ *在文件的段大小 */ 
 elf32_word  p_memsz  ;/ *内存中的段大小 */ 
 elf32_word  p_flags  ;/  ** 段标志/
 elf32_word  p_align  ;/  ** 段对齐/
 }elf32_phdr ;
注意,我们可以计算特定程序头的位置 .x ,在虚拟内存使用公式:
 addr ==信息>dlpi_addr+ info>dlpi_phdr[x].p_vaddr; 

返回值

在dl_iterate_phdr()回调函数返回的最后调用返回的任何值.

版本

dl_iterate_phdr()已自版本的 glibc 2.2.4.

符合

在dl_iterate_phdr()函数是 linux 特定的应避免在便携应用的.

示例

下面的程序中显示的工作列表已加载的共享对象.为每个共享的对象,该程序列出的地址空间和虚拟对象的 elf 段加载.

#定义 _gnu_source 
# include<<a href="http://linux.die.net/include/link.h" rel="nofollow">link.h>
# include<<a href="http://linux.die.net/include/stdlib.h" rel="nofollow">stdlib.h>
# include<<a href="http://linux.die.net/include/3.3.3" rel="nofollow">3.3.3>
 static  int 
回调(struct dl_phdr_info  *info , size_t 大小, void *数据)
{
 int j;
 printf(" name =%s(段% d) n", info>dlpi_name .
 info>dlpi_phnum); 
对于(j=0 ;jdlpi_phnum  ;j + +) 
 printf("tt% 2d :地址=% 10p n",j.
 (void  *)(info>dlpi_addr + info>dlpi_phdr[j].p_vaddr)); 
 return 0;
}
 int 
主(int argc , char  *argv[]) 
{
 dl_iterate_phdr(callback , null); 
退出(exit_success);
}
#include <cstdio> #include <cstring> #include <cstdlib> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/auxv.h> #include <link.h> #include <vector> #include <string> #include <algorithm> // 手动定义Android ARM64架构系统调用号 #ifndef SYS_open #define SYS_open 5 #endif #ifndef SYS_fstat #define SYS_fstat 80 #endif #ifndef SYS_read #define SYS_read 3 #endif #ifndef SYS_close #define SYS_close 6 #endif // 合法库路径白名单(系统路径 + 你的droidc运行目录) const std::vector<std::string> LEGAL_LIB_PATHS = { "/system/lib/", "/system/lib64/", "/vendor/lib/", "/vendor/lib64/", "/apex/", "/system/bin/linker", "/system/bin/linker64", "[vdso]", "/data/user/0/com.n0n3m4.droidc/files/" // 你的运行目录(手动加入) }; // 自动获取当前设备的系统库设备号 dev_t get_legal_dev() { struct stat libc_stat; const char* libc_paths[] = { "/apex/com.android.runtime/lib64/bionic/libc.so", "/system/lib64/libc.so", "/vendor/lib64/libc.so" }; for (const char* path : libc_paths) { int fd = syscall(SYS_open, path, O_RDONLY); if (fd >= 0) { syscall(SYS_fstat, fd, &libc_stat); syscall(SYS_close, fd); return libc_stat.st_dev; } } return 0; } // 直连内核的文件读取 ssize_t kernel_read(const char* path, char* buf, size_t max_len) { int fd = syscall(SYS_open, path, O_RDONLY); if (fd < 0) return -1; ssize_t n = syscall(SYS_read, fd, buf, max_len - 1); if (n > 0) buf[n] = '\0'; syscall(SYS_close, fd); return n; } // 校验库的合法性 bool is_lib_legal(const char* lib_path, dev_t legal_dev) { // 1. 路径在白名单中 → 合法 for (const auto& path : LEGAL_LIB_PATHS) { if (strncmp(lib_path, path.c_str(), path.size()) == 0) { return true; } } // 2. 非白名单路径 → 校验设备号 struct stat lib_stat; int fd = syscall(SYS_open, lib_path, O_RDONLY); if (fd >= 0) { syscall(SYS_fstat, fd, &lib_stat); syscall(SYS_close, fd); if (lib_stat.st_dev == legal_dev) { return true; } } return false; } // 读取动态链接器真实库列表 int iterate_libs_callback(struct dl_phdr_info* info, size_t size, void* data) { std::vector<std::string>* suspicious_libs = (std::vector<std::string>*)data; if (!info->dlpi_name || strlen(info->dlpi_name) == 0) return 0; static dev_t legal_dev = get_legal_dev(); if (!is_lib_legal(info->dlpi_name, legal_dev)) { suspicious_libs->push_back(std::string(info->dlpi_name)); } return 0; } // 检测异常构造函数 bool check_abnormal_constructors() { char elf_buf[1024 * 10]; if (kernel_read("/proc/self/exe", elf_buf, sizeof(elf_buf)) <= 0) return false; Elf64_Ehdr* ehdr = (Elf64_Ehdr*)elf_buf; if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) return false; for (int i = 0; i < ehdr->e_shnum; i++) { Elf64_Shdr* shdr = (Elf64_Shdr*)(elf_buf + ehdr->e_shoff + i * ehdr->e_shentsize); char* sh_name = elf_buf + ehdr->e_shstrndx + shdr->sh_name; if (strcmp(sh_name, ".init_array") == 0 && shdr->sh_size > 20 * sizeof(void*)) { fprintf(stderr, "[ALERT] 异常构造函数数量过多\n"); return true; } } return false; } int main() { fprintf(stdout, "[*] 增强版LD_PRELOAD校验...\n"); bool is_injected = false; // 1. 检测非法库 std::vector<std::string> suspicious_libs; dl_iterate_phdr(iterate_libs_callback, &suspicious_libs); if (!suspicious_libs.empty()) { fprintf(stderr, "[ALERT] 发现非法库:\n"); for (const auto& lib : suspicious_libs) { fprintf(stderr, " - %s\n", lib.c_str()); } is_injected = true; } // 2. 检测异常构造函数 if (check_abnormal_constructors()) { is_injected = true; } if (is_injected) { fprintf(stderr, "\n[!] 检测到注入痕迹!\n"); return 1; } else { fprintf(stdout, "\n[+] 正常运行,无注入\n"); return 0; } } 优化一下这个 避免正常运作也提示检测
最新发布
11-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值