IDA Pro > IDA Pro教程 > 技术问题 > 使用IDA处理U-Boot二进制流文件

使用IDA处理U-Boot二进制流文件

发布时间:2022-10-08 16: 10: 48

 最近在研究IoT设备的过程中遇到一种情况。

 一个IoT设备,官方不提供固件包,网上也搜不到相关的固件包,所以我从flash中直接读取。因为系统是VxWorks,能看到flash布局,所以能很容易把uboot/firmware从flash中分解出来。对于firmware的部分前一半左右是通过lzma压缩,后面的一半,是相隔一定的区间有一部分有lzma压缩数据。而固件的符号信息就在这后半部分。因为不知道后半部分是通过什么格式和前半部分代码段一起放入内存的,所以对于我逆向产生了一定的阻碍。所以我就想着看看uboot的逻辑,但是uboot不能直接丢入ida中进行分析,所以有了这篇文章,分析uboot格式,如何使用ida分析uboot。

 uboot格式

 正常的一个uboot格式应该如下所示:

 

 $binwalk bootimg.bin

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 13648 0x3550 CRC32 polynomial table,big endian

 14908 0x3A3C uImage header,header size:64 bytes,header CRC:0x25ED0948,created:2019-12-02 03:39:51,image size:54680 bytes,Data Address:0x80010000,Entry  Point:0x80010000,data CRC:0x3DFB76CD,OS:Linux,CPU:MIPS,image type:Firmware Image,compression type:lzma,image name:"u-boot image"

 14972 0x3A7C LZMA compressed data,properties:0x5D,dictionary size:33554432 bytes,uncompressed size:161184 bytes

 

 而这uboot其实还得分为三部分:

 1.从0x00-0x346C是属于bootstrap的部分

 2.0x346C-0x34AC有0x40字节的uboot image的头部信息

 3.从0x34AC到结尾才是uboot image的主体,经过lzma压缩后的结果

 

 那么uboot是怎么生成的呢?Github上随便找了一个uboot源码:https://github.com/OnionIoT/uboot,编译安装了一下,查看uboot的生成过程。

 1.第一步,把bootstrap和uboot源码使用gcc编译成两个ELF程序,得到bootstrap和uboot

 2.第二步,使用objcopy把两个文件分别转换成二进制流文件。

 

 $mips-openwrt-linux-uclibc-objcopy--gap-fill=0xff-O binary bootstrap bootstrap.bin

 $mips-openwrt-linux-uclibc-objcopy--gap-fill=0xff-O binary uboot uboot.bin

 $binwalk u-boot/bootstrap

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 0 0x0 ELF,32-bit MSB executable,MIPS,version 1(SYSV)

 13776 0x35D0 CRC32 polynomial table,big endian

 28826 0x709A Unix path:/uboot/u-boot/cpu/mips/start_bootstrap.S

 

 $binwalk u-boot/bootstrap.bin

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 13648 0x3550 CRC32 polynomial table,big endian

 

 $binwalk u-boot/u-boot

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 0 0x0 ELF,32-bit MSB executable,MIPS,version 1(SYSV)

 132160 0x20440 U-Boot version string,"U-Boot 1.1.4(Dec 2 2019,11:39:50)"

 132827 0x206DB HTML document header

 133794 0x20AA2 HTML document footer

 134619 0x20DDB HTML document header

 135508 0x21154 HTML document footer

 135607 0x211B7 HTML document header

 137363 0x21893 HTML document footer

 137463 0x218F7 HTML document header

 138146 0x21BA2 HTML document footer

 138247 0x21C07 HTML document header

 139122 0x21F72 HTML document footer

 139235 0x21FE3 HTML document header

 139621 0x22165 HTML document footer

 139632 0x22170 CRC32 polynomial table,big endian

 179254 0x2BC36 Unix path:/uboot/u-boot/cpu/mips/start.S

 

 $binwalk u-boot/u-boot.bin

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 132032 0x203C0 U-Boot version string,"U-Boot 1.1.4(Dec 2 2019,11:39:50)"

 132699 0x2065B HTML document header

 133666 0x20A22 HTML document footer

 134491 0x20D5B HTML document header

 135380 0x210D4 HTML document footer

 135479 0x21137 HTML document header

 137235 0x21813 HTML document footer

 137335 0x21877 HTML document header

 138018 0x21B22 HTML document footer

 138119 0x21B87 HTML document header

 138994 0x21EF2 HTML document footer

 139107 0x21F63 HTML document header

 139493 0x220E5 HTML document footer

 139504 0x220F0 CRC32 polynomial table,big endian

 

 3.把u-boot.bin使用lzma算法压缩,得到u-boot.bin.lzma

 

 $binwalk u-boot/u-boot.bin.lzma

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 0 0x0 LZMA compressed data,properties:0x5D,dictionary size:33554432 bytes,uncompressed size:161184 bytes

 

 4.使用mkimage,给u-boot.bin.lzma加上0x40字节的头部信息得到u-boot.lzming

 $binwalk u-boot/u-boot.lzimg

 

 DECIMAL HEXADECIMAL DESCRIPTION

 --------------------------------------------------------------------------------

 0 0x0 uImage header,header size:64 bytes,header CRC:0x25ED0948,created:2019-12-02 03:39:51,image size:54680 bytes,Data Address:0x80010000,Entry Point:0x80010000,data  CRC:0x3DFB76CD,OS:Linux,CPU:MIPS,image type:Firmware Image,compression type:lzma,image name:"u-boot image"

 64 0x40 LZMA compressed data,properties:0x5D,dictionary size:33554432 bytes,uncompressed size:161184 bytes

 

 5.最后把bootstrap.bin和u-boot.lzming合并到一起,然后根据需要uboot的实际大小,比如需要一个128k的uboot,在末尾使用0xff补齐到128k大小

 

 使用ida处理bootstrap二进制流文件

 

 在上面的结构中,需要注意几点:

 

 1.Data Address:0x80010000,Entry Point:0x80010000表示设备启动后,会把后续uboot通过lzma解压出来的数据存入内存地址0x80010000,然后把$pc设置为:0x80010000,所以uboot最开头4字节肯定是指令。

 2.uncompressed size:161184 bytes,可以使用dd把LZMA数据单独取出来,然后使用lzma解压缩,解压缩后的大小要跟这个字段一样。如果还想确认解压缩的结果有没有问题,可以使用CRC算法验证。

 

 接下来就是通过dd或者其他程序把二进制流从uboot中分离出来,再丢到ida中。先来看看bootstrap,首先指定相应的CPU类型,比如对于上例,则需要设置MIPS大端。

 随后我们暂时设置一下起始地址为0x80010000,通电以后CPU第一个执行的地址默认情况下我们是不知道的,不同CPU有不同的起始地址。设置如下图所示:

 bootstrap最开头也指令,所以按C转换成指令,如下图所示:

 跳转到0x80010400,随后是一段初始化代码,下一步我们需要确定程序基地址,因为是mips,所以我们可以根据$gp来判断基地址。

 如上图所示,因为bootstrap的大小为0x3a3c bytes,所以可以初步估计基地址为0x9f000000,所以下面修改一下基地址:

 并且修改在Options->General->Analysis->Processor specific......设置$gp=0x9F0039A0

 

 0x9F0039A0地址开始属于got表的范围,存储的是函数地址,所以把0x9F0039A0地址往后的数据都转成word:

 到此就处理完毕了,后面就是存逆向的工作了,具体bootstrap代码都做了什么,不是本文的重点,所以暂不管。

 

 使用ida处理uboot流文件

 处理bootstrap,我们再看看uboot,和上面的处理思路大致相同。

 1.使用dd或其他程序,把uboot数据先分离出来。

 2.使用lzma解压缩

 3.丢到ida,设置CPU类型,设置基地址,因为uboot头部有明确定义基地址为0x80010000,所以不用再自己判断基地址

 4.同样把第一句设置为指令

 正常情况下,uboot都是这种格式,0x80010008为got表指针,也是$gp的值。

 5.根据0x80010008的值,去设置$gp 6.处理got表,该地址往后基本都是函数指针和少部分的字符串指针。结尾还有uboot命令的结构体。

 到此uboot也算基础处理完了,后续也都是逆向的工作了,也不是本文的关注的内容。

 

 编写idapython自动处理uboot

 

 拿uboot的处理流程进行举例,使用Python编写一个ida插件,自动处理uboot二进制流文件。

 1.我们把0x80010000设置为__start函数

 idc.add_func(0x80010000)

 idc.set_name(0x80010000,"__start")

 

 2.0x80010008是got表指针,因为我们处理了0x80010000,所以got表指针地址也被自动翻译成了代码,我们需要改成word格式。

 idc.del_items(0x80010008)

 idc.MakeDword(0x80010008)

 got_ptr=idc.Dword(0x80010008)

 idc.set_name(idc.Dword(0x80010008),".got.ptr")

 

 3.把got表都转成Word格式,如果是字符串指针,在注释中体现出来

 def got():

 assert(got_ptr)

 for address in range(got_ptr,end_addr,4):

 value=idc.Dword(address)

 if value==0xFFFFFFFF:2019-12-03 15:36:56星期二

 break

 idc.MakeDword(address)

 idaapi.autoWait()

 if idc.Dword(value)!=0xFFFFFFFF:

 func_name=idc.get_func_name(value)

 if not idc.get_func_name(value):

 idc.create_strlit(value,idc.BADADDR)

 else:

 funcs.append(func_name)

 

 基本都这里就ok了,后面还可以加一些.text段信息,但不是必要的,最后的源码如下:

 

