使用闪烁的“ rhino”示例对设备固件进行反向工程。 第二部分


SMARTRHINO-2018大会上的一次研讨会的基础上,我们向您介绍有关Flash Rhino设备固件的逆向工程的文章的第二部分。

在本文的第一部分中 ,将设备固件加载到IDA拆装器中,并对设备协议命令进行了初步分析。 在工作设备上测试了各个命令。

在第二部分中,将执行剩余固件任务的分析。

让我提醒您,在从控制LED的角度分析了蓝牙任务之后,决定切换到LED任务,因为最初的任务是创建一个用于控制LED的应用程序,为此需要对固件操作进行详细的了解。

固件文件可供独立研究。

提供所有信息仅出于教育目的。

猫下有很多闪烁的犀牛。

LED任务


简要地说:对负责切换LED的任务的完整分析。 分析数据类型和全局变量。

LED任务由位于0x08005A08x_leds_task函数表示。

除了在LED任务的主要功能中出现奇怪的行“我拥有超能力...”之外,您还可以注意行“ hue> max:change shine \ r \ n”



同时,我们看到了一个熟悉的情况-(WORD *)(v26 + 4)。 在变量v26的上下文菜单中,选择“转换为struct *”项,然后指示之前创建的结构:



给定v5 = v26 ,我们对变量v5重复执行“转换为struct *”操作。

我们将继续构建代码和数据。 在各处设置十六进制表示。 重命名:

  • v5 领导 ;
  • v6- idx ;
  • v8- hue_1 ;
  • v9-色调_2 ;
  • v26- _led ;

代码正在改进。 但是有些变量仍然伤害眼睛,例如变量v23:





显然,v23是4个字节的数组。
idx是LED的索引; 该索引被添加到基址中; 这样,可以访问相同位移的元素-这就是数组的行为方式。

我们将类型分配为char v23[4]并将其重命名为leds_smth ,代码变得更漂亮:



您还可以注意到x_queue_recv函数的结果返回到v25变量:

 x_queue_recv(&v25, leds_queue, 1000); 

但是可能不清楚您需要的数据如何在_led结构中。 事实是变量v25和_led 位于堆栈的附近 -这可以通过以下事实来理解,即在反编译中它们被写在相邻的行上。 如果双击变量,则可以在单独的窗口中查看变量在堆栈上的位置:



它们可能是一个结构,或者编译器已经做了一些优化。 因此,可以说来自蓝牙任务的数据被传输到LED任务。 为了更精确地查找,我将检查设备-通过蓝牙的零LED,我将发送值0x208,0x2D0,0x398,0x3E9 ,这可以在代码中注意到:



检查设备上的色相值的结果:

  • 0x208-LED停止平稳开关并设置为以下颜色:红色,绿色,蓝色,紫色;
  • 0x2D0-LED重新开始切换;
  • 0x398-一切都没有改变;
  • 0x3E9-一切都没有改变。

如果再次查看该代码,则会看到值0x398可以与小于0x167的值在逻辑上关联(为数组元素leds_smth设置了不同的值)。 因此,我将执行此检查:首先,将第一个LED设置为绿色(色相= 0x78, LED 010078FF20 ),而其他三个LED继续切换其颜色。


现在,我将LED 010398FFFF蓝牙协议LED 010398FFFF在此之后,第一个LED已切换到常规颜色切换模式。

因此,色调值为0x398会重置静态颜色值,这意味着leds_smth数组包含要占用的LED的标志(0或1):

  • 0-LED不忙,参与平滑的颜色切换( 色相= 0x398 );
  • 1-LED忙,用户设置静态颜色( 色相<= 0x167 )。

将leds_smth重命名为leds_busy

现在,以下代码块的目的应该变得清楚:



第83-101行的循环执行颜色切换步骤为5的平滑颜色拼接: v12 += 5 。 如果LED具有静态颜色,则此LED不参与镶嵌。 周期结束后,短期内将包含所有LED。

重命名:

  • sub_800678A- x_led_set_hsv ;
  • v12- hue_step ;
  • v13,v17,v18,v19- led0_busyled1_busyled2_busyled3_busy ;
  • v11,v20,v21,v22- hue0hue1hue2hue3 ;
  • dword_200004C4- led_control

sub_80039FE函数大概会执行超时(否则,LED不会平稳切换,而是立即切换),我们将其称为x_sleep ,变量v16为led_timeout

sub_8006934函数的用途尚不明确,但是在将LED上的颜色设置好之后,该函数将在所有地方使用-您可以将其称为x_led_fix_color

这些重命名之后,很容易理解函数sub_8006944 (在hue <= 0x167分支中调用):



