ELF文件转至EFI文件的转换器

本文是应作者格拉斯神剑要求,转载的文章。原文在:https://www.bilibili.com/opus/1049776129847590912?spm_id_from=333.1387.0.0

有兴趣的朋友可以关注他的项目。

格拉斯神剑使用Pascal(Free Pascal)语言写了一个能够将Linux下的可执行文件格式ELF转为UEFI下的可执行格式EFI的转换器(为了跨平台方便,做成了命令行程序,可以把各个UEFI所支持架构对应的ELF文件转换为对应的EFI文件(并不是x64平台下只能转换x64平台下的elf文件,而是什么架构的elf文件转换为对应架构的efi文件,如果elf是riscv的,转换为riscv上的UEFI EFI文件,这点可以直接点击或命令行执行elf2efi/elf2efi.exe可以看到),不知道这是不是第一个使用Pascal写elf2efi软件的。

当然这里的ELF文件是有要求的,要求如下:

1.转换前的ELF文件必须是static-pie(静态并且位置无关的可执行文件)(可以用-fPIE -Wl,–no-dynamic-linker(GNU C Compiler,gcc)或-k-pie -k–no-dynamic-linker(free pascal compiler,fpc)的编译选项得到)。

2.不能链接任何动态库,否则会导致转成的EFI文件在UEFI下运行失败。

3.对于x64平台,入口函数要求必须是微软ABI;对于i386平台,入口函数要求必须是cdecl(c的调用规范)。显然,对于所有UEFI支持的平台,其入口函数的参数必须符合UEFI规范(在UEFI国际论坛(UEFI.org)的开发者选项点开规范,会看到UEFI Specifications,点开版本号最高的就是,点进去搜索入口函数的定义即可)。

4.ELF文件本身不能有空指针这样运行时发生的错误,否则就会在转换的EFI文件里面出现错误并被UEFI Debugger报告显示。

5.ELF文件必须平台无关,避免链接系统相关的静态库和标准静态库(当然UEFI相关的库可以链接,但必须是静态库,不能动态库,动态库在链接时不会链接进去,会造成EFI文件运行时发生错误)

本程序既可以在gitee上面(https://gitee.com/tydqsoft/elf2efi)也可以在github上面(https://github.com/TYDQSoft/elf2efi)上面进行下载(两边都是一样的,下载最新版本Alpha v0.0.2就行,顺带附上Pascal源代码,自由开源)

顺带附上该命令行程序的截图(amd64架构windows下):

1.对UEFI支持的所有架构(总共4个,x64,aarch64,riscv64,loongarch64)的支持更加完美

2.比上一个版本有更快的转换速度

3.转换出来的efi文件一般来说比elf文件小

(以下的测试efi程序(由elf文件转换出来的efi文件)的功能仅是把屏幕的所有部分都变成黄色,当然不同架构的需要编译器编译成不同架构的二进制文件elf然后使用elf2efi转换来进行测试)(本elf2efi软件是自己用pascal写的,不是github上那个用C语言写的elf2efi)

x64转换成功可运行示意图:

FFMPEG  生成测试视频

在研发测试中,我们经常需要从网络或者本地播放一段视频以便验证系统的稳定性。同样的,我们在测试网络视频播放的问题时经常要被问到: 本地播放是否存在问题?因此,这里研究如何生成一个测试视频,并且传到网络上,这样可以方便的得知问题是否和网络有关系。

ffmpeg 可以使用下面的命令产生一个5秒,1280×720名称为LABZPattenX.mp4的

本地视频,编码和压缩使用 Windows 默认方式,这样生成的视频可以直接在Windows中播放:

ffmpeg -f lavfi -i gradients=c0=red:c1=blue:c2=green:n=3:duration=5:size=1280x720:rate=60:type=circular:seed=1,format=rgb0 -c:v libx264 -pix_fmt yuv420p -b:v 6000k LABZPattenX.mp4

视频内容:

为了更好的测试,我用上述方法生成了1小时的视频内容上传到 B站,链接如下:

有兴趣的朋友可以使用如下方法生成1小时的内容

ffmpeg -f lavfi -i gradients=c0=red:c1=blue:c2=green:n=3:duration=3600:size=1920x1080:rate=60:type=circular:seed=1,format=rgb0 -c:v libx264 -pix_fmt yuv420p LABZPatten1.mp4

还可以指定一个较高的码率:

ffmpeg -f lavfi -i gradients=c0=red:c1=blue:c2=green:n=3:duration=3600:size=1920x1080:rate=60:type=circular:seed=1,format=rgb0 -c:v libx264 -pix_fmt yuv420p  -b:v 6000k LABZPatten3.mp4

谢尔宾斯基地毯:

ffmpeg -y -filter_complex  sierpinski=s=1920x1080:type=carpet:rate=60:jump=3:seed=1 -c:v libx264 -pix_fmt yuv420p -b:v 6000k -t 3600 LABPatten2.mp4

上述方法的优点:

  1. 没有版权问题;
  2. 线上线下内容相同;
  3. 可以生成需要的任意分辨率,以及帧率;

网站提供了这次测试对应的 FFMPEG 文件,有兴趣的朋友可以在网站的“常用测试工具以及软件下载”页面看到:

有需要的朋友可以自行尝试。

本文提到的生成的测试视频可以在下面的链接看到:

https://player.bilibili.com/player.html?isOutside=true&aid=114268108231934&bvid=BV1mvfKYJE8A&cid=29141635048&p=1

https://player.bilibili.com/player.html?isOutside=true&aid=114268125010083&bvid=BV1mYfKYDEdb&cid=29141699907&p=1

https://player.bilibili.com/player.html?isOutside=true&aid=114268141785602&bvid=BV1PcfKYCEUA&cid=29141697090&p=1

参考:

  1. https://trac.ffmpeg.org/wiki/FancyFilteringExamples   介绍测试Patten
  2. https://ayosec.github.io/ffmpeg-filters-docs/5.1/Sources/Video/gradients.html gradients 使用参数介绍
  3. https://ffmpeg.org/doxygen/trunk/vsrc__gradients_8c_source.html  gradients源代码参数部分

如何制作一个 UEFI Shell 启动盘

UEFI Shell 是一个类似 DOS Command 启动环境的东西,对于普通用户来说它最常见的功能是启动进入之后能够烧写更新BIOS或者其他Firmware。

这次就介绍一个如何制作标准的 UEFI Shell 启动盘。

简单的说,主要分为两步:

1.格式化一个U盘,特别注意需要格式化为 FAT

对于一些容量较大的U盘,还可以选择 exFAT,但是兼容性会差一些,不能保证一定能够启动

2.下载一个 UEFI Shell 的 EFI 文件,这个是 EDK2 开源的项目,在 https://github.com/tianocore/edk2 下载到源代码后,可以编译获得 EFI 文件。如果觉得麻烦,可以在这里下载一个我编译好的(来自edk2-stable202408.01)

3.在上面格式化好的U盘上创建 EFI\BOOT 目录,然后放入 UEFI Shell 文件并且命名为 BOOTX64.EFI

4.在目标机上选择启动到U盘,即可进入 UEFI Shell。

如果你希望其他版本的UEFI Shell(比如 IA32版本, ARM 版本),可以在 https://github.com/pbatard/UEFI-Shell/releases/tag/24H2 下载到

EasyX 测试颜色渐变代码

让AI 给我写了一个颜色渐变的算法,代码如下:

#include <graphics.h>  // EasyX图形库头文件
#include <conio.h>     // 用于_getch()
#include <math.h>    
#include <stdio.h>  
#include <cstdio>
#include <conio.h>
#include <tchar.h>

// HSL转RGB辅助函数
void HSLtoRGB(float h, float s, float l, BYTE& r, BYTE& g, BYTE& b) {
    float q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    float p = 2 * l - q;
    float t[3] = { h + 1 / 3.0f, h, h - 1 / 3.0f };

    for (int i = 0; i < 3; ++i) {
        if (t[i] < 0) t[i] += 1;
        if (t[i] > 1) t[i] -= 1;

        if (t[i] < 1 / 6.0f)
            t[i] = p + (q - p) * 6 * t[i];
        else if (t[i] < 0.5f)
            t[i] = q;
        else if (t[i] < 2 / 3.0f)
            t[i] = p + (q - p) * 6 * (2 / 3.0f - t[i]);
        else
            t[i] = p;
    }

    r = static_cast<BYTE>(t[0] * 255);
    g = static_cast<BYTE>(t[1] * 255);
    b = static_cast<BYTE>(t[2] * 255);
}

// 在视图类中定义成员变量
float m_currentHue = 0.0f;        // 当前色相值 [0,1]
float m_targetHue = 0.0f;         // 目标色相值 [0,1]
float m_transitionSpeed = 0.1f; // 颜色过渡速度


int main()
{
    char Buffer[256];
    // 初始化640x480像素的图形窗口
    initgraph(640, 480);

    for (int i = 0; i < 60*30; i++) {
        BeginBatchDraw();
        cleardevice();
        // 线性插值过渡
        m_currentHue += (m_targetHue - m_currentHue) * m_transitionSpeed;

        // 到达目标时生成新随机目标
        if (fabs(m_targetHue - m_currentHue) < 0.001f) {
            m_targetHue = static_cast<float>(rand() % 1000) / 1000.0f;
        }

        BYTE r, g, b;
        HSLtoRGB(m_currentHue, 0.7f, 0.5f, r, g, b); // 固定饱和度和亮度
        printf("[%f]->%d  %d  %d\n", m_currentHue,r, g, b);
        setbkcolor(RGB(r, g, b));
        EndBatchDraw();
       
        sprintf_s(Buffer, sizeof(Buffer), "output\\%05d.png", i);
        LPCTSTR lpctstr = (LPCTSTR)Buffer;  // 直接强制类型转换
        printf("%s\n", Buffer);
        saveimage(lpctstr);

    }

    // 保持窗口显示
    //_getch();

    // 关闭图形窗口
    closegraph();
    return 0;
}

上述代码能够无限生成渐变的颜色。

测试的视频如下:

Step to UEFI (298)将 RU 装入 Shell 中

这次的实验是将 RU 内置在Shell 中,当你输入 zru 之后就可以执行 RU(特别命令为 zru 是为了和 ru 避免冲突)。代码是基于之前的 MyCmd 实现的。特别需要注意的地方是 RuCommand.c 文件中的 MyShellCommand() 函数中,我们调用 LoadImage 需要传递 ParentImageHandle, 但是MyShellCommand(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) 传递进来的ImageHandle一直是0。这样会导致LoadImage调用失败。因此,这里使用了gImageHandle,给出的是 UEFI Shell 的 Handle。

        //
        // Load the image with:
        //
        Status = gBS->LoadImage(
                        FALSE,
                        gImageHandle,
                        DP,
                        (VOID*)&amp;ru_efi[0],
                        ru_efi_size,
                        &amp;NewHandle);     
        if (EFI_ERROR(Status)) {
                Print(L"Load image Error! %r\n",Status);
                return 0;
        }

最终生成的完整 UEFI Shell:

完整的代码(基于 EDK2 202411 ):

这样做的好处是方便用户使用,只用一个文件就可以实现很多功能,缺点是:更新内容不太容易。

参考:

1. https://www.lab-z.com/stu165/

检查 HSTI 的简便方法

‌HSTI(Hardware Security Test Interface)‌是由Microsoft定义的硬件安全测试接口,其全称为Hardware Security Testability Interface。HSTI的主要作用是确保Windows设备上的安全配置正确,通过定义一系列测试来验证设备的安全性。这些测试包括SPI闪存或eMMC分区锁定、SMM配置、Intel Boot Guard等安全功能的正确设置‌。

基本的原理是:BIOS在启动的时候进行一些检查,然后将检查结果汇报给 Windows。在进行微软 WHQL 测试时,这是一个必测的项目。通常情况下,BIOS工程师需要搭建WHQL测试环境,在目标机上安装 WHQL Client软件,每次修改后都需要在 WHQL Server 上运行测试并且查看结果。

现在,可以通过本地运行 FaintSnow 编写的 HE (Hardware Read & Write Utility)来进行检测,避免繁琐的WHQL环境设定:

打开工具后在 Tools ->Dump Hardware Security Test Interface Report中即可看到结果:

工具读取到的信息:

可以直接拉到最下面查看结果:

作者的网站是 http://hwrwdrv.phpnet.us 。可以在上面看到最新版。此外,还可以在 https://www.lab-z.com/allsoft/ 常用测试工具以及软件下载页面看到(不定期更新,下载速度较快),有测试需要的朋友可以试试这个工具。

参考:

1. https://www.lab-z.com/hstiwhql/

USB 手柄转无线

这是一个能够将USB 手柄转为无线手柄的设备。这样,你可以将有线的 USB 手柄变为无线的手柄进行游戏。之所以做这个项目是因为我发现Linux 系统在支持多HID设备上和 Windows上有所不同。之前设计的,能够在 Windows上工作正常的USB转蓝牙设备无法在 Linux 系统上正常工作。

这次带来的方案是有两部分:发射端和接收端。

发射端是 ESP32 C3 配合 CH9350实现的,它能够实现USB手柄数据的读取和无线信号的发送。其中 CH9350负责USB手柄数据的解析;解析后的数据由ESP32 C3通过ESP-Now发送出去。CH9350是 WCH (就是出品 Ch340 的那个公司)推出的USB HID 转串口通讯控制芯片。就是说USB 手柄连接到这个芯片之后,数据会转化为串口输出。关于这个芯片的功能介绍如下:

  • 支持12Mbps全速USB传输和1.5Mbps低速USB传输,兼容USB V2.0。
  • 上位机端USB端口符合标准HID类协议,不需要额外安装驱动程序,支持内置HID类设备驱动的Windows、Linux、macOS等操作系统。
  • 同一芯片可配置为上位机模式和下位机模式,分别连接USB-Host主机和USB键盘、鼠标。
  • 支持USB键盘鼠标在BIOS界面使用,支持多媒体功能键,支持不同分辨率USB鼠标。
  • 支持各种品牌的USB键盘鼠标、USB无线键盘鼠标、USB转PS2线等。
  • 上位机端和下位机端支持热插拔。
  • 提供发送状态引脚,支持485通讯。
  • 串口支持115200/57600/38400串口通信波特率。
  • 内置晶振和上电复位电路,外围电路简单。
  • 支持5V、3.3V电源电压。
  • 提供LQFP-48无铅封装,兼容RoHS。

发射端方案的优点是:成本比较低,体积比较小,容易DIY焊接(ESP32 C3 是我用过的最容易焊接的ESP32芯片)。能够同时支持2个USB设备,就是说你可以同时使用2个USB手柄,同时转化为无线给主机使用。

接收器使用的是ESP32-S2 ,它带有USB Device ,能够方便的将自身模拟为一个USB手柄。因此,无论 Windows 还是 Linux 只要支持USB 手柄,在操作系统端看起来插入的就是一个USB手柄,完全不会碰到兼容性问题。这里使用淘宝直接购买的Mini ESP32-S2 开发板,体积小,价格便宜。

接下来首先进行电路的设计。

  1. 主控 ESP32 C3 部分。这款主控内置了 USB 下载电路,我们设计一个 USB接口即可工作。此外,复位与下载按钮是必须的,当出现问题无法下载时,这两个按钮随时可以帮助恢复。

2.接下来时CH9350部分,它外部电路非常简单,只需要一个 3.3uf和一个 0.1uf电容即可工作。外部的 LED1和 LED2用来指示USB工作状态,没有有效数据时会亮,有数据传输时会熄灭。其中的USB3 是一个双层USB座子,这样我们可以同时使用两个USB手柄。

3.我们使用 TLV1117将5V 转为3.3V,同时还预留一个假负载,用来避免在使用充电宝供电,输出小于50ma 一段时间后自动关机的问题。

电路比较简单PCB设计也相对简单:

预览如下:

上面就是硬件设计,接下来着手软件的设计。

同样,分成发送端和接收端分别介绍。

发送端的主要工作是:USB 手柄数据的获取,获得数据的发送。

对于USB 手柄数据的获取和之前的并没有多少差别,只需要解析来自串口的数据即可;特别注意代码加入了判断,只有收到和之前数据不同的时候才会发送;

我们使用 ESP-Now 进行数据发送。对于 Arduino 开发来说,这个非常简单。创建 ESPNow 对象,然后指定发送的地址即可:

// 创建esp_now_peer_info_t类型变量存储有关peer方的信息。
esp_now_peer_info_t peerInfo0;
esp_now_peer_info_t peerInfo1;

//  ESP32 接收器 MAC 地址
uint8_t broadcastAddress0[] = {'L', 'A', 'B', 'Z', '-', '0'};
uint8_t broadcastAddress1[] = {'L', 'A', 'B', 'Z', '-', '1'};

之后每次收到改变后的USB手柄数据,可以使用下面的函数进行发送

// Send message via ESP-NOW
                esp_err_t result = esp_now_send(broadcastAddress1, (uint8_t *) &amp;Data[i + 6], 7);

对应的接收端的设计如下:

  1. 程序开始处,设置当前ESP32 的MAC地址,作为接收数据的地址。发送端同时支持2个USB手柄,因此我们通过GPIO 判断设置2个Mac。这样,我们的程序刷到2个ESP32 S2主板上之后,根据外部跳线可以选择不同的 Mac 非常方便。
  if (digitalRead(ADDRESSPIN2) == HIGH) {
    esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress0[0]);
  } else {
    esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress1[0]);
  }
  if (DEBUGMODE) {
    Serial.print("[NEW] ESP32 Board MAC Address:  ");
    Serial.println(WiFi.macAddress());
  }