#!/usr/bin/env python

# -*- coding=utf-8 -*-

 

import idc

import idaapi

 

class Anlysis:

def __init__(self):

self.start_addr = idc.MinEA()

self.end_addr = idc.MaxEA()

self.funcs = []

 

def uboot_header(self):

idc.add_func(self.start_addr)

idc.set_name(self.start_addr, "__start")

idc.del_items(self.start_addr + 0x8)

idc.MakeDword(self.start_addr + 0x8)

self.got_ptr = idc.Dword(self.start_addr+8)

idc.set_name(idc.Dword(self.start_addr+8), ".got.ptr")

 

def got(self):

assert(self.got_ptr)

for address in range(self.got_ptr, self.end_addr, 4):

value = idc.Dword(address)

if value == 0xFFFFFFFF:

break

idc.MakeDword(address)

idaapi.autoWait()

if idc.Dword(value) != 0xFFFFFFFF:

func_name = idc.get_func_name(value)

if not idc.get_func_name(value):

idc.create_strlit(value, idc.BADADDR)

else:

self.funcs.append(func_name)

 

def get_max_text_addr(self):

assert(self.funcs)

max_addr = 0

for func_name in self.funcs:

addr = idc.get_name_ea_simple(func_name)

end_addr = idc.find_func_end(addr)