它仅执行附加检查即可确定LED的颜色。 将函数sub_8006944重命名为x_led_set_hsv_wrap (后缀_wrap-这是另一个函数的“包装器”的说明),并为其设置以下原型:

 signed int __fastcall x_led_set_hsv_wrap(int led_control, signed int idx, int hue, char sat, char val) 

让我们回到x_leds_task函数的上一级。 再次查看代码,您会发现分支“ hue> 0x3E8”开始看起来像这样:



也就是说,色相值大于0x3E8会改变彩色马赛克的超时时间。 我将向设备发送一些值进行检查:

  • 色相= 0x3E9-LED开始快速切换:


  • 色相= 0xFFFF-LED开始缓慢切换:



退出LED任务的主循环时,将使用sub_8003C44函数,该函数也在sub_8005070函数中使用:



重命名:

  • sub_8005070- x_freeMsg ;
  • sub_8003C44- x_free_queue

在LED任务中,以下分支不得不引起注意:



您可以尝试执行LED B816D8D90000FFFF命令LED B816D8D90000FFFF 。 但是,如果您还记得只有2个字符被用作LED索引,那么尝试达到此代码显然是不成功的。 将此线程留待以后使用。 将函数sub_8004AE8重命名为x_mad_blinking ,现在该修复x_printf函数的签名了(上次我写错了签名):

 void x_printf(const char *format, ...) 

LED任务的主要周期已被分解,但是在任务开始时仍然有一个代码。

让我们看一下代码:



在第49行中,最有可能检查LED的可用性,并在发生错误的情况下调用sub_8004BBC函数,该函数关闭中断并启动无限循环,其中使用“ ../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_gpio.c”行。 最有可能是断言或类似功能。

重命名:

  • sub_8004BBC- x_gpio_assert ;
  • sub_800698C- x_check_gpio

如果在打开设备时仔细查看设备, sub_8006968函数的目的将变得清楚:


所有四个LED一起先点亮红色,然后点亮绿色,然后点亮蓝色。 之后,按颜色设置它们:0红色,1-绿色,2-蓝色,3-紫色。 然后他们才开始切换马赛克。

由于拼接是从主任务周期开始的,因此合乎逻辑的是,主周期之前的线58-61负责短期内在LED上包含不同的颜色,而线52-56则负责一次在所有LED上设置红绿蓝。 将函数sub_8006968重命名为x_led_all_set_rgb (根据传递的参数,RGB 仅凭直觉)。

LED任务的可能性


简要地说:定义包含奇怪行的代码的功能。 生成设备密码。

现在,让我们继续x_leds_task函数的最开始:



“ Eraze”“ gen”“ flash”“ reset” -为什么所有这些???

让我们尝试找出答案。

设sub_80066BC为x_leds_task_init

我们来看一下sub_8006B38:



纯净水,同意吗?



返回到x_leds_task。 v24变量类型出了点问题:



IDA在类型上犯了一个小错误,但是带有堆栈标记的注释对我们有帮助。 在变量v24和v25之间,最多12个字节(0x44-0x38)。 因此,我们将unsigned __int8 buf[12]重命名为buf并分配类型为unsigned __int8 buf[12] (Ida将警告新数据类型大于旧数据类型-我们同意)。

下一个 函数sub_8004CE4:



a1重命名为buf ,将v1重命名为_buf

函数sub_8006B26:



你认识她吗?

如果不化妆?

当然是memcpy 。 重命名。

那么sub_8004CE4函数的目的是在地址0x08007C00处获取一些数据。 顺便说一下,该地址在微控制器的闪存(特别是固件)的地址范围内。 将sub_8004CE4重命名为x_read_data_0x08007C00

X_leds_task功能行36:

 if ( (unsigned int)buf[0] - 65 > 0x19 ) 

更改数据显示(数字65上的R键,数字0x19上的H键):

 if ( (unsigned int)buf[0] - 'A' > 25 ) 

经过一番反思,您可以理解这是对拉丁字母AZ范围的测试。

接下来,使用格式字符串形式的提示,重命名:

  • sub_8004C10- x_erase ;
  • sub_80059C8- x_gen ;
  • sub_8004C84- x_flash

sub_8003C66函数的作用不明显-它仅增加一些全局变量-将sub_8003C66重命名为x_smth_inc

x_erase函数实际上并不接受任何参数-这可以在反汇编程序中进行验证:



在x_erase内部,使用了熟悉的地址0x08007C00,并访问了三个未知函数:



快速浏览这三个函数,我们看到它们正在访问0x40022000-0x400223FF范围内的地址。 微控制器的文档明确指出,这是“闪存接口”范围。 也就是说,x_erase函数可擦除闪存-太棒了!

显然,在检查了要写入的行的长度之后,x_flash函数将写入闪存(顺便说一下,参数a2和a3在这里是多余的-我们将帮助Idea):



