背景介绍

  固件系统中的二进制文件依赖于特定的系统环境执行,针对固件的研究在没有足够的资金的支持下需要通过固件的模拟来执行二进制文件程序。依赖于特定硬件环境的固件无法完整模拟,需要hook掉其中依赖于硬件的函数。

 LD_PRELOAD的劫持

 对于特定函数的劫持技术分为动态注入劫持和静态注入劫持两种。静态注入指的是通过修改静态二进制文件中的内容来实现对特定函数的注入。动态注入则指的是在运行的过程中对特定的函数进行劫持,动态注入劫持一方面可以通过劫持PLT表或者GOT表来实现,另一方面可以通过环境变量LD_PRELOAD来实现。

 在《揭秘家用路由器0day漏洞挖掘》中作者针对D-link DIR-605L(FW_113)路由器中的Web应用程序boa,通过hook技术劫持 apmib_init()和apmib_get()函数修复boa对硬件的依赖,使得qemu-static-mips可以模拟执行,在文中作者通过LD_PRELOAD环境变量实现对函数的劫持。网上针对LD_PRELOAD的劫持也有大量的描述。但是LD_PRELOAD仍旧不是普适的。

 存在的问题

 LD_PRELOAD环境变量的开关在编译生成ulibc的时候指定,当关闭该选项的时候,无法使用LD_PRELOAD来预加载指定的动态链接库文件。

 固件的二进制文件中会将二进制文件中的Section信息去除掉,只保留下Segment的信息,使得无法通过patchelf来增加动态链接库实现劫持。patchelf 支持对二进制文件的patch修改,或者添加执行过程中的链接lib。

 本文方案思路

 在ELF文件中,存在DYNAMIC Segment,ld.so通过该段内容来加载程序运行过程中需要的lib文件,图1为在IDA中反编译后查看的DYNAMIC段的内容。

图1 Elf32_Dyn结构体数组

 DYNAMIC段介绍

 dynamic 段开头包含了由N个Elf32_Dyn组成的结构体,该结构体的D_tag代表了结构体的类型。d_un为通过union 联合的指针d_ptr或者对应的结构体的值d_val。

typedef struct {

Elf32_Sword d_tag;

union {

Elf32_Word d_val;

Elf32_Addr d_ptr;

} d_un;

}Elf32_Dyn;

  d_tag字段保存了类型的定义参数,详见ELF(5)手册。下面列出了动态链接器常用的比较重要的类型值

 1-DT_NEEDED 该类型的数据结构保存了程序所需要的共享库的名字相对字符串表的偏移量

 2-DT_SYMTAB 动态符号表的地址,对应的节名为.dynsym

 3-DT_HASH 符号散列表的名称,对应的节名为.hash又称为.gnu.hash

 4-DT_STRTAB 符号字符串表的地址,对应的节名为 .dynstr

 5-DT_PLTGOT 全局偏移表的地址

 如上各个字段对应到 IDA 中显示如下,d_tag字段代表了动态段的类型,当该值为1的时候。表示程序依赖的共享库的名字,其对应的d_val表示了是要加载的lib的名称在string table中的偏移。

 共享库文件名对应的结构体的类型值为0x01,如图2所示,0x4001C4-0x4001E4保存有5个二进制文件所依赖的共享库名字,其D_VAL的值是指向string table的偏移量。

图2 DT_NEEDED 共享库依赖图

 DYNAMIC段的定位

 从二进制文件头起始定位DYNAMIC段的流程如下:

 ELF_HEADER->Program Header -> DYNAMIC Program

 ELF_HEADER中保存有Program Header的偏移

图3 ELF Header

 Program Header中保存有Dynamic Segment 的相关结构体信息

图4 Program Header

 展开该结构体,可以找到Dynamic段在文件中的偏移量0x140h

