逆向WiFi驱动板 RISC-V BL602

翻译原文链接:https://lupyuen.github.io/articles/wifi

文章翻译总结:文章分析了BL602的WiFi演示固件源码,涵盖了WiFi事件处理、启动WiFi固件任务、连接WiFi热点等过程,通过GitHub代码搜索,找到了与BL602 WiFi驱动匹配的开源代码,包括RivieraWaves UMAC和LMAC代码、WiFi认证代码以及部分物理层代码,深入分析了BL602的WiFi驱动工作流程,并找到了大量可参考的开源代码。

文章相关标签:#RivieraWaves、#FreeRTOS、#物理层、#GitHub Search、#UMAC&LMAC

逆向WiFi驱动板 RISC-V BL602

[TOC]

今天,我们对 BL602 RISC-V 系统芯片上的 WiFi 驱动程序进行逆向工程,并了解内部发生的情况……在找到的(不完整)驱动程序源代码的指导下进行。

为什么要对 BL602 W(See this non-BL602 example)iFi 驱动程序进行逆向工程?

  1. 教育:掌握在BL602(一种芯片型号)上WiFi数据包的传输和接收过程。
  2. 故障排除:当WiFi驱动程序出现问题时,我们应该能够定位并解决问题。(或许还能进行修复!)
  3. 审计:确保WiFi数据包的传输和接收既正确又安全。(可参考非BL602的相关例子)
  4. 替换:未来,我们可能用开源驱动程序(如Openwifi)替换现有的闭源WiFi驱动程序

https://twitter.com/Yu_Wei_Wu/status/1406940637773979655?s=19

https://github.com/open-sdr/openwifi

接下来开始一起阅读并开始学习逆向工程吧。

一、BL602 WiFi 固件演示demo

让我们一起来研究BL602物联网软件开发套件中的BL602 WiFi演示固件源代码:bl602_demo_wifi

在演示固件中,我们将执行以下操作:

  1. 注册处理WiFi事件的事件处理程序
  2. 启动控制BL602 WiFi固件的任务
  3. 启动管理WiFi连接状态的任务
  4. 连接到WiFi**接入点**
  5. 发送HTTP请求

1.1 注册WiFi事件处理

固件启动时,我们将注册一个处理WiFi事件的回调函数:main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi/bl602_demo_wifi/main.c#L819-L866

//  启动时调用以初始化驱动程序并运行事件循环
static void aos_loop_proc(void *pvParameters) {
  //  省略部分: 初始化驱动程序
  ...
  //  为WiFi事件注册回调函数
  aos_register_event_filter(
    EV_WIFI,              //  Event Type
    event_cb_wifi_event,  //  Event Callback Function
    NULL);                //  Event Callback Argument

  //  启动WiFi网络堆栈
  cmd_stack_wifi(NULL, 0, 0, NULL);

  //  运行事件循环
  aos_loop_run();
}

(我们稍后会讨论event_cb_wifi_event变量) 启动代码中,通过调用cmd_stack_wifi来启动WiFi网络堆栈。 现在,让我们深入了解其内部实现…

1.2 启动WiFi固件管理

在cmd_stack_wifi函数中,我们按照如下方式启动WiFi固件任务:main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi/bl602_demo_wifi/main.c#L729-L747

//  启动WiFi网络堆栈
static void cmd_stack_wifi(char *buf, int len, int argc, char **argv) {
  //  Check whether WiFi Networking is already started
  static uint8_t stack_wifi_init  = 0;
  if (1 == stack_wifi_init) { return; }  //  Already started
  stack_wifi_init = 1;

  //  启动WiFi固件任务 (FreeRTOS)
  hal_wifi_start_firmware_task();

  //  发布WiFi事件以启动WiFi管理器任务
  aos_post_event(
    EV_WIFI,                 //  Event Type
    CODE_WIFI_ON_INIT_DONE,  //  Event Code
    0);                      //  Event Argument
}

(本文稍后将讨论hal_wifi_start_firmware_task函数) 任务启动后,我们触发WiFi事件CODE_WIFI_ON_INIT_DONE,以便启动WiFi管理任务。 现在,让我们探究WiFi事件处理程序的内部工作原理…

1.3 启动WiFi管理任务

这里是处理WiFi事件的方法:main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi/bl602_demo_wifi/main.c#L374-L512