而这一切都发生在“照明设备”中?

那么, x_gen函数呢? 快速浏览并重命名变量后,将如下所示:



sub_8006CB4函数如下所示:



sub_8006D10-像这样:



不要抑制在Internet上搜索这些不雅的常量的愿望: 0xABCD0x1234,0xE66D0xDEEC0x4C957F2D0x5851F42D 。 如果尚未完全禁止Internet,则可能会在随机函数的源代码中找到这些常量。 难怪父函数称为x_gen。

这也是一种非常典型的情况:在循环之前调用srand(),在循环中调用random(),因此将其重命名:

  • sub_8006D10-x_rand;
  • sub_8006CB4-x_srand。

好奇的读者可以通过查看sub_8005098函数来找出srand函数的种子来源

因此,x_gen函数生成指定大小的随机字符串

将生成的行写入闪存后,设备将重新引导:



似乎有些奇怪的重启。 但是,如果我们查看此设备的任务列表,则会在其中找到“ watchdogTask”。 显然,如果存在“卡住任务”,则看门狗会重新启动。

可以考虑分析除MadBlinking模式以外的LED任务。

让我们仔细看看系统中还有哪些其他任务:



恢复了代码中字符串的链接后,您可以看到以下图片:



首先,存在指向名称为task的字符串的链接,然后是至主task函数的链接。 它们在启动以下任务的主要功能中使用:



让我们执行丢失的重命名:

  • sub_80050FC- x_sensor_task ;
  • sub_8004AAC- x_watchdogTask ;
  • sub_8005440- x_uartRxTask

看门狗任务


任务看门狗没有做任何特别有趣的事情:



重命名:

  • dword_200003F8- wd_variable ;
  • sub_8001050- x_update_wd_var

UART任务


简要地说:搜索具有来自不同功能的链接的数据和功能。 确定其目的。

快速浏览UART任务使您能够检测到将数据发送到由变量unk_200003EC定义的未知队列:



通过二进制搜索恢复到此变量的链接后,我们将看到除x_uartRxTask之外,它还在主要(显然已经创建了队列)和迄今为止未知的函数sub_80051EC中使用



重命名:

  • sub_80051EC- x_recvMsg_uart_queue ;
  • unk_200003EC- uart_queue

请参阅对x_recvMsg_uart_queue的交叉引用:

  • sub_8005250;
  • x_bluetooth_task。

首先,请参见sub_8005250函数:



思考之后,重命名:

  • unk_2000034C- cmd_count ;
  • a1- cmd ;
  • v4- _cmd ;
  • v6是rsp ;
  • sub_8005250- x_bluetooth_cmd

现在让我们看一下仍在使用x_bluetooth_cmd的位置。 所有其他链接仅来自Bluetooth任务,现在该返回它。

回到蓝牙任务


简要地说:蓝牙任务的最终分析。 搜索无密码的授权。



如果您查看使用sub_8006A84函数的位置 ,并且您不太懒惰并且仔细研究其肠子,那么毫无疑问-这就是calloc 。 这是合乎逻辑的-为了将数据接收到缓冲区中,必须首先创建此缓冲区。

现在为sub_8006DBC 。 让我们看一下(变量已经重命名):



回顾标准C库用于处理字符串的功能,我们将在此处看到strstr (搜索子字符串)并对其进行粗体重命名。

让我们看一下x_bluetooth_task函数的代码- 自上次访问以来,这里的内容可能有所更改 。 在此过程中,我们将变量命名为:

  • v2- _state ;
  • v3- data_len

它旁边有一个sub_80052E2函数。 类似于从Bluetooth命令中提取数字的函数,它提取指定长度的字符串-我们称之为x_get_str

我们继续:

  • v26- isEcho ;
  • v6- meow_str ;
  • v10- uart_cmd_byte ;
  • v11- uart_cmd_str ;
  • v12- str_0 ;
  • v13- str_1 ;
  • v14- format_str ;
  • sub_8000F5C- x_blink_small_led

完成快速重命名:

  • v19 密码 ; (因为旁边有关于授权和密码的行)
  • sub_8004CC0- x_check_password ;
  • sub_8006AF4- x_free (由于密码,cmd和bt_args是指向动态对象的指针(请选中此选项!),使用它们后应释放内存);
  • sub_8006DAC- x_strcpy (检查出来!)。

现在探索READWRITAUTPSETP等分支。

如在运行中的设备上进行的测试所示,READ,WRIT,SETP命令需要授权。 使用AUTP命令进行的授权尝试使我们进入x_check_password函数以验证密码:



事实证明,密码长度必须为8个字符,并且将密码(在sub_8006B08函数中)与地址0x08007C00处的字节进行比较 -存储生成的随机字符AZ字符串。

原来,不知道密码,我们就无法登录该设备。 好吧,还是几乎不能...