Program Header结构体

 DT_NEEDED伪造

 动态段由dynamic的结构体数组组成,dynamic的结构体定义如下:

 在dynamic和elf_hash之间仍存在一段空余空余可填充空间。本文利用该段空间填充,实现特定lib的加载。

 【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)

 本文方案实验与测试

 利用dynamic和elf_hash之间的空余区域,在该区域伪造出新的dynamic的一个数组。如下图,不修改二进制文件大小,伪造增添ibcjson.so,使得二进制文件加载 ibcjson.so。在ibcjson.so中编写对应的劫持函数。

 思路: 将原有的Elf32_Dyn数组元素依次后移,并在该数组的首部添加伪造的ibcjson.so,该lib的命名可以选用string table中的任一字符串即可。

 核心移动代码,将Elf32_Dyn中的元素依次后移一个,dynamic段 dynamic[0]元素作为要伪造填充的数据,在本文的实验中,将dynamic[0]中的value值加1。由于在MIPS下存在大小端两种架构,在小端机器上的代码解决大端架构的填充伪造时要注意大小端的转换问题。

void move_dynamic(char* buf){

int x = 0;

Elf32_Dyn* dyn = (Elf32_Dyn *)buf;

while(1){

if(dyn[x].d_tag == 0 && dyn[x].d_un.d_ptr == 0){

break;

}

x++;

if(x>100) {

printf("Error break\n");

break;

}

}

while(x--){

//printf("the index x is %x\n",x);

mem_cpy(&dyn[x],&dyn[x+1],8);

}

dyn[x+1].d_un.d_val = b2l(l2b(dyn[x+1].d_un.d_val) + 1);

}

 测试环境

  TOTOLink N210RE中boa程序,劫持函数参考DIR605A,劫持apmib_init以及apmib_get

  该固件的ld,关闭了LD_PRELOAD程序选项,未提供/etc/ld.preload

  劫持函数代码,采用了《揭秘家用路由器漏洞挖掘》提供的示例代码如下,在原有的基础上,增加printf来查看显示是否劫持成功。

#include<stdio.h>

#define MIB_HW_VER 0x250

#define MIB_IP_ADDR 170

#define MIB_CAPTCHA 0x2C1

int apmib_init(void){

printf("helllo");

return 1;

}

int fork(void){

return 0;

}

void apmib_get(int code,int *value){

switch(code){

case MIB_HW_VER:

*value = 1;

break;

case MIB_IP_ADDR:

*value = 1;

break;

case MIB_CAPTCHA:

*value = 1;

break;

}

return;

}

 通过mips-linux-gcc进行编译,这里注意mips-linux-gcc在编译过程中的编译文件的位置要位于参数之后(踩坑了!!!!)

 mips-linux-gcc -Wall -fPIC -shared apmib.c -o ibcjson.so

 通过如下代码,给原有的boa二进制文件添加一个dynamic

#include<stdio.h>

#include <stdio.h>

#include <stdlib.h>

#include "elf.h"

#define PATCH "boa_patch"

int b2l(int be)

{

return ((be >> 24) &0xff )

| ((be >> 8) & 0xFF00)

| ((be << 8) & 0xFF0000)

| ((be << 24));

}

char* buf = NULL;

int l2b(int le) {

return (le & 0xff) << 24

| (le & 0xff00) << 8

| (le & 0xff0000) >> 8

| (le >> 24) & 0xff;

}

static char *_get_interp(char *buf)

{

int x;

// Check for the existence of a dynamic loader

Elf_Ehdr *hdr = (Elf_Ehdr *)buf;

Elf_Phdr *phdr = (Elf_Phdr * )(buf + l2b(hdr->e_phoff));

printf("the phdr address is: 0x%x 0x%x 0x%x 0x%x\n",phdr,l2b(hdr->e_phoff),buf,sizeof(hdr->e_phoff));

for(x = 0; x < hdr->e_phnum; x++){

if(l2b(phdr[x].p_type) == PT_DYNAMIC){

// There is a dynamic loader present, so load it

return buf + l2b(phdr[x].p_offset);

}

}

return NULL;

}

int mem_cpy(char* src,char* dst,int len){

printf("the src addr is %x , %x\n",src-buf,dst-buf);

for(int x=0;x<len;x++){

dst[x]=src[x];

}

return 0;

}

/*

1 2 3 4

temp = 2

strcpy(1,2)

1 2

strcpy()

*/