if end_addr > max_addr:

max_addr = end_addr

if max_addr % 0x10 == 0:

self.max_text_addr = max_addr

else:

self.max_text_addr = max_addr + 0x10 - (max_addr % 0x10)

 

def add_segment(self, start, end, name, type_):

segment = idaapi.segment_t()

segment.startEA = start

segment.endEA = end

segment.bitness = 1

idaapi.add_segm_ex(segment, name, type_, idaapi.ADDSEG_SPARSE | idaapi.ADDSEG_OR_DIE)

 

def start(self):

# text seg

self.uboot_header()

self.got()

self.get_max_text_addr()

self.add_segment(self.start_addr, self.max_text_addr, ".text", "CODE")

# end

idc.jumpto(self.start_addr)

 

 

if __name__ == "__main__":

print("Hello World")

 

 案例作者:Hcamael

 原地址:https://www.freebuf.com/sectool/221983.html

展开阅读全文

标签:IDA反编译工具

读者也访问过这里:
邀请您进入交流群 点击扫码
400-8765-888 kefu@makeding.com

专业销售为您服务

欢迎添加好友,了解更多IDA优惠信息,领逆向工程学习资料礼包1份!
热门文章
exe反编译工具哪个好?反编译能力强的工具盘点
随着软件技术的发展,exe(可执行文件)已经成为了电脑、手机等多个平台上的主要软件运行格式,而对于exe文件的反编译也成为了逆向工程中不可缺少的一个步骤。本文将介绍一些常用的exe反编译工具,并评价其优缺点,帮助读者选择合适的工具。
2023-04-12
idapro怎么改为中文
IDA Pro是一款功能强大的反汇编和反编译工具,广泛应用于逆向工程和软件开发领域。在使用IDA Pro时,如果我们不习惯英文界面,可以将其改为中文界面。本文将介绍IDA Pro怎么改为中文界面。IDA Pro界面改成中文主要有两种方法,下面是详细介绍。
2023-04-19
c++反编译工具有哪些
反编译C++代码的工具一般是针对可执行文件和库文件的反汇编和逆向分析工具。本文将给大家介绍c++反编译工具有哪些的内容。市面说的c++反编译工具有很多,下面介绍几款使用认识较多的软件。
2023-04-23
ida怎么查找字符串 ida字符串窗口快捷键
在数字化时代,逆向工程作为解密软件和分析程序的关键技术,正日益受到广泛关注。在逆向分析的过程中,IDA(Interactive DisAssembler)是一款备受推崇的工具,它为逆向工程师们提供了强大的功能和灵活的操作。本文将带您深入探讨如何在IDA中查找字符串,优化字符串窗口的使用,并探讨IDA如何将变量转换成字符串,帮助您更加熟练地驾驭这一工具,为逆向分析的世界增添一抹精彩。
2023-09-27
ida如何转伪代码 ida伪代码怎么看
IDA Pro是一款常用的反汇编和反编译工具,可以帮助我们分析二进制文件的实现细节和执行过程,以便更好地理解程序的执行过程和逻辑。在进行逆向工程的过程中,我们经常需要将反汇编结果转换为伪代码,以便更好地进行分析和修改。本文将介绍如何使用IDA Pro转换为伪代码,并简单讲解ida伪代码怎么看。
2023-04-14
最新文章
IDA Pro动态调试教程需要配哪些环境 IDA Pro动态调试教程里断点通常下在哪里
光靠盯着反汇编和那些近似C语言的伪代码来看,很多分叉的执行路径其实还是很难吃准;所以大家就会关心IDA Pro的动态调试流程到底需要提前配好哪些环境,在实际操作里头断点一般又该下在什么地方比较管用,从自己拥有授权的测试小软件开始练手是一条比较稳当的路。在铺排环境的时候,不妨先把操作系统、处理器架构、程序要用的依赖库和输入文件都一一备齐,然后再顺着软件大致的执行路径,循序渐进地把中断位置加上去;这么做既能比较清楚地观察到程序是怎么跑起来的,也不容易被环境方面的小毛小病把思路搅乱。
2026-06-03
IDA Pro逆向教程练什么样本更合适 IDA Pro逆向教程里先练静态还是动态
刚开始学习IDA Pro的时候,如果一上来就选了过于复杂的样本,很容易就会陷进数量庞大的函数、库代码和异常分支里面,半天都理不出一个清晰的头绪。所以,先得弄明白两个很实际的问题:在IDA Pro的逆向学习过程中,到底该选什么样的样本来练手,以及在整个学习次序上,是应该先练静态分析,还是先练动态调试。一个比较稳妥的起步办法,是从自己动手编译的小型程序开始,先把程序的整体结构看明白,之后再打开调试器去验证自己之前的那些想法。练手用的文件范围,最好就严格限定在自编译程序、公开发布的教学样本,还有已经拿到明确授权的文件上面,不要直接去分析那些没有经过授权的商业软件,也尽量不要随便运行那些来源不明的程序。
2026-06-03
IDA Pro使用教程适合零基础吗 IDA Pro使用教程里哪些窗口最常用
很多人刚接触二进制分析的时候,很容易被IDA Pro的界面弄得不知所措,它的窗口多、地址多,汇编语句也排得密密麻麻。IDA Pro的使用教程到底适不适合零基础呢,答案是可以用来入门,只是不要刚上来就指望能看明白太复杂的样本。官方的入门资料里,也把IDA View、Pseudocode、Hex Dump View、Local Types和Functions View这几个窗口列为了初学者平时用得比较多的。所以零基础阶段最好先学会怎样加载文件、定位函数、查看字符串和跟踪调用关系,然后再逐步去补汇编与数据结构的知识,这样进入状态会更顺畅一些。
2026-06-03
IDA Pro下载教程该看哪个版本 IDA Pro下载教程安装后先检查哪些环境
刚开始使用IDA Pro的时候,要是教程的版本选得不对,后面很容易碰到各种别扭的情况。不同版本的软件,界面菜单的布局、许可证管理的方式、插件该放在哪个目录,还有一些功能入口的位置,都会跟着变化。按照Hex-Rays官方最新的发布记录,IDA 9.3sp1已经当成9.3系列的一个维护版本放出来了,它主要是改进了一下V850反编译器,顺带修了一些别的问题。如果是头一回装IDA,那优先去看9.3系列的教程会更合适;要是手上还在维护一些比较老的工程,那就应该根据机器上实际装的版本来找对应的资料,这样才不容易对不上号。
2026-06-03
IDA中文乱码通常和什么设置有关 IDA中文乱码出现后编码方式该怎么调整
在分析那些带有中文资源、日志文本或者配置内容的程序时,字符串窗口里时不时就会跳出问号、方框,或者干脆是一些没法阅读的字符。要弄清楚IDA里头的中文乱码通常跟哪些设置有关,还有乱码出现后编码方式该怎么去调整,先得分辨清楚这些乱码到底是出现在反汇编里的字符串、是Hex View右侧显示的那部分文本,还是旧数据库里留下来的注释。从7.0版本开始,IDA内部已经统一换成了UTF-8,但被分析的那个程序本身的原始字节,它照样可能用的是GBK、UTF-8、UTF-16LE这些不一样的编码,一旦判断错了,显示出来的东西就会不正常。
2026-06-03
IDA配置文件放在哪个目录 IDA配置文件改错以后怎么回退
很多人改IDA配置时,前面只是想调一个分析选项、界面行为或插件相关参数,结果一改完发现启动表现不对,甚至连原来的习惯都乱了。这个问题往往不是配置项本身有多复杂,而是没先分清IDA的配置文件到底放在安装目录还是用户目录,也没先想好改错以后该从哪一层回退。Hex-Rays官方文档已经把这条线说明白了,配置文件会按固定目录顺序查找,常见文件名也比较明确,所以只要先把目录和优先级理顺,后面的回退动作就会清楚很多。
2026-06-01

通过微信咨询我们

欢迎添加好友,了解更多IDA优惠信息,领取逆向工程学习资料礼包1份!

读者也喜欢这些内容: