windows无盘启动技术开发之传统BIOS(Legacy BIOS)引导程序开发之二

news/2024/5/12 0:23:47/文章来源:https://blog.csdn.net/fanxiushu/article/details/129700446

by fanxiushu 2023-03-21 转载或引用请注明原始作者,

接上文,

这篇文章其实主要就是讲述上文中 Int13HookEntry 这个C函数的实现过程,

看起来就一个函数,可实现起来一点也不轻松。

首先得准备编译环境,因为是16位程序,所以得去找以前那种比较老的编译器,比如turboC,

当然,如果在linux下找到合适的16位编译环境,也是可以的。

传统BIOS的引导程序并不是windows的一部分,它可以在其他任何能支持16位交叉编译的操作系统上编译。

从上文的asm汇编代码片段,在调用 Int13HookEntry 函数之前,压栈了一堆的寄存器,ax,bx。。。cs,ds等等。

而在C代码中,总归是需要换算成对应的结构体,

如下,就是上文汇编代码压栈对应的结构体描述:

///INT13 的入口参数

struct int13_entry

{

/// AX,BX,CX,DX register

union{

struct {

UINT8 al;

UINT8 ah;

};

UINT16 ax;

};

union{

struct {

UINT8 bl;

UINT8 bh;

};

UINT16 bx;

};

union{

struct {

UINT8 cl;

UINT8 ch;

};

UINT16 cx;

};

union{

struct {

UINT8 dl;

UINT8 dh;

};

UINT16 dx;

};

///SI,DI, CS, DS,ES SS

UINT16 si;

UINT16 di;

//

UINT16 cs;

UINT16 ds;

UINT16 es;

UINT16 ss;

///

UINT16 flags;//标志寄存器

};

看起来似乎有点多,其实就是各种寄存器,还包括段寄存器。

而C函数的声明对应如下所示:

extern "C" void Int13HookEntry( int13_entry far* reg );

是 far 指针,也就是UINT32来描述地址,高16位是段地址,低16位是偏移地址。

在讲述 Int13HookEntry之前,我们先来看看另外一个在上文中出现的C函数x7c00_Init,

它其实就是初始化 PXE,为 Int13HookEntry 函数进行网络通信建立基础。

如何初始化PXE?具体的代码细节,可以去参阅 ipxe等开源代码的关于PXE接口调用的过程。

这里只是抛砖引玉的简单阐述。

其实PXE提供的也是一组接口函数调用,我们只要获取到了对应接口函数地址,调用它们,就可以实现各种功能。

因此这里简述从获取接口地址,到封装成一个通用的C函数接口的过程。

如下C代码:

static BOOL find_pxe_api_entry( HPXE far* pxe)

{

UINT32 pxe_env = 0;

///

__asm{

mov ax,5650h

int 1Ah

cmp ax, 564Eh

jnz exit

mov word ptr [pxe_env + 2], es

mov word ptr [pxe_env], bx

exit:

}

if( !pxe_env ){

return FALSE;

}

t_PXENV far* env = (t_PXENV far*)pxe_env;

t_PXE far* pxe_e = (t_PXE far*)env->PXEPtr;

pxe->pxe_env = env;

pxe->pxe_pxe = pxe_e;

///

if( env->Version < 0x0201 ){

pxe->api_entry = env->RMEntry;

}else{

pxe->api_entry = pxe_e->EntryPointSP;

}

return TRUE;

}

其中HPXE结构是方便我这边封装定义的结构体

类似:

struct HPXE

{

UINT32 api_entry; //

UINT8 is_localinfo;

UINT8 is_udpopen;

t_PXENV far* pxe_env;

t_PXE far* pxe_pxe;

UINT16 irq; //中断号

UINT32 old_isr;

volatile UINT8 isr_trigger; //中断ISR用来判断是否发生了中断

int isr_processing; ///正在处理的 ISR, Currently processing ISR

UINT32 local_ip;

UINT16 local_port; 2023-01-31

UINT8 local_mac[6]; ///

///

UINT32 r_ip;

UINT8 r_mac[6];

///

};

而t_PXENV和t_PXE结构的定义则是PXE规范提供的,我记得这个结构体的来源好像是从ipxe代码中复制过来的,

/** The PXENV+ structure */