注意使用auth_flag变量的位置:



事实证明,它不仅用于蓝牙任务。 在这里,我们只是没有查看Sensor任务。 我们去那里。

传感器任务


简要地说:触摸按钮有什么作用?

根据最佳编程实践,整个Sensor任务都放在一个IDA屏幕中。 这不能不让我们高兴:



线对线...

  • “ TSC%d \ r \ n”-此行应使您考虑STM32微控制器的触摸感应控制器;
  • “ AUTH BTN \ r \ n”-授权按钮???
  • “ SET AUTH%d \ r \ n”-设置授权标志?

让我们看看如果您按下触摸按钮,设备将如何运行(您是否意识到腿上的犀牛具有触摸按钮?):



短暂按下时,红色的小LED点亮。 长按此指示灯可长时间点亮。

如果将其与代码相关联,则可以假定sub_8000708函数是用于获取当前时间的函数。 然后,如果当前时间与开始触摸传感器之间的时间差大于1000(1秒),则LED点亮0xEA60毫秒(1分钟)。 但是auth_flag变量引起了极大的兴趣,通过长按触摸按钮将其设置为1,可以使攻击者可以访问“照明设备”的管理员来访问特权功能。

因此,在通过“按钮”授权后,您可以读取设备中存储的密码(READ命令),写入RAM(WRIT功能)或设置新密码(SETP)。

疯狂眨眼


简要地说:可以执行一个奇怪的Mad Blinking代码分支吗?

让我们回到蓝牙任务并进行更多重命名。

  • v21- vip_smth (目前尚不清楚那里是什么);
  • v22- vip_str (未知大小的字符串,从参数中提取);
  • v23- mad_led-分配“转换为struct *”并指定struct_LED

在这里,我们看到数字0xB816D8D9 (在Bluetooth任务的文章的第一部分中找到)作为LED的索引。 如果执行验证,将执行以下代码:

 if ( sub_8005520(vip_str, vip_smth) == 0x46F70674 ) 

将sub_8005520重命名为x_vip_check并进行查看:



假定第一个参数是一个字符串(至少字符串已传递给此函数),此代码显示第二个参数是该字符串的长度(或必须处理的长度)。 重命名:

  • a1- str ;
  • a2- len

让我们看一下sub_8000254函数:



现在查看sub_8000148 。 这是它的开始:



这只是功能的三分之一...嗯...好吃! 经验丰富的挖掘机很容易在这里看到...

什么啊
整数除法运算。

怎么发掘呢?
如果您不费吹灰之力 ,则可以从功能sub_8000254进入x_printf (通过其他几个功能)。 重要的一点是,通常所有标准功能都是相当标准的 。 这意味着您可以尝试在公共领域中至少找到要研究的功能的某些源代码,从而使研究工作更有效率。

因此,我们以printf的源代码为基础,然后查看vfprintf ,并将其与所研究固件的代码进行比较。 使用源代码,我们退出到itoa函数,并得出以下结论: sub_8000254函数是运算符operator% (取除法的余数),而这个可怕的long函数仅取除法的整数部分(div运算)。

可能会出现一个合理的问题-为什么会这样? 事实是,在特定的微控制器中不能进行DIV,MOD操作,因此,编译器代替了对单个函数的调用来代替这些运算符。 顺便说一下,这里还有其他一些数学函数

挖掘时不要忘记重命名。

因此,函数x_vip_check会计算...这将是您的作业

顺便说一句,如果您执行正确的VIP命令,我们将得到“迪斯科犀牛”:



固件简要报告


设备的固件基于FreeRTOS实时操作系统。 系统具有以下任务:

  1. 蓝牙任务 。 通过蓝牙处理文本形式的命令。
  2. LED任务 。 根据蓝牙命令控制彩色LED。
  3. 传感器任务 。 打开红色LED指示灯,允许短期授权而无需在设备上输入密码。
  4. UART任务 。 允许您通过内部UART端口与蓝牙模块进行交互(用于初始化蓝牙)。
  5. 看门狗任务 。 跟踪冻结。

该研究没有考虑从UART端口(Tx / GND触点)读取数据的能力。

总结


在会议的大师班上,只有主要的LED控制功能被拆卸了。 向最活跃的参与者展示了他们的实验性“犀牛”。

在我看来,“犀牛”为反向工程和漏洞搜索培训课程提供了不错的布局。 布局的一个特点是可以根据需要多次更改固件,每个过程都有自己的固件。 与解析可执行文件不同,反向固件使您可以更好地理解:

  • 如何与IDA合作;
  • 固件和设备之间的交互原理;
  • RTOS的工作原理。

非常感谢读完所有文章的所有人!

Source: https://habr.com/ru/post/zh-CN412561/


All Articles