2.为了实现USB 手柄,我们使用USB 手柄相同的 Report 描述符,这样在操作系统端看起来插入的就是一个 HID 手柄。

    CustomHIDDevice(void) {
      static bool initialized = false;
      if (!initialized) {
        initialized = true;
        HID.addDevice(this, sizeof(report_descriptor));
      }
}

3.接收到的来自ESP NOW 的数据会出现在 OnDataRecv() 这个回调函数中,我们接收之后无需额外处理直接作为USB数据发送给主机即可。

// 创建一个回调函数,当 ESP32 通过 ESP-NOW 接收到数据时将被调用
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  if (DEBUGMODE) {
    // 收到的数字,例如:
    // 128 128 128    128 31 0
    // 发过来的数据直接就是 RAW 格式
    for (uint8_t i = 0; i < len; i++) {
      printf("%02x  ", incomingData[i]);
    }
    printf("\n");
  }

  if (HID.ready()) {
    Device.send((uint8_t *)&incomingData[0]);
  }
}

工作的视频在B站可以看到:

https://www.bilibili.com/video/BV168411r7r3/?share_source=copy_web&vd_source=5ca375392c3dd819bfc37d4672cb6d54

对应的 Arduino 代码在这里下载:

Windows 10/11 直接重启进入 Setup的方法

根据【参考1】,评论区有人提到,以管理员权限运行cmd窗口下输入如下命令即可立刻重启进入 Setup 界面:

shutdown /r /fw /t 0

此外还可以用下面的命令让系统进入 Boot Option 界面

shutdown /r /o /t 0

结合【参考2】编写直接申请权限然后执行操作的批处理

参考:

1.https://zhuanlan.zhihu.com/p/606258549

2.https://www.lab-z.com/batadm/

香港保险投诉局案例分析1

最近听说香港有一个“香港保险投诉局(Insurance Complaints Bureau)”,它一家成立于2018年1月16日,前身是香港保险投诉索偿局,是一个独立的、非营利性的保险纠纷调解机构。该机构旨在为保险消费者提供便捷、高效的纠纷解决服务,维护消费者权益,促进保险市场的公平和诚信。

也就是说当投保人申请理赔时遇到拒赔时,且认为理赔结果不合理时,无需先走司法程序,可直接向保险投诉局投诉,他们接到投诉后将以裁决的方式处理。【参考1】

好奇之下,去看看他们的案例【参考2】,第二页:

何谓「表面证据不成立」?

陈先生计划于2021年7月前往上海,并向保险公司投购旅游保险,而保险公司网页上闪烁的宣传字句「隔离现金津贴」吸引了他的注意。
根据中国对2019冠状病毒病疫情实施的检疫政策,陈先生于上海的一家酒店接受了 14 天的集中强制检疫,酒店费用接近12,000人民币。回港后,他向保险公司申索于隔离期间的酒店费用,但他的索偿遭保险公司拒绝。
有关保单的「强制隔离现金津贴」条款订明:「如受保人因疑似感染或确诊患上传染病而于旅程期间或于返回常住地方后七日内被强制隔离,保险公司将就每个完整天数支付现金津贴500港元……」
陈先生的个案被定为表面证据不成立,原因是:
1) 他被强制隔离的原因并非因为疑似感染或确诊患上传染病;及
2) 保险的原则仅就不可预见或预料的事件向个人或实体提供保障。由于内地当时的政策要求
所有到上海的入境旅客必须隔离至少 14 天,因此相关隔离而导致的酒店费用属预期和可
以预见,不符合保险的承保原则。

这就是为什么语文要做阅读理解吧?话说保险条款还真是阅读理解的好材料。

三十多年前,我老家(大庆市)流传着一个故事。本地人习惯将罐车称作“大罐”。

然后有“狡猾”的外地人过来和本地人签合同,内容是以几十万元的价格出售“大罐”给本地。交付的时候,真的是大铁罐,但是合同如此,最终官司打不赢白白让对方占了便宜。

参考:

1.https://baijiahao.baidu.com/s?id=1820155891286124609&wfr=spider&for=pc

2.https://www.icb.org.hk/gb/doc/ICB-Connect-Issue-1.pdf