void move_dynamic(char* buf){

int x = 0;

Elf32_Dyn* dyn = (Elf32_Dyn *)buf;

//Elf32_Dyn tmp_dyn;

//mem_cpy()

while(1){

if(dyn[x].d_tag == 0 && dyn[x].d_un.d_ptr == 0){

printf("the x is %d\n",x);

break;

}

x++;

if(x>100) {

printf("Error break\n");

break;

}

}

while(x--){

//printf("the index x is %x\n",x);

mem_cpy(&dyn[x],&dyn[x+1],8);

}

dyn[x+1].d_un.d_val = b2l(l2b(dyn[x+1].d_un.d_val) + 1);

//FILE* fw = fopen("")

}

void analyse(char* buf){

char* phdr_address = NULL;

phdr_address = _get_interp(buf);

printf("phdr address:  0x%x\n",phdr_address-buf);

move_dynamic(phdr_address);

}

void save_binary(char* buf,int size){

FILE* fw = fopen(PATCH,"wb");

fwrite(buf,size,1,fw);

fclose(fw);

}

int main(int argc,char *argv[],char* envp[]){

if(argc < 2){

printf("not enough argc\n");

}

FILE* fp = fopen(argv[1],"rb");

fseek(fp,0,SEEK_END);

int size = ftell(fp);

fseek(fp, 0L, SEEK_SET);

buf = malloc(size);

fread(buf,size,1,fp);

analyse(buf);

save_binary(buf,size);

free(buf);

return 0;

}

 Makefile如下

all: elf.h analyse_ph.c

gcc analyse_ph.c -m32 -g3 -o analyse

./analyse boa_real_n210

 针对N210RE 的测试截图如下,通过export LD_LIBRARY_PATH使得程序加载ibcjson.so,成功劫持boa,输出helllo

 Ubuntu下rand函数劫持测试

 rand函数的头文件是stdlib.h

 编写rand.c

#include<stdio.h>

#include<stdlib.h>

int main(){

int a = 0;

a = rand()%100;

printf("the a is %d\n",a);

return 0;

}

//gcc -m32 rand.c -o rand

 编写rand_hook.so

#include<stdio.h>

int rand(){

printf("hook !\n");

return 100;

}

//gcc -fPIC -Wall -shared -m32 rand_hook.c -o rand_hook.so

 使用LD_PRELOAD测试,成功实现对该函数的劫持

 使用patch,针对dynamic段元素添加伪造,测试rand_patch,依赖的库文件增加了ibc.so.6,ibc.so.6需要通过export LD_LIBRARY_PATH导入ibc.so.6的文件路径

 实现对rand的劫持

 总结

 本文通过研究二进制文件中的dynamic段,通过修改二进制文件增加依赖共享库,可以解决在模拟固件的过程时,固件缺少节信息且固件函数无法通过LD_PRELOAD劫持的问题。该方案仍有不足之处,对于ld加载共享库的依赖顺序、共享库劫持的底层原理尚未深入探究。

 参考

 《揭秘家用路由器0day挖掘技术》

 《二进制分析实战》

 更多靶场实验练习、网安学习资料,请点击这里>>

搜索

复制