//  WiFi事件的回调功能
static void event_cb_wifi_event(input_event_t *event, void *private_data) {

  //  处理WiFi事件
  switch (event->code) {

  //  由cmd_stack_wifi发布以启动Wi-Fi管理器任务
    case CODE_WIFI_ON_INIT_DONE:

    //  启动WiFi管理器任务 (FreeRTOS)
      wifi_mgmr_start_background(&conf);
      break;

    //  省略部分: 处理其他WiFi事件

当接收到WiFi事件CODE_WIFI_ON_INIT_DONE时,我们会调用wifi_mgmr_start_background启动WiFi管理任务(在FreeRTOS中)。 wifi_mgmr_start_background函数是由BL602 WiFi驱动程序提供的。(请参考源代码)

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr.c#L1406-L1415

1.4 连接到WiFi网络

既然我们已经启动了WiFi固件任务和WiFi管理任务这两个后台任务,接下来让我们连接到一个WiFi网络! 通过演示固件,我们可以输入特定的命令来连接到WiFi**接入点**…

wifi_sta_connect YOUR_WIFI_SSID YOUR_WIFI_PASSWORD

这里是wifi_sta_connect命令的实现方法:main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi/bl602_demo_wifi/main.c#L366-L372

//  连接到WiFi接入点
static void wifi_sta_connect(char *ssid, char *password) {

  //  启用WiFi客户端
  wifi_interface_t wifi_interface
    = wifi_mgmr_sta_enable();

  //  连接到WiFi接入点
  wifi_mgmr_sta_connect(
    wifi_interface,  //  WiFi Interface
    ssid,            //  SSID
    password,        //  Password
    NULL,            //  PMK
    NULL,            //  MAC Address
    0,               //  Band
    0);              //  Frequency
}

我们通过调用BL602 WiFi驱动程序提供的wifi_mgmr_sta_enable函数来激活WiFi客户端功能

(“STA”代表“WiFi站点”,也就是WiFi客户端)

接着,我们通过调用BL602 WiFi驱动程序中的wifi_mgmr_sta_connect函数来连接到WiFi**接入点**。

(下一章我们将深入探讨wifi_mgmr_sta_connect函数的内部机制)

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr_ext.c#L202-L217

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr_ext.c#L302-L307

1.5 发送HTTP请求

现在,我们可以输入特定的命令来通过WiFi发送**HTTP请求**…

httpc

这里是httpc命令的实现代码:main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi/bl602_demo_wifi/main.c#L704-L727

//  使用LWIP发送HTTP GET请求
static void cmd_httpc_test(char *buf, int len, int argc, char **argv) {
  //  检查HTTP请求是否已在运行
  static httpc_connection_t settings;
  static httpc_state_t *req;
  if (req) { return; }  //  请求已在运行

  //  初始化LWIP HTTP设置
  memset(&settings, 0, sizeof(settings));
  settings.use_proxy = 0;
  settings.result_fn = cb_httpc_result;
  settings.headers_done_fn = cb_httpc_headers_done_fn;

  //  使用LWIP发送HTTP GET请求
  httpc_get_file_dns(
    "nf.cr.dandanman.com",  //  Host
    80,                     //  Port
    "/ddm/ContentResource/music/204.mp3",  //  URI
    &settings,              //  Settings
    cb_altcp_recv_fn,       //  Callback Function
    &req,                   //  Callback Argument
    &req);                  //  Request
}

在BL602上,我们采用LWIP(轻量级IP堆栈)来实现IP、UDP、TCP和HTTP网络功能。 httpc_get_file_dns的详细文档可以在此查阅。 想要获取更多关于BL602 WiFi演示固件的信息,请参阅相关文档…

  • BL602 WiFi演示固件文档

让我们一起逆向分析BL602 WiFi演示固件… 探索其内部的工作原理!

https://www.nongnu.org/lwip/2_1_x/index.html

https://pine64.github.io/bl602-docs/Examples/demo_wifi/wifi.html

二、连接到WiFi接入点

BL602连接到WiFi接入点时,实际发生了什么过程?

为了深入了解BL602如何连接WiFi接入点,我们将研读BL602 WiFi驱动程序的**源代码**。

让我们观察在连接过程中会发生哪些变化…

  1. 向WiFi管理任务发起连接请求
  2. 通过WiFi管理器的状态机来处理该连接请求
  3. 将连接请求传递给WiFi硬件(LMAC)
  4. 激发LMAC中断以执行连接操作

2.1 向WiFi管理任务发送请求

我们之前使用wifi_mgmr_sta_connect函数来连接到WiFi接入点。 这里是该函数内部的工作原理:wifi_mgmr_ext.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr_ext.c#L302-L307

//  连接到WiFi接入点
int wifi_mgmr_sta_connect(wifi_interface_t *wifi_interface, char *ssid, char *psk, char *pmk, uint8_t *mac, uint8_t band, uint16_t freq) {
  //  设置WiFi SSID和PSK
  wifi_mgmr_sta_ssid_set(ssid);
  wifi_mgmr_sta_psk_set(psk);

  //  连接到WiFi接入点
  return wifi_mgmr_api_connect(ssid, psk, pmk, mac, band, freq);
}

我们首先设定了WiFi的SSID和PSK。接着,我们通过调用wifi_mgmr_api_connect函数来连接到WiFi接入点。

wifi_mgmr_api_connect函数的具体实现如下所示:wifi_mgmr_api.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr_api.c#L40-L84

//  连接到WiFi接入点
int wifi_mgmr_api_connect(char *ssid, char *psk, char *pmk, uint8_t *mac, uint8_t band, uint16_t freq) {
  //  省略部分: 复制PSK、PMK、MAC地址、频带和频率
  ...
  //  向WiFi管理器任务发送连接请求
  wifi_mgmr_event_notify(msg);
  return 0;
}

在此,我们通过调用wifi_mgmr_event_notify函数,将连接**请求发送**给WiFi管理任务。

wifi_mgmr_event_notify函数的定义位于wifi_mgmr.c文件中…

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr.c#L1332-L1343

//  向WiFi管理器任务发送请求
int wifi_mgmr_event_notify(wifi_mgmr_msg_t *msg) {
  //  省略部分: 等待WiFi管理器启动
  ...
  //  通过消息队列向WiFi管理器发送请求
  if (os_mq_send(
    &(wifiMgmr.mq),  //  Message Queue
    msg,             //  Request Message
    msg->len)) {     //  Message Length
    //  发送请求失败
    return -1;
  }
  return 0;
}

os_mq_send函数是如何将请求发送给WiFi管理任务的? os_mq_send函数通过调用FreeRTOS,将请求消息发送到WiFi管理器的**消息队列**中:os_hal.h

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/os_hal.h#L174

#define os_mq_send(mq, msg, len) \
    (xMessageBufferSend(mq, msg, len, portMAX_DELAY) > 0 ? 0 : 1)

2.2 WiFi管理状态机

WiFi管理器在其后台任务(FreeRTOS)中运行着一个状态机,用来管理每个WiFi连接的状态。 当WiFi管理器接收到我们发出的连接到WiFi接入点的请求时,会发生什么? 让我们深入wifi_mgmr.c文件来一探究竟…

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/wifi_mgmr.c#L702-L745

//  当WiFi管理器接收到连接请求时调用
static void stateIdleAction_connect( void *oldStateData, struct event *event, void *newStateData) {
  //  为连接请求设置WiFi配置文件
  wifi_mgmr_msg_t *msg = event->data;
  wifi_mgmr_profile_msg_t *profile_msg = (wifi_mgmr_profile_msg_t*) msg->data;
  profile_msg->ssid_tail[0] = '\0';
  profile_msg->psk_tail[0]  = '\0';

  //  记住WiFi管理器中的WiFi配置文件
  wifi_mgmr_profile_add(&wifiMgmr, profile_msg, -1);

  //  连接到WiFi配置文件。TODO:其他安全支持
  bl_main_connect(
    (const uint8_t *) profile_msg->ssid, profile_msg->ssid_len,
    (const uint8_t *) profile_msg->psk, profile_msg->psk_len,
    (const uint8_t *) profile_msg->pmk, profile_msg->pmk_len,
    (const uint8_t *) profile_msg->mac, (const uint8_t) profile_msg->band, (const uint16_t) profile_msg->freq);
}

在此,我们配置了WiFi配置文件,并通过调用bl_main_connect函数来连接到该配置文件。 在bl_main_connect函数中,我们为802.11 WiFi协议设定了连接参数:bl_main.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/bl_main.c#L189-L216

//  连接到WiFi配置文件
int bl_main_connect(const uint8_t* ssid, int ssid_len, const uint8_t *psk, int psk_len, const uint8_t *pmk, int pmk_len, const uint8_t *mac, const uint8_t band, const uint16_t freq) {

  //  802.11 WiFi协议的连接参数
  struct cfg80211_connect_params sme;    

  //  省略部分: 设置802.11连接参数
  ...
  //  使用802.11连接参数连接到WiFi网络
  bl_cfg80211_connect(&wifi_hw, &sme);
  return 0;
}

连接参数随后被传递给在bl_main.c中定义的bl_cfg80211_connect函数…

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/bl_main.c#L539-L571

//  使用802.11连接参数连接到WiFi网络
int bl_cfg80211_connect(struct bl_hw *bl_hw, struct cfg80211_connect_params *sme) {

  //  将填充连接结果
  struct sm_connect_cfm sm_connect_cfm;

  //  将连接参数转发到LMAC
  int error = bl_send_sm_connect_req(bl_hw, sme, &sm_connect_cfm);

  //  省略部分: 检查连接结果

该函数通过调用bl_send_sm_connect_req,将连接参数发送至WiFi硬件LMAC)。

接下来,让我们进一步探究其具体实现方式…

2.3 发送请求到LMAC

LMAC是什么?

LMAC(Lower Medium Access Control 低媒体访问控制)是BL602 WiFi无线电硬件内部运行的固件,负责执行WiFi无线电的相关功能。

为了连接到WiFi接入点,我们会通过调用bl_send_sm_connect_req(定义在bl_msg_tx.c文件中)来将连接参数传递给LMAC

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/bl_msg_tx.c#L722-L804

//  将连接参数转发到LMAC
int bl_send_sm_connect_req(struct bl_hw *bl_hw, struct cfg80211_connect_params *sme, struct sm_connect_cfm *cfm) {

  //  生成SM_CONNECT_REQ消息
  struct sm_connect_req *req = bl_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID, sizeof(struct sm_connect_req));

  //  省略部分: 设置SM_CONNECT_REQ消息的参数
  ...
  //  向LMAC固件发送SM_CONNECT_REQ消息
  return bl_send_msg(bl_hw, req, 1, SM_CONNECT_CFM, cfm);
}

在这里,我们构建了一个包含连接参数的SM_CONNECT_REQ消息。

(“SM”指的是RivieraWaves的LMAC状态机)

接着,我们通过调用bl_send_msg函数将该消息发送给LMAC:bl_msg_tx.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/bl_msg_tx.c#L315-L371

//  向LMAC固件发送消息的函数,静态定义
static int bl_send_msg(struct bl_hw *bl_hw, const void *msg_params, int reqcfm, lmac_msg_id_t reqid, void *cfm) {
  //  省略:为消息分配缓冲区
  ...
  //  省略:将消息复制到缓冲区 
  ...
  //  将消息添加到LMAC消息队列
  int ret = bl_hw->cmd_mgr.queue(&bl_hw->cmd_mgr, cmd);

上述代码使用ipc_host_msg_push函数将消息加入LMAC**消息队列**:ipc_host.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/ipc_host.c#L139-L171

//  将消息添加到LMAC消息队列。
//  IPC = Interprocess Communication 进程间通信
int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len) {
    //  Get the address of the IPC message buffer in Shared RAM
    uint32_t *src = (uint32_t*) ((struct bl_cmd *) msg_buf)->a2e_msg;
    uint32_t *dst = (uint32_t*) &(env->shared->msg_a2e_buf.msg);

    //  将消息复制到仪表板消息缓冲区
    for (int i = 0; i < len; i += 4) { *dst++ = *src++; }
    env->msga2e_hostid = msg_buf;

    //  触发LMAC中断以将消息发送到EMB
    //  IPC_IRQ_A2E_MSG is 2
    ipc_app2emb_trigger_set(IPC_IRQ_A2E_MSG);

在将消息复制到LMAC消息队列(位于共享RAM中)之后,我们调用ipc_app2emb_trigger_set来触发LMAC中断

随后,LMAC(以及BL602无线电硬件)将发送适当的中继WiFi数据包,用以建立与WiFi**接入点**的网络连接

这样,BL602就完成了与WiFi接入点的连接过程!

2.4 触发LMAC中断

我们是如何触发LMAC中断的呢?

//  触发LMAC中断以将消息发送到EMB
//  IPC_IRQ_A2E_MSG is 2
ipc_app2emb_trigger_set(IPC_IRQ_A2E_MSG);

我们来探究一下ipc_app2emb_trigger_set函数的内部工作原理,了解它是如何激活LMAC中断的:reg_ipc_app.h

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/reg_ipc_app.h#L41-L69

//  WiFi硬件寄存器基本地址
#define REG_WIFI_REG_BASE          0x44000000

//  IPC硬件寄存器基址
#define IPC_REG_BASE_ADDR          0x00800000

//  APP2EMB_TRIGGER 寄存器定义
//  Bits    Field Name           Reset Value
//  -----   ------------------   -----------
//  31:00   APP2EMB_TRIGGER      0x0
#define IPC_APP2EMB_TRIGGER_ADDR   0x12000000
#define IPC_APP2EMB_TRIGGER_OFFSET 0x00000000
#define IPC_APP2EMB_TRIGGER_INDEX  0x00000000
#define IPC_APP2EMB_TRIGGER_RESET  0x00000000

//  Write to IPC 寄存器
#define REG_IPC_APP_WR(env, INDEX, value) \
  (*(volatile u32 *) ((u8 *) env + IPC_REG_BASE_ADDR + 4*(INDEX)) \
    = value)

//  触发LMAC中断
static inline void ipc_app2emb_trigger_set(u32 value) {
  //  写入地址为0x4480 0000的WiFi IPC寄存器
  REG_IPC_APP_WR(
    REG_WIFI_REG_BASE, 
    IPC_APP2EMB_TRIGGER_INDEX, 
    value);
}

这段代码通过向WiFi硬件寄存器(用于进程间通信)写入,触发了LMAC中断。

REG_WIFI_REG_BASE + IPC_REG_BASE_ADDR + 4 * IPC_APP2EMB_TRIGGER_INDEX

这个地址对应的是0x4480 0000。

等等……事实上0x4480 0000这个地址在官方文档中有记录吗?

然而,这个地址并没有在BL602的参考手册中公开文档化的记录。

https://github.com/bouffalolab/bl_docs/blob/main/BL602_RM/en/BL602_BL604_RM_1.2_en.pdf

实际上,整个位于0x4400 0000WiFi硬件寄存器区域都没有在官方文档中提及。

我们刚刚揭开了一个BL602 WiFi的神秘面纱!

三、反编译WiFi固件演示demo

我们真的在逆向BL602 WiFi驱动程序吗?

还没有。到目前为止,我们一直在阅读BL602 WiFi驱动程序的公开**源代码**。

我们现在可以进行一些真正的逆向工程吗?

当然可以!BL602 WiFi驱动程序的大部分并没有附带**源代码**。 (比如WiFi WPA认证的函数)

但是,BraveHeartFLOSSDev使用Ghidra将BL602 WiFi演示固件成功地反编译成了C语言代码……

  • BraveHeartFLOSSDev/bl602nutcracker1

(我们将使用这个分支)

接下来,我们将研究这个反编译的C代码…… 并深入进行BL602 WiFi驱动程序的逆向工程!

  • 更多关于Ghidra的信息
  • Ghidra针对BL602的配置

https://github.com/BraveHeartFLOSSDev

https://github.com/BraveHeartFLOSSDev/bl602nutcracker1

https://github.com/lupyuen/bl602nutcracker1

https://ghidra-sre.org/

https://github.com/BraveHeartFLOSSDev/GhidWork

3.1 链接到反编译代码

遗憾的是,GitHub无法在网页浏览器中展示我们那些庞大的反编译C文件。这意味着直接链接到代码的特定行会比较困难。

不过,你可以通过下载反编译后的C文件仓库来解决这个问题。

  1. 下载反编译后的C文件仓库
  2. 当我们看到这样的链接时… 反编译后的代码在这里:bl602_demo_wifi.c

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L38512-L38609

点击链接右侧(或长按),然后选择“复制链接地址”。

  1. 将复制的地址粘贴到文本编辑器中。

然后可以看到如下

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L38512-L38609
  1. 记住链接尾部部分
bl602_demo_wifi.c#L38512-L38609
  1. 这意味着:

我们需要在代码编辑器(比如VSCode)中打开下载的源文件bl602_demo_wifi.c,然后使用Ctrl-G快捷键跳转到第38512行。

  1. 我们可以看到所提及的反编译C代码。

四、WiFi固件任务

BL602 WiFi驱动程序运行在两个后台任务(FreeRTOS)上…

  • WiFi管理任务:负责管理WiFi连接状态
  • WiFi固件任务:负责控制WiFi固件

我们已经了解了WiFi管理任务(还记得状态机吗?)。

现在,我们将深入研究WiFi固件任务,看看当我们启动并调度内核事件来处理WiFi数据包,以及处理WiFi数据包传输时会发生什么

  1. 启动WiFi固件任务
  2. 调度内核事件以处理WiFi数据包
  3. 处理WiFi数据包的传输

4.1 启动固件任务

我们之前看到了cmd_stack_wifi调用了hal_wifi_start_firmware_task来启动固件任务。

现在,让我们深入探究hal_wifi_start_firmware_task的内部工作原理:hal_wifi.c

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_wifi.c#L41-L49

//  启动WiFi固件任务 (FreeRTOS)
int hal_wifi_start_firmware_task(void) {
  //  WiFi固件任务的堆栈空间
  static StackType_t wifi_fw_stack[WIFI_STACK_SIZE];

  //  WiFi固件任务的任务句柄
  static StaticTask_t wifi_fw_task;

  //  创建FreeRTOS后台任务
  xTaskCreateStatic(
    wifi_main,         //  即将运行的任务函数
    (char *) "fw",     //  任务名 
    WIFI_STACK_SIZE,   //  任务堆栈大小
    NULL,              //  任务参数
    TASK_PRIORITY_FW,  //  任务优先级
    wifi_fw_stack,     //  任务堆栈
    &wifi_fw_task);    //  任务句柄
  return 0;
}

这创建了一个FreeRTOS**后台任务,它会一直运行wifi_main**函数。

那么,wifi_main里面是什么呢?

我们没有wifi_main的源代码。但是,得益于BraveHeartFLOSSDev,我们从BL602 WiFi固件中成功反编译得到了C代码。

下面是从反编译的C代码中提取的wifi_main函数:bl602_demo_wifi.c

https://github.com/BraveHeartFLOSSDev

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L32959-L33006

//  WiFi固件任务将永远运行
void wifi_main(void *param) {
  ...
  //  初始化LMAC和UMAC
  rfc_init(40000000);
  mpif_clk_init();
  sysctrl_init();
  intc_init();
  ipc_emb_init();
  bl_init();
  ...
  //  循环永远处理WiFi内核事件
  do {
    ...
    //  等等 
    if (ke_env.evt_field == 0) { ipc_emb_wait(); }
    ...
    //  安排WiFi内核事件并进行处理
    ke_evt_schedule();

    //  休息一下
    iVar1 = bl_sleep();

    coex_wifi_pta_forece_enable((uint) (iVar1 == 0));
  } while( true );
}

wifi_main函数的实际反编译C代码要复杂得多……

因此,我们为逆向工程筛选出了关键部分。(同时我们也添加了一些注释)

wifi_main函数会不断循环,处理WiFi内核事件,以实现WiFi数据包的传输和接收。

(“ke”代表WiFi内核,它是WiFi驱动程序的心脏)

wifi_main通过调用ke_evt_schedule函数来处理WiFi内核事件。

现在,让我们在我们的反编译C代码中查找ke_evt_schedule:bl602_demo_wifi.c

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L28721-L28737

//  安排WiFi内核事件并进行处理
void ke_evt_schedule(void) {
  int iVar1;
  evt_ptr_t *peVar2;

  while (ke_env.evt_field != 0) {
    iVar1 = __clzsi2(ke_env.evt_field);
    peVar2 = ke_evt_hdlr[iVar1].func;
    if ((0x1a < iVar1) || (peVar2 == (evt_ptr_t *)0x0)) {
      assert_err("(event < KE_EVT_MAX) && ke_evt_hdlr[event].func","module",0xdd);
    }
    (*peVar2)(ke_evt_hdlr[iVar1].param);
  }
  //  这一行可能被错误地反编译了
  gp = (code *)((int)SFlash_Cache_Hit_Count_Get + 6);
  return;
}

这段反编译代码执行了某些操作,但具体内容并不清晰。

幸运的是,一个特别的技巧能帮助我们解读这段晦涩的反编译代码——那就是GitHub搜索!

https://en.wikipedia.org/wiki/One_weird_trick_advertisements

4.2 调度内核事件

可能ke_evt_schedule并不是为BL602特别设计的?

也许它最初是为其他目的而开发的?

说对了!让我们在GitHub**上搜索**ke_evt_schedule!

  • 在GitHub上对ke_evt_schedule进行代码搜索

(搜索结果按最近索引排序)

我们会发现,ke_evt_schedule的代码来自于AliOS Things(一个嵌入式操作系统)和RivieraWaves(下一章会解释),具体在ke_event.c文件中。

https://github.com/search?o=desc&q=ke_evt_schedule&s=indexed&type=Code

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/ke/ke_event.c#L203-L231

//  事件调度程序入口点。必须在后台循环中调用此原语,才能执行所设置事件的事件处理程序。
void ke_evt_schedule(void) {
  uint32_t field, event;
  field = ke_env.evt_field;
  while (field) { // 假定编译器使用循环反转进行优化

    // 查找优先级最高的事件集
    event = co_clz(field);

    // 过滤检查
    ASSERT_ERR((event < KE_EVT_MAX) && ke_evt_hdlr[event].func);

    // 执行相应的处理程序
    (ke_evt_hdlr[event].func)(ke_evt_hdlr[event].param);

    // 更新volatile值
    field = ke_env.evt_field;
  }
}

这个版本与我们反编译的ke_evt_schedule版本进行对比…… 完全一致

甚至包括断言检查的部分!

(event < KE_EVT_MAX) && ke_evt_hdlr[event].func

既然这两个版本的ke_evt_schedule在功能上是完全相同的,那么我们来阅读AliOS/RivieraWaves版本的ke_evt_schedule代码。

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L28721-L28737

我们注意到,ke_evt_schedule通过调用ke_evt_hdlr中的事件处理程序来处理WiFi内核事件。

以下是来自AliOS / RivieraWaves代码的ke_evt_hdlr事件处理程序:ke_event.c

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/ke/ke_event.c#L78-L138

//  ke_evt_schedule调用的事件处理程序
static const struct ke_evt_tag ke_evt_hdlr[32] = {
  {&rwnxl_reset_evt,    0},      // [KE_EVT_RESET]        
  {&ke_timer_schedule,  0},      // [KE_EVT_KE_TIMER]   
  {&txl_payload_handle, AC_VO},  // [KE_EVT_IPC_EMB_TXDESC_AC3]

  //  此事件处理程序看起来很有意思                                                   
  {&txl_payload_handle, AC_VI},  // [KE_EVT_IPC_EMB_TXDESC_AC2]
  {&txl_payload_handle, AC_BE},  // [KE_EVT_IPC_EMB_TXDESC_AC1]
  {&txl_payload_handle, AC_BK},  // [KE_EVT_IPC_EMB_TXDESC_AC0]        

  {&ke_task_schedule,   0},      // [KE_EVT_KE_MESSAGE]
  {&mm_hw_idle_evt,     0},      // [KE_EVT_HW_IDLE]
  ...

txl_payload_handle是一个专门处理WiFi有效负载传输的事件处理程序。

接下来,我们将深入研究它的内部工作原理,了解它是如何发送WiFi数据包的。

4.3 处理传输负载

txl_payload_handle是什么?

得益于AliOS / RivieraWaves的源代码,我们得到了对txl_payload_handle函数的详细描述:位于txl_cntrl.h文件中。

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/tx/txl/txl_cntrl.h#L377-L386

//  对已从主机内存传输的有效负载执行操作。该原语由中断控制器ISR调用。如果需要,它执行LLC翻译和MIC计算。
//  LLC = 逻辑链路控制, MIC = Message Integrity Code消息完整性代码
void txl_payload_handle(int access_category);

这表明txl_payload_handle函数被调用来发送WiFi数据包……这发生在从BL602复制数据包有效负载到无线电硬件之后。(通过共享**RAM**缓冲区) 在我们的反编译代码中搜索txl_payload_handle,发现了如下内容:bl602_demo_wifi.c

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L20205-L20216

//  处理传输有效载荷
void txl_payload_handle(void) {
  while ((_DAT_44a00024 & 0x1f) != 0) {
    int iVar1 = __clzsi2(_DAT_44a00024 & 0x1f);

    //  写入0x44A0 0020的WiFi寄存器
    _DAT_44a00020 = 1 << (0x1fU - iVar1 & 0x1f);
  }
}

看起来它并没有进行太多有效负载的处理。但它向一个未在文档中提及的WiFi寄存器0x44A0 0020写入数据。

这可能触发了LMAC固件传输WiFi数据包吗?

但在此之前,我们发现了一些可能解释txl_payload_handle内部工作原理的信息……

4.4 第二个传输负载payload

在反编译代码中txl_payload_handle函数之后,有一个名为txl_payload_handle_backup的函数。

根据其名称,txl_payload_handle_backup可能是一个处理有效负载传输的辅助函数。

以下是txl_payload_handle_backup函数的反编译要点:bl602_demo_wifi.c

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L20222-L20398

//  另一个传输有效负载处理程序。
//可能与txl_payload_handle的工作方式相同
//在BL602而不是LMAC固件上运行。
void txl_payload_handle_backup(void) {
  ...
  //  Iterate through a list of packet buffers (?)
  while (ptVar4 = ptVar10->list[0].first, ptVar4 == (txl_buffer_tag *)0x0) {
LAB_230059f6:
    uVar3 = uVar3 + 1;
    ptVar10 = (txl_buffer_env_tag *)&ptVar10->buf_idx[0].free_size;
    ptVar11 = (txl_cntrl_env_tag *)(ptVar11->txlist + 1);
  }

txl_payload_handle_backup开始时会遍历一个待传输的数据包缓冲区列表。

然后它调用一些来自RivieraWaves的RXU、TXL和TXU相关的函数

//  循环 (until when?)
  do {
    //  调用一些RXU、TXL和TXU函数
    rxu_cntrl_monitor_pm((mac_addr *)&ptVar4[1].lenheader);
    ...
    txl_machdr_format((uint32_t)(ptVar4 + 1));
    ...
    txu_cntrl_tkip_mic_append(txdesc,(uint8_t)uVar2);

(关于RivieraWaves的更多内容,请参阅下一章)

接下来,我们将向一些尚未在文档中公开的WiFi寄存器写入数据:0x44B0 8180、0x44B0 8198、0x44B0 81A4和0x44B0 81A8……

//  写入WiFi寄存器
    _DAT_44b08180 = 0x800;
    _DAT_44b081a4 = ptVar9;
    ...
    _DAT_44b08180 = 0x1000;
    _DAT_44b081a8 = ptVar9;
    ...
    _DAT_44b08180 = 0x100;
    _DAT_44b08198 = ptVar9;

(这些寄存器也不在上述列表中)

该函数执行了一些断言检查

这些断言失败的消息可能有助于我们理解和解读反编译后的代码……

https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/reg_access.h

//  断言检查
    line = 0x23c;
    condition = "blmac_tx_ac_2_state_getf() != 2";
    ...
    line = 0x236;
    condition = "blmac_tx_ac_3_state_getf() != 2";
    ...
    line = 0x22f;
    condition = "blmac_tx_bcn_state_getf() != 2";
    ...
    line = 0x242;
    condition = "blmac_tx_ac_1_state_getf() != 2";
    ...
    line = 0x248;
    condition = "blmac_tx_ac_0_state_getf() != 2";
    ...
    assert_rec(condition, "module", line);

该函数的执行以设置一个定时器作为结束。

//  设置计时器
    blmac_abs_timer_set(uVar6, (uint32_t)(puVar8 + _DAT_44b00120));

    //  继续循环
  } while( true );
}

txl_payload_handle_backup的原始源代码真的有这么长吗?

很可能不是。C编译器可能会通过内联一些函数来优化固件代码。

当我们对固件进行反编译时,原本内联的代码现在出现在调用它们的函数内部。

(这就是为什么反编译代码中会出现大量重复内容的原因)

接下来,让我们来探讨一下RivieraWaves的相关内容……

五、专题 CEVA RivieraWaves

在GitHub上搜索WiFi事件调度程序ke_evt_schedule时,我们找到了这个源代码:

  • mclown/AliOS-Things

(我们将使用这个分支)

这似乎是AliOS Things嵌入式操作系统的源代码,它被移植到了Beken BK7231U WiFi SoC。

但在源文件的顶部,我们注意到……

Copyright (C) RivieraWaves 2011-2016

这意味着WiFi固件的部分源代码实际上来自于CEVA RivieraWaves,而不是AliOS!

https://github.com/mclown/AliOS-Things/tree/master/platform/mcu/bk7231u/beken/ip

https://github.com/lupyuen/AliOS-Things/tree/master/platform/mcu/bk7231u/beken/ip

http://www.bekencorp.com/en/goods/detail/cid/13.html

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/ke/ke_event.c

CEVA RivieraWaves是什么?

RivieraWaves是运行在WiFi SoCs(比如BL602)上实现802.11无线协议的软件/固件

在BL602上有两层RivieraWaves固件:

  • 上层媒体访问控制(Upper Medium Access Control,UMAC):在BL602 RISC-V CPU上运行。我们之前看到的一些代码,比如内核事件调度器,就来自UMAC。
  • 下层媒体访问控制(Lower Medium Access Control,LMAC):运行在BL602无线电硬件内部。

我们还可以了解更多关于WiFi媒体访问控制的信息。

以及更多关于RivieraWaves的内容。

RivieraWaves是否在其他地方使用?

是的,RivieraWaves被广泛应用于许多流行的WiFi SoCs。

https://www.ceva-dsp.com/product/rivierawaves-wi-fi-platforms/

https://www.controleng.com/articles/wi-fi-and-the-osi-model/

https://www.ceva-dsp.com/product/rivierawaves-wi-fi-platforms/

https://csimarket.com/stocks/markets_glance.php?code=CEVA#:~:text=Included%20among%20our%20licensees%20are,%2C%20RDA%2C%20Renesas%2C%20Rockchip%2C

5.1 上层媒体访问控制

回想一下,UMAC(上层媒体访问控制)是运行在BL602 RISC-V CPU上的RivieraWaves代码。

当我们比较BL602 WiFi固件的反编译结果与AliOS / RivieraWaves代码时,我们发现了BL602中使用的UMAC模块(和通用模块)的源代码……

  1. CO模块(通用)
  2. KE模块(内核)
  3. ME模块(消息?)
  4. RC模块(速率控制)
  5. RXU模块(接收UMAC)
  6. SCANU模块(扫描SSID UMAC)
  7. SM模块(状态机)
  8. TXU模块(传输UMAC)

这些模块在BL602和AliOS / RivieraWaves之间大多是相同的,除了RXU模块看起来有所不同。

(更多关于UMAC模块的匹配将在我们讨论定量分析时介绍)

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/common

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/ke

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/me

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/rc

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/rxu

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/scanu

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/sm

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/umac/src/txu

5.2 下层媒体访问控制

请记住,LMAC(下层媒体访问控制)是运行在BL602无线电硬件内部的RivieraWaves代码。

通过将BL602 WiFi固件的反编译结果与AliOS / RivieraWaves代码进行匹配,我们识别出了BL602无线电硬件所暴露的LMAC接口…

  1. APM接口(AliOS中未提供)
  2. CFG接口(AliOS中未提供)
  3. CHAN接口(负责MAC频道管理)
  4. HAL接口(提供硬件抽象层)
  5. MM接口(负责MAC管理)
  6. RXL接口(负责接收LMAC数据)
  7. STA接口(负责站点管理)
  8. TXL接口(负责传输LMAC数据)

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/chan

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/hal

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/mm

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/rx/rxl

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/sta

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/tx/txl

上述链接的LMAC接口仅供参考,因为BL602实现的LMAC与上面提到的Beken BK7231U实现有很大不同

这些LMAC模块在BL602和AliOS / RivieraWaves之间似乎是基本相同的……

  • PS模块(省电)
  • SCAN模块(扫描SSID)
  • TD模块(流量检测)
  • VIF模块(虚拟接口)

(更多关于LMAC模块匹配的细节将在我们讨论定量分析时提供)

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/ps

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/scan

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/td

https://github.com/lupyuen/AliOS-Things/blob/master/platform/mcu/bk7231u/beken/ip/lmac/src/vif

六、专题 WiFi Supplicant

WiFi Supplicant是负责处理WiFi认证的代码。

(比如WPA和WPA2)

那么,WiFi Supplicant代码是从RivieraWaves来的吗?

不是的。根据反编译的代码,BL602实现了自己的WiFi Supplicant,包含诸如supplicantInit、allocSupplicantData和keyMgmtGetKeySize等函数。(请查看相关部分)

也许WiFi Supplicant的代码来自另一个项目?

当我们搜索GitHub上的这些函数名称时,我们找到了与此相匹配的源代码……

(我们将使用这个分支)

实际上,那是基于Linux的Rockchip RK3399的WiFi Supplicant代码。

它们的代码真的完全一样吗?

我们对比了反编译的BL602 WiFi Supplicant代码和Rockchip RK3399的源代码……它们几乎是一模一样的!(请查看相关部分)

这真是太棒了,因为我们刚刚揭开了BL602固件中(大约)2,500行代码的神秘起源!

(这些数据来自定量分析,我们稍后会详细讨论)

https://en.wikipedia.org/wiki/Wireless_supplicant

https://en.wikipedia.org/wiki/Wpa_supplicant

https://lupyuen.github.io/images/wifi-supplicant.png

https://github.com/search?o=desc&q=supplicantInit+allocSupplicantData+keyMgmtGetKeySize&s=indexed&type=Code

https://github.com/karthirockz/rk3399-kernel/tree/main/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa

https://github.com/lupyuen/rk3399-kernel/tree/main/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa

https://lupyuen.github.io/images/wifi-supplicant2.png

七、WiFi 物理层

WiFi物理层是什么?

WiFi物理层是控制无线频率的协议,它决定了WiFi数据包应该如何发送和接收。

(它位于媒体访问控制层之下)

更多关于WiFi物理层的信息

难道是BL602的物理层没有使用RivieraWaves吧?

不,我们怀疑BL602的物理层并不是来自RivieraWaves。

BL602物理层的起源有些模糊……

这里是从反编译代码中提取的BL602物理层的一部分:bl602_demo_wifi.c

https://www.controleng.com/articles/wi-fi-and-the-osi-model/

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c#L33527-L33614

//  来自BL602反编译代码:初始化物理层
void phy_init(phy_cfg_tag *config) {
  mdm_reset();
  ...
  mdm_txcbwmax_setf((byte)(_DAT_44c00000 >> 0x18) & 3);
  _Var2 = phy_vht_supported();
  agc_config();
  ...
  // 初始发射机速率功率控制
  trpc_init();

  // 初始物理自适应功能
  pa_init();

  phy_tcal_reset();
  phy_tcal_start();
}

在GitHub上搜索phy_init和phy_hw_set_channel(BL602的另一个函数)时,我们找到了一个有用的结果……

  • jixinintelligence/bl602-604

(我们将使用这个分支)

它实现了phy_init函数,具体如下:phy_bl602.c……

https://github.com/search?q=phy_init+phy_hw_set_channel&type=code

https://github.com/jixinintelligence/bl602-604

https://github.com/lupyuen/bl602-604

https://github.com/lupyuen/bl602-604/blob/master/components/bl602/bl602_wifi/plf/refip/src/driver/phy/bl602_phy_rf/phy_bl602.c#L474-L492

//  从GitHub搜索: Init Physical Layer //初始化物理层:
void phy_init(const struct phy_cfg_tag *config) {
  const struct phy_bl602_cfg_tag *cfg = (const struct phy_bl602_cfg_tag *)&config->parameters;
  phy_hw_init(cfg);
  phy_env->cfg               = *cfg;
  phy_env->band              = PHY_BAND_2G4;
  phy_env->chnl_type         = PHY_CHNL_BW_OTHER;
  phy_env->chnl_prim20_freq  = PHY_UNUSED;
  phy_env->chnl_center1_freq = PHY_UNUSED;
  phy_env->chnl_center2_freq = PHY_UNUSED;

  // 初始化传输速率控制
  trpc_init();

  // 初始物理自适应功能
  pa_init();
}

将BL602的反编译代码与GitHub上的搜索结果进行对比……BL602的代码似乎执行了更多的操作

(tdm_reset、phy_tcal_reset和phy_tcal_start的调用在哪里?)

因此,我们并没有找到BL602物理层的100%完全匹配。(可能只有50%)

尽管如此,这对于我们的逆向工程来说是一个非常有价值的发现!

八、定量分析

我们实际上需要解码多少行反编译代码呢?

BL602 WiFi源代码中已经可以在其他地方找到多少部分?

要回答这些问题,我们需要对BL602固件的反编译代码进行定量分析。

(这是对电子表格进行数据处理的术语)

我们需要执行以下步骤:

  1. 从BL602 WiFi演示固件的反编译版本中提取所有函数名称
  2. 将这些函数名称导入电子表格进行进一步分析。
  3. 根据模块对这些函数名称进行分类
  4. 将反编译的函数代码与通过GitHub搜索找到的源代码**进行匹配**。
  5. 统计没有找到匹配源代码的反编译代码行数。

8.1 提取反编译函数

BL602 WiFi演示固件bl602_demo_wifi已经被反编译成一个庞大的C文件。

  • 反编译的WiFi演示固件bl602_demo_wifi.c

我们运行这个命令来提取所有函数名称及其行号,以便于后续的代码行数统计。

https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/bl602_demo_wifi

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.c

# Extract the function names (and line numbers)
# from the decompiled firmware. The line must
# begin with an underscore or a letter,
# without indentation.
grep --line-number \
    "^[_a-zA-Z]" \
    bl602_demo_wifi.c \
    | grep -v LAB_ \
    >bl602_demo_wifi.txt

这将生成bl602_demo_wifi.txt,一个包含反编译函数名称及其行号的长列表。(这里是一个示例)

(还包括函数参数和类型定义……我们很快就会处理掉这些内容)

但这个列表包含了所有内容……包括非WiFi函数,对吧?

是的。但是,查看反编译固件中的每一个函数(总共128,000行代码)也很有趣,只是为了看看它是如何运作的。

为什么不直接反编译并分析BL602 WiFi库:libbl602_wifi.a?

BL602 WiFi库libbl602_wifi.a可能包含一些不会链接到WiFi固件的额外WiFi函数。

因此,我们反编译并分析的是实际由WiFi固件调用的WiFi函数

(顺便说一下:我们计算代码行数时会包括空行和注释行)

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.txt

https://lupyuen.github.io/images/wifi-quantify.png

https://github.com/pine64/bl602-re/tree/master/blobs

8.2 整理函数到电子表格

我们将bl602_demo_wifi.txt(包含反编译函数名称和行号的列表)导入到一个电子表格中,用于进一步分析。(见上图)

这是我们进行定量分析**的电子表格**,采用了不同的格式……

我们清理数据,移除了类型定义、函数返回类型和函数参数。

根据行号,我们计算了每个函数的代码行数(包括空行和注释行)。

然后应用条件格式化,以突出显示代码行数最多的反编译函数。(这些函数通常也是最复杂的

在分析过程中,这些函数值得我们更多的关注。

https://github.com/lupyuen/bl602nutcracker1/blob/main/bl602_demo_wifi.txt

8.3 反编译函数分类

接下来,我们将每个反编译函数根据所属模块进行分类

上面的图片显示我们已经将“rxl_”函数分类为“???RivieraWaves RXL”(RXL代表接收LMAC)。

我们使用“???”来标记那些我们找不到任何源代码的模块。

对所有3,000个反编译函数进行分类听起来很繁琐……吗?

幸运的是,属于同一个模块的反编译函数通常是聚集在一起的。因此,复制并填写一批函数的模块名称是相对简单的。

还记得我们对复杂函数的红色突出显示吗?如果对如何分类不太复杂的函数不确定,跳过分类是可以的。

在我们的电子表格中,我们已经对超过97,000行反编译代码进行了分类,这占所有反编译代码行数的86%。这对于我们的分析来说已经足够好了!

8.4 匹配反编译函数

还记得我们之前通过GitHub**搜索**找到的源代码吗?

(用于RivieraWaves、WiFi Supplicant和物理层)

现在我们深入研究那些源代码,看看它们与反编译函数的匹配程度有多高

我们如何记录我们的发现?

在我们的电子表格中,有一个列记录了与我们反编译函数匹配的源代码**URL**(来自GitHub搜索)。(见上图)

我们还添加了一条评论,说明它们匹配得有多近。例如:“BL602版本有所不同”

如果发现的原代码与反编译函数不匹配,我们会用“???”标记模块名称

我们需要匹配每一个反编译函数吗?

为了简化匹配过程,我们从每个模块中选取了一个或两个最复杂的函数进行匹配

(是的,红色突出显示真的很有帮助!)

因此,我们的匹配并不是100%彻底和准确的……但它相当准确。

8.5 代码行数计算

最后,我们在电子表格中加入了一个数据透视表,用于计算与GitHub搜索结果匹配或不匹配的代码行数。

在我们的电子表格第二页,我们可以看到一个数据透视表,它汇总了我们定量分析**的结果**……

  1. 需要进行逆向工程的代码行数:10,500行

在GitHub搜索中未找到的代码包括LMAC接口和WiFi Supplicant的一部分。

  1. 部分需要逆向工程的代码行数:3,500行

我们在GitHub搜索中找到了物理层的部分匹配代码。

(关于BL602 HAL和标准驱动程序的讨论将在下一章进行)

  1. 在其他地方已经找到的代码行数:11,300行(真是惊人!)

在GitHub搜索中找到了UMAC和WiFi Supplicant的大部分代码。

  1. 我们还拥有来自BL602 WiFi驱动程序的7,500行代码,这部分代码的源代码可以在BL602**物联网软件开发套件(BL602 IoT SDK**)中找到。(请查看相关部分)

这其中包括我们之前已经了解的WiFi管理器

总结:我们有大量的**源代码可以用来指导BL602 WiFi的**逆向工程工作!

九、其他模块

WiFi相关函数占据了反编译WiFi固件总代码行数的29%

那么,剩下的71%的反编译代码中包含了什么呢?

接下来,让我们查看反编译固件中的非WiFi函数部分……

(复杂模块已用红色突出显示)

  • AliOS: 用于多任务和设备驱动的嵌入式系统框架
  • AWS IoT, AWS MQTT: 演示固件与AWS云进行物联网和MQTT(消息队列)服务的通信
  • BL602 Hardware Abstraction Layer (HAL): 硬件抽象层包含引导加载程序、DMA、GPIO、闪存内存、中断、实时时钟、安全(加密)、UART等功能
  • BL602 Standard Driver: 标准驱动程序由BL602硬件抽象层调用,用于访问BL602的硬件寄存器
  • C Standard Library: C标准库,因为我们的固件是使用GCC编译器编译的
  • EasyFlash: 一个嵌入式数据库
  • FreeRTOS: 一个运行在AliOS下面的嵌入式操作系统
  • Lightweight IP (LWIP): 轻量级IP,提供IP、UDP、TCP和HTTP网络通信功能
  • Mbed TLS: 实现传输层安全,AWS IoT和MQTT服务需要这个安全机制

大多数非WiFi函数的源代码都可以找到。

(只需点击上方的链接即可)

十、GitHub搜索:最好的伙伴

我们得到了一个宝贵的启示……GitHub搜索是我们进行逆向工程的最好帮手!

通过GitHub搜索,我们发现了以下信息:

  1. UMAC和LMAC的源代码
    1. GitHub Code Search for ke_evt_schedule
  2. WiFi Supplicant的源代码
    1. GitHub Code Search for supplicantInit, allocSupplicantData and keyMgmtGetKeySize
  3. 物理层的源代码
    1. GitHub Code Search for phy_init and phy_hw_set_channel

因此下次在进行任何逆向工程时,别忘了使用GitHub搜索!

十一、 下一步内容

这是一次激动人心的逆向旅程……感谢 Pine64 BL602 Reverse Engineering Project 工程项目的贡献者激发了我撰写这篇文章的灵感!

今天,我们为了教育目的进行了一些逆向工程……只是为了弄明白BL602是如何发送和接收WiFi数据包的。

现在我们知道了如何探测反编译的WiFi固件,以揭开每一个WiFi硬件寄存器的秘密。

我希望有人能继续这项逆向工程工作……也许能为BL602开发一个开源的WiFi驱动程序!

(也许我们应该重启Pine64 BL602 Nutcracker项目)

在下一篇文章中,我们将探讨BL706音频视频板……

所以请继续关注关于BL602、BL604和BL706的更多精彩内容吧!

如果您有任何问题、评论或建议,请在这里创建一个Issue或提交一个Pull Request……

lupyuen.github.io/src/wifi.md

BL706 音视频开发板

十二、 附录与笔记

  1. 本篇文章属于该推文的完整版本 this Twitter Thread
  2. 根据推文信息 madushan1000, BL602 WiFi RTL 也许能在这里找到…
    1. fengmaoqiao/my_logic_code
    2. fengmaoqiao/workplace
    3. More tips on BL602 WiFi and Bluetooth LE
  3. 更多有关 BL602 RF IP and 硬件寄存器信息:
    1. 硬件信息: RF IP
    2. 硬件信息: MDM 寄存器
    3. 硬件信息: AGC 寄存器
  4. 有一个有趣的讨论涉及到WiFi Supplicant的许可问题,这个讨论的版本看起来与Rockchip RK3399上的Linux版本完全一样。

(出处链接)

5 根据Twitter上的用户SpritesMods,ESP32使用了CEVA的蓝牙IP,但没有使用CEVA的WiFi IP

0 条评论
某人
表情
可输入 255

没有评论