typedef struct s_PXENV {

UINT8 Signature[6]; // 'PXENV+'

UINT16 Version; // 0x0201

UINT8 Length;

UINT8 Checksum;

UINT32 RMEntry; /**< Real-mode PXENV+ entry point */

UINT32 PMOffset; //Protected-mode PXENV+ entry point segment selector

SEGSEL PMSelector;

SEGSEL StackSeg; /**< Stack segment selector */

UINT16 StackSize; /**< Stack segment size */

SEGSEL BC_CodeSeg; /**< Base-code code segment selector */

UINT16 BC_CodeSize; /**< Base-code code segment size */

SEGSEL BC_DataSeg; /**< Base-code data segment selector */

UINT16 BC_DataSize; /**< Base-code data segment size */

SEGSEL UNDIDataSeg; /**< UNDI data segment selector */

UINT16 UNDIDataSize; /**< UNDI data segment size */

SEGSEL UNDICodeSeg; /**< UNDI code segment selector */

UINT16 UNDICodeSize; /**< UNDI code segment size */

///

UINT32 PXEPtr;

} t_PXENV;

/** The !PXE structure */

typedef struct s_PXE {

UINT8 Signature[4]; // '!PXE'

UINT8 StructLength; /**< Length of this structure */

UINT8 StructCksum;

UINT8 StructRev;

UINT8 reserved_1; /**< Must be zero */

SEGOFF16 UNDIROMID;

SEGOFF16 BaseROMID; //

UINT32 EntryPointSP; //本来是 SEGOFF16改为 UINT32

SEGOFF16 EntryPointESP;

SEGOFF16 StatusCallout;

UINT8 reserved_2; /**< Must be zero */

UINT8 SegDescCnt;

SEGSEL FirstSelector;

SEGDESC Stack;/** Stack segment descriptor */

SEGDESC UNDIData;/** UNDI data segment descriptor */

SEGDESC UNDICode;/** UNDI code segment descriptor */

SEGDESC UNDICodeWrite;/** UNDI writable code segment descriptor */

SEGDESC BC_Data;/** Base-code data segment descriptor */

SEGDESC BC_Code;/** Base-code code segment descriptor */

SEGDESC BC_CodeWrite;/** Base-code writable code segment descriptor */

} t_PXE;

上面的find_pxe_api_entry函数找到了 接口地址 api_entry,而现在再把这个接口地址封装成C函数。

unsigned int pxe_call_api( HPXE far* pxe, WORD op_code, void far* param)

{

UINT16 rr = PXENV_STATUS_FAILURE ; ///

if( !pxe->api_entry ){

//查找入口地址

find_pxe_api_entry( pxe );

///

if( !pxe->api_entry )

return rr;

}

UINT32 pxe_api_entry = pxe->api_entry;

//以下调用兼容 老的和新的PXE API

__asm{

mov es, word ptr [param+2]

mov di, word ptr [param]

mov bx, op_code

push es

push di

push bx

call dword ptr [pxe_api_entry]

add sp,6

mov rr, ax

}

return rr;

}

这样,这个 pxe_call_api 函数,几乎在我的代码中承担了绝大部分跟PXE相关通信的调用。

而上面参数中op_code则对应着各种操作类型,有初始化的,也有ARP,以及原始链路层数据发送和接收的操作。

对应每个不同的op_code,param也对应着各种不同的结构体,这些都必须符合PXE规范。

而这些op_code操作码,以及param代表的结构体的具体函数,可以自己进一步去查询PXE规范手册,或者查询 各类PXE相关源代码。

在x7c00_Init初始化函数中,我们调用pxe_call_api函数,包括的操作码

PXENV_UNDI_STARTUP(0x01)

PXENV_UNDI_INITIALIZE(0x03)

PXENV_GET_CACHED_INFO(0x71),

因为我们需要获取PXE加载引导程序之前获取的的DHCP信息,

以方便 Int13HookEntry 进一步进行网络通信。

以及其他一些我们自己的初始化操作。

至此x7c00_Init的任务基本完成,接下来就是 Int13HookEntry 函数了。

Int13HookEntry 需要与服务器端通信,需要获取服务器端的磁盘镜像数据,因此我们得开发自己的服务端程序。

当然如果你想借用一些公共协议的磁盘镜像服务器端也行。

这里是我们自己开发的服务器端,实际上服务端也把DHCP,TFTP等也集成到一块了。

这里清一色使用UDP协议进行传输,

因为如果使用其他除开UDP,TCP之外的协议,服务端处理起来也特麻烦,

比如如果使用AOE协议(直接基于链路层的磁盘传输协议),windows服务端还得开发协议驱动来收发磁盘数据,

如果使用TCP,服务端倒没啥问题,而到了 PXE一端就挺痛苦了,因为没有现成的TCP,那意味着还得自己实现。

所以最终权衡之后,还是得使用UDP传输。

其实PXE提供的也没有UDP协议,但是因为UDP协议格式简单,组装UDP报文也不是什么难事。

MAC头+IP头+UDP头+数据内容,这就是UDP报文,而且每个报文都是独立的,不像TCP那样有非常复杂的超时重传等机制。

因此,我们可以使用上面的pxe_call_api函数再次封装UDP的收发函数,

当然,进行数据传输前,需要打开PXE设备,同样是调用 pxe_call_api 函数:

PXENV_UNDI_SET_STATION_ADDRESS(0xA),设置网卡MAC地址,

PXENV_UNDI_OPEN(0x06), 打开设备。

UDP发送函数,操作码是 PXENV_UNDI_TRANSMIT(0x08),发送的是包括MAC头在内的链路层数据,因此需要在发送前进行组包处理。

UDP接收函数,操作码PXENV_UNDI_ISR(0x14),采用轮询方式接收,其实就是设置死循环查询,当然需要设置一个超时时间,

收到UDP数据就返回,当然得去掉各种头信息。

UDP的收发函数,还必须同时处理ARP报文,因为还得管理MAC-IP的关联关系。

同时因为MTU的原因,以太网卡MTU一般都是1500,所以实际上每个UDP报文携带的数据不得超过1472,

如果要达到UDP报文的极限大小(64K),得进行IP分片,

所以还不如自己在应用层,每个UDP包大小限制小于1472,同时进行分包处理。

这里只是大致描述了一下UDP收发函数的处理过程,

实际编程过程,可能没有想象的那么容易,同时还是得在16位环境下。

如果有兴趣,可以自行去实现相关功能。

在使用pxe_call_api 基础函数,进一步封装实现UDP收发函数,比如 udp_recv和udp_send 函数之后,

开始真正实现 Int13HookEntry 函数了。

从PXE的PXENV_GET_CACHED_INFO命令中,我们获取了DHCP信息,

其实就是本地网卡的MCA地址和分配到的一个本地IP地址。

也就是获取到了本地网卡的地址,但是我们还无法知道磁盘服务端的地址,

以及一些磁盘相关的信息,比如磁盘大小,扇区大小,磁盘编号等信息。

因此还得像DHCP广播那样,在局域网内使用UDP协议,广播一条查询磁盘服务端的报文。

我们的磁盘服务端收到这条广播报文之后,开始回复相关信息。

在PXE客户端,接收到这条信息之后,就已经正确获取到了磁盘相关信息,

这样接下来自然就可以和服务端正常通信了。

因此,Int13HookEntry函数的实现看起来大致如下:

void Int13HookEntry( int13_entry far* reg )

{

while ( !netdisk_init() ) { //死循环来获取磁盘服务端信息,如果已经获取了,直接返回TRUE

;

}

//

int status = int13_hook( reg );

if( status == INT13_STATUS_SUCCESS || status == INT13_STATUS_ALL_OK ) { //success

reg->flags &= ~1; //clear CF

///

if( status == INT13_STATUS_SUCCESS )

reg->ah = status;

///

}

else{

reg->flags |= 1 ; //set CF

reg->ah = status;

}

}

netdisk_init 函数就是上面讲的,通过UDP广播方式,获取磁盘服务端各种信息,

这里还补充一点,当正确获取服务端地址以及磁盘等信息之后,

还需要告诉BIOS,我们增加了一块磁盘,告诉BIOS可以正常使用这块磁盘了。

大致如下:

修改BIOS 数据区, 增加硬盘个数

UINT32 bios_addr = 0x00400075;

UINT8 disk_num = *((char far*)bios_addr);

disk_num ++;

*((char far*)bios_addr) = disk_num;

再接下来,就是 Int13HookEntry函数里边的int13_hook函数调用。

这个函数其实就是响应BIOS的关于磁盘的各种调用,读或写磁盘扇区数据,

这就是关于BIOS的13H中断的具体细节了。比如

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

这个连接比较详细的描述了INT13H中断的各种命令(国内可能无法直接访问上面的连接)。

13H中断的AH寄存器,也就是对应着 int13_entry结构体中的ah变量,

存放着磁盘各种命令,大致如下一些常用的命令:

#define INT13_RESET 0x00 // Reset disk system

#define INT13_GET_LAST_STATUS 0x01 /// Get status of last operation

#define INT13_READ_SECTORS 0x02 /// Read sectors

#define INT13_WRITE_SECTORS 0x03 /// Write sectors

#define INT13_VERIFY_SECTORS 0x04 /// 验证扇区

#define INT13_GET_PARAMETERS 0x08 /// Get drive parameters

#define INT13_GET_DISK_TYPE 0x15 /// Get disk type

#define INT13_EXTENSION_CHECK 0x41

#define INT13_EXTENDED_VERIFY 0x44 // verify sectors

#define INT13_EXTENSION_PARAM 0x48

#define INT13_EXTENDED_READ 0x42

#define INT13_EXTENDED_WRITE 0x43

其中 INT13_READ_SECTORS和INT13_WRITE_SECTORS, 以及

INT13_EXTENDED_READ和INT13_EXTENDED_WRITE

就是读和写扇区命令,

这个命令需要通过 UDP协议发送到服务端,完成真正的磁盘扇区数据的读写。

至于如何实现磁盘扇区数据传输过程,则是八仙过海,各显神通了。

需要保证尽可能快的完成数据通信,

而且在上文也提到过,windows7在实模式会传输70MB多的数据,而win10则传输80-90MB多的数据。

因此,在千兆网络环境下,起码得保证每秒 20-30MB的传输速率,(MB是兆字节,而不是兆位)

才能保证在 4-5秒内完成实模式下的数据传输过程。

下面是我开发的传统BIOS下引导程序 +

windows虚拟磁盘驱动一起形成的无盘启动程序的gif演示图。

在vmware虚拟机环境中的 win7的无盘启动过程,速度其实也挺快的,启动过程基本没超过30秒。

当然,换成win10,速度就有点勉强了,启动过程大概需要1分钟多些,

大概跟电脑配置,vmware虚拟机,以及win10更加臃肿有些关系,

毕竟没有在真实机器上试过,真机上应该会更快。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_274455.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode岛屿问题通用解决模板

文章目录前言第一题&#xff1a;求岛屿的周长模板整理遍历方向确定边界重复遍历问题处理模板解第一题第二题&#xff1a;求岛屿数量第三题&#xff1a;岛屿的最大面积第四题&#xff1a;统计子岛屿第五题&#xff1a;统计封闭岛屿的数目第六题&#xff1a;最大人工岛总结前言 …

04.hadoopHDFS

win java访问hadoop //复制文件夹,配置环境变量//配置HADOOP_HOME为我们的路径 ,hadoop-3.3.0 ,记得JAVA_HOME不要带有空格,!!!默认java安装环境有空格C:\Program Files//要在cmd hadoop -fs 查看是否配置成功//%HADOOP_HOME%\bin到path//maven添加依赖hadoop3.1.0//创建目录Be…

常见的CMS后台getshell姿势总结

目录 WordPress dedecms aspcms 南方数据企业系统 phpmyadmin日志 pageadmin 无忧企业系统 WordPress 默认后台登录地址 /wp-login.php /wp-admin 登录后在外观的编辑里面找一个模板&#xff0c;我们在404模板 (404.php)里面写入一句话后门 可以蚁剑连接 上传一个压缩…

自定义类型的超详细讲解ᵎᵎ了解结构体和位段这一篇文章就够了ᵎ

目录 1.结构体的声明 1.1基础知识 1.2结构体的声明 1.3结构体的特殊声明 1.4结构体的自引用 1.5结构体变量的定义和初始化 1.6结构体内存对齐 那对齐这么浪费空间&#xff0c;为什么要对齐 1.7修改默认对齐数 1.8结构体传参 2.位段 2.1什么是位段 2.2位段的内存分配…

web前端框架——Vue的特性

目录 前言&#xff1a; 一.vue 二.特性 1.轻量级 2.数据绑定 3.指令 4.插件 三.比较Angular 、React 、Vue 框架之间的比较 1. Angular Angular的优点&#xff1a; 2. React React 的优点&#xff1a; 3.vue 3.Vue的优点&#xff1a; 前言&#xff1a; 本篇文章…

QT开发笔记(多媒体)

多媒体 多媒体&#xff08;Multimedia&#xff09;是多种媒体的综合&#xff0c;一般包括文本&#xff0c;声音和图像等多种媒体形式。 在计算机系统中&#xff0c;多媒体指组合两种或两种以上媒体的一种人机交互式信息交流和传播媒体。 使用的媒体包括文字、图片、照片、声音…

头歌c语言实训项目-函数(2)

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 第1关&#xff1a;模拟投掷骰子游戏: 题目&#xff1a; 代码思路&#xff1a; 代码表示&#xff1a; 第…

20230322英语学习

Why Are So Many Gen Z-Ers Drawn to Old Digital Cameras? 老式数码相机&#xff1a;Z世代的复古潮流 The latest digital cameras boast ever-higher resolutions, better performance in low light, smart focusing and shake reduction – and they’re built right into …

牛客C/C++刷题笔记(五)

122、对于"int *pa[5]&#xff1b;"的描述中&#xff0c;&#xff08; &#xff09;是正确的。 123、以下叙述中正确的是&#xff08;&#xff09; C语言的源程序中对缩进没有要求,所以A选项错误。C语言中区分大小写,main函数不能写成Main或_main,所以B选项错误。一…

声声不息,新“声”报到

魅力声音大家庭总群&#xff08;10&#xff09;大玲&#xff0c;刚见到新来的四川孩儿——樱桃&#xff0c;真是太可爱了&#xff01;可不就是&#xff0c;这孩儿真是招人稀罕&#xff0c;我现在就把她拉到咱大群里“大玲” 邀请 “樱桃” 加入群聊所有人 咱们大家庭迎来了第一…

【字符串】刷题

P4173残缺的字符串心得&#xff1a;这道题&#xff0c;我觉得是不难的&#xff0c;代码逻辑很清晰&#xff0c;但是提交就是有问题最后发现两个问题&#xff1a;scanf输入字符后要用getchar() 吞回车 !!!!&#xff08;天坑用 scanf 输入时&#xff0c;不管输入什么&#xff0c;…

C语言小程序:通讯录(静态版)

哈喽各位老铁们&#xff0c;今天给大家带来一期通讯录的静态版本的实现&#xff0c;何为静态版本后面会做解释&#xff0c;话不多说&#xff0c;直接开始&#xff01;关于通讯录&#xff0c;其实也就是类似于我们手机上的通讯录一样&#xff0c;有着各种各样的功能&#xff0c;…

30天从零到1创业螺旋式

趁梦想还在&#xff0c;想去的地方&#xff0c;现在就去&#xff1b;想做的事情&#xff0c;现在就做。一开始立刻启动的你的项目&#xff0c;安排好时间计划&#xff0c;拆分微模块&#xff0c;每天花20分钟去完善产品&#xff0c;去改变世界。可以为自己的创意设置临时办公室…

RK3588平台开发系列讲解(显示篇)DP显示调试方法

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、查看 connector 状态二、强制使能/禁⽤ DP三、DPCP 读写四、Type-C 接口 Debug五、查看 DP 寄存器六、查看 VOP 状态七、查看当前显示时钟八、调整 DRM log 等级沉淀、分享、成长,让自己和他人都能有所收获!😄…

【数据结构】链队列的C语言实现

队列 1.队列的概念 队列 和栈一样&#xff0c;是一个 特殊的线性表。 队列只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。进行 插入操作 的一端称为 队尾&#xff0c;进行 删除操作 的一端称为队头。 队列中的元素遵守 先进先出(First I…

【类的继承与派生的知识点】

文章目录类的继承与派生的知识点类的继承与派生&#xff1a;类成员的访问&#xff1a;类型兼容规则&#xff1a;一个公有派生类的对象在使用上可以被当成基类的对象&#xff0c;反之不行单继承与多继承派生类的构造与析构类成员的标识与访问类的继承与派生的实验结果类型兼容规…

Baumer工业相机堡盟相机如何使用Sharpening图像锐化功能( Sharpening图像锐化功能的优点和行业应用)(C++)

项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能&#xff0c;可以实时传输高分辨率图像。此外&#xff0c;该相机还具…

【计算机网络】如何解决TCP粘包问题?

【计算机网络】如何解决TCP粘包问题&#xff1f; 文章目录【计算机网络】如何解决TCP粘包问题&#xff1f;如何理解字节流&#xff1f;如何解决粘包&#xff1f;固定长度的消息特殊字符作为边界自定义消息结构如何理解字节流&#xff1f; 之所以会说 TCP 是面向字节流的协议&a…

RK3588编译环境Ubuntu20.04编译配置-增加交换内存

迅为提供的编译环境 Ubuntu20.04 默认配置了交换内存是 9G&#xff0c;如果在编译过程中&#xff0c;因内 存不够而编译报错&#xff0c;可以参考本小节进行设置。 这里举例分配 5G 交换内存。 在开始之前&#xff0c;使用命令检查一下您的 ubuntu 的 swap 分区。 sudo swa…

Android进阶面经,面试10余家经验分享,拿到offer真不难~

前言 我们都知道面试大厂主要就是考察程序员技术方向的专业技能&#xff0c;Java开发主要考察的就是Java方面的专业技能&#xff0c;而Android岗位的 专业技能 就是Android程序员面试的重要考察方向。 大厂的招聘条件是明牌的&#xff0c;但技术这一块却难倒了大部分的人。 面…