二进制固件函数劫持术-DYNAMIC的更多相关文章

  1. JavaScript函数劫持

    一.为什么我会写这篇文章 这篇文章其实是在一个偶然的机会下发现了居然有JavaScript劫持这种东西,虽然这种东西在平时用的比较少,而且一般实用价值不高,但是在一些特殊的情况下还是要使用到的,所以在 ...

  2. 一个简单例子弄懂什么是javascript函数劫持

    javascript函数劫持很简单,一般情况下,只要在目标函数触发之前,重写这个函数即可. 比如,劫持eval函数的代码如下: var _eval=eval; eval=function(x){ if ...

  3. [转]浅谈javascript函数劫持

    转自:Ph4nt0m Security Team 这么多年了,现在学习依然还是有很多收货,向前辈致敬.转载一方面是自己存档一份,另一方面是让更多喜欢安全的人一同学习. ================ ...

  4. lua二进制操作函数

    由于 Lua 脚本语言本身不支持对数字的二进制操作(例如 与,或,非 等操作),MUSHclient 为此提供了一套专门用于二进制操作的函数,它们都定义在一个“bit”表中,使用时只要requre “ ...

  5. java中十进制转二进制转换函数

    十进制转成十六进制: Integer.toHexString(int i) 十进制转成八进制 Integer.toOctalString(int i) 十进制转成二进制 Integer.toBinar ...

  6. PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明

    PHP函数篇详解十进制.二进制.八进制和十六进制转换函数说明 作者: 字体:[增加 减小] 类型:转载   中文字符编码研究系列第一期,PHP函数篇详解十进制.二进制.八进制和十六进制互相转换函数说明 ...

  7. PHP函数十进制、二进制、八进制和十六进制转换

    PHP函数篇详解十进制.二进制.八进制和十六进制互相转换函数说明,主要掌握各进制转换的方法,以应用于实际开发. 一,十进制(decimal system)转换函数说明 1,十进制转二进制 decbin ...

  8. 什么是递归?用十进制转二进制的Python函数示例说明

    先上用Python写的十进制转二进制的函数代码: def Dec2Bin(dec): result = '' if dec: result = Dec2Bin(dec//2) return resul ...

  9. PHP函数十进制、二进制、八进制和十六进制转换函数说明

    1.十进制转二进制 decbin() 函数,如下实例  echo decbin(12); //输出 1100 echo decbin(26); //输出 11010 2.十进制转八进制 decoct( ...

随机推荐

  1. MyEclipse如何刷新项目

    第一种:选中项目,点右键Refresh即可, 第二种:选择project->clean,选中所要编译得项目,点ok即可

  2. JS判断数组中的对象的每一个值不能为空

    方法一:使用every()函数,此函数不怎么常用,想要了解更多请自查 //表格 evaluateData为表格的数据 <el-table id="out-table3" :d ...

  3. 微信小程序列表拖动排序Demo

    wxml页面编写 <view class="container"> <view bindtap="box" class="box&q ...

  4. 夯实基础上篇-图解 JavaScript 执行机制

    讲基础不易,本文通过 9 个 demo.18 张 图.2.4k 文字串讲声明提升.JavaScript 编译和执行.执行上下文.调用栈的基础知识.

  5. 如何使用 python 爬取酷我在线音乐

    前言 写这篇博客的初衷是加深自己对网络请求发送和响应的理解,仅供学习使用,请勿用于非法用途!文明爬虫,从我做起.下面进入正题. 获取歌曲信息列表 在酷我的搜索框中输入关键词 aiko,回车之后可以看到 ...

  6. java 竞赛常用方法

    一,基本数据类型 1.int,float.double等转换为字符串用 String.valueOf方法 eg:double转换为String Double dtDouble=12.31354; St ...

  7. (十一)React Ant Design Pro + .Net5 WebApi:后端环境搭建-IdentityServer4(三)持久化

    一.前言 IdentityServer配合EFCore持久化,框架已经为我们准备了两个上下文: ConfigurationDbContext:配置数据(资源.客户端.身份等) PersistedGra ...

  8. (转)Angular中的拦截器Interceptor

    什么是拦截器? 异步操作 例子 Session 注入(请求拦截器) 时间戳(请求和响应拦截器) 请求恢复 (请求异常拦截) Session 恢复 (响应异常拦截器) 转之:http://my.osch ...

  9. 论文翻译:2020_TinyLSTMs: Efficient Neural Speech Enhancement for Hearing Aids

    论文地址:TinyLSTMs:助听器的高效神经语音增强 音频地址:https://github.com/Bose/efficient-neural-speech-enhancement 引用格式:Fe ...

  10. 封闭的一个多月,老菜鸟的 机械手和AGV 自动搬运小项目总结

    最近上海疫情严重,闲赋在家无事可做,手机里不断的推送一些无脑的谩骂声音,索性找点事情做,将3月份实施的一个自动搬运小项目做一个简单的汇总,便于今后项目实施中积累一些经验.项目需求非常简单,因为能力有限 ...