无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站广告联系 微信:wuyouceo QQ:184822951
查看: 20772|回复: 97
打印 上一主题 下一主题

[求助] 求教(已解决,pecmd脚本方案)判断当前系统是否为 uefi

  [复制链接]
跳转到指定楼层
1#
发表于 2019-3-24 22:52:51 | 显示全部楼层 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 dos时代菜鸟 于 2021-6-18 12:30 编辑

问题已经解决,
感谢 liuzhaoyzz 、窄口牛 、adef 、liuzhaoyzz 等诸位大侠的热心回复和提供的思路。

我想弄个 cmd 实现这个功能,进而判断 系统启动类型是 uefi 还是 leagy-bios

鉴于 2021新版win10 已经不能使用以往  call dll函数 的方式,

针对新版win10 改进了一下,用了另一个 api

bootmode-2021.06.18.7z (1.82 MB, 下载次数: 97)

  1.     ENVI$# &ret=*4 0     //申请4个窄字符空间,也可以用   ENVI$ &ret=*2 0 //申请2个宽字符空间
  2.     CALL $--qd --bool --ret:&&r Kernel32.dll,GetFirmwareType,*&ret
  3.     FIND $%&&r%=,
  4.     {*
  5.         //为空,可以认为没有返回数据,当前环境不支持此API,低于Win8版本的系统
  6.         //MESS. 不支持此API。
  7.                 CALL $ --ret:r Kernel32.dll,GetFirmwareEnvironmentVariableW,"",{00000000-0000-0000-0000-000000000000},0,0
  8.                 CALL $ --ret:mode1 Kernel32.dll,GetLastError, ""
  9.                 IFEX $%mode1% = 1 ,envi BootMODE=BIOS! find $%mode1%=998 ,envi BootMODE=UEFI ! envi BootMODE=???1
  10.     }!  
  11.     {*
  12.         IFEX #%&&r%=0,MESS API Error.!  
  13.         {*
  14.             ENVI?int &ret=&ret1
  15.             IFEX #%&ret1%=0,envi BootMODE=???0。
  16.             IFEX #%&ret1%=1,envi BootMODE=BIOS
  17.             IFEX #%&ret1%=2,envi BootMODE=UEFI
  18.             IFEX #%&ret1%=3,envi BootMODE=???9
  19.         }
  20.     }
  21. mess %Bootmode%
复制代码




2021新版 pecmd 也有所改进,
改进后 的 如下:2021.01.14
bootmode.2021.2.7z (1.67 MB, 下载次数: 159)






PE下 用pecmd 脚本 bootmode.7z (1.82 MB, 下载次数: 258) 压缩包中包含 pecmd x86 /x64 所以在 本地 windows下也可以用。
有的  PE 因为 pecmd 版本不同 有的 call 指令略有不同,需要 自行对 bm.ini 进行修改。压缩包中的 pecmd 是 2018新版

脚本解决方案 是 调用 pecmd 加载 kernel32.dll 的 api 句柄
按照网上流行的做法 ,本质就是 通过 给 GetFirmwareEnvironmentVariableW 函数一个 错误的信息,其返回值必为0 ,但是其 错误代码返回值就不一样了。如果错误代码返回 1 说明是 bios ,代表 当前系统不支持 该函数运行,如果 错误代码返回值为 998 ,说明 该句柄运行了,但是运行错误。
值得一提的是,必须 将 kernel32.dll 的  GetFirmwareEnvironmentVariableW  和 getLastError 两个函数 紧连着使用,以便能够获取 真正的错误返回码。
所以 考虑 用 team 命令将两个 call 链接起来。
pecmd 脚本内容如下:
(根据自身 pecmd 版本 需要将下面源码中 红色部分修改  比如 2017版 pecmd 需要把 ** 变成 --)
--------------
  1. TEAM CALL $ --ret:mode1 Kernel32.dll,GetFirmwareEnvironmentVariableW, "",{00000000-0000-0000-0000-000000000000},0,0 | CALL $ --ret:mode1 Kernel32.dll,GetLastError, ""
  2. IFEX $%mode1% = 1 ,ENVI MODE1=BIOS! find $%mode1%=998 ,ENVI MODE1=UEFI ! ENVI MODE1=Unknow
  3. MESS %mode1%
复制代码
----------------------


exe 方案 用这个 detectefi.7z (22.24 KB, 下载次数: 142) 由 adef 提供,很好用。

如果是 win10 系统 powershell 3.0 的话 可以尝试用这个
bmx.7z (576 Bytes, 下载次数: 84)

如果你的系统 可以运行 bcdedit 列出 bcd 内容,可以用这个
B2.7z (896 Bytes, 下载次数: 104)




评分

参与人数 1无忧币 +5 收起 理由
chshrm + 5 很给力!

查看全部评分

2#
 楼主| 发表于 2019-3-24 23:34:14 | 显示全部楼层
通过 {current }可以半段 当前系统 对应的 bcd 选项。
回复

使用道具 举报

3#
 楼主| 发表于 2019-3-24 23:54:51 | 显示全部楼层

如果删除了 path ,bootmgr 如何知道用什么文件引导进入 windows ,这个技术我还没接触过。一定是 还修改了其他地方 以 与之对应。还请赐教。
回复

使用道具 举报

4#
 楼主| 发表于 2019-3-24 23:55:55 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-24 23:59 编辑
窄口牛 发表于 2019-3-24 23:39
bcd判断不了,还得用其它方法,有个第三方命令可以。


第三方命令 是 用  api dll 编的 那个 吧。或者用  au3



如果 存在 path 就可以通过 exe /efi 判断出
至于 pe 可这样:
  1. for /f "tokens=2* delims=  " %%A in ('reg query HKLM\System\CurrentControlSet\Control /v PEFirmwareType') DO SET Firmware=%%B
  2. :: Note: delims is a TAB followed by a space.
  3. if %Firmware%==0x1 echo The PC is booted in BIOS mode.
  4. if %Firmware%==0x2 echo The PC is booted in UEFI mode.
复制代码
回复

使用道具 举报

5#
 楼主| 发表于 2019-3-25 00:02:10 | 显示全部楼层

那可能就是 bootmgr  通过自己的 类型 加载 winload 的 类型了吧。这个就 有些难办了。
回复

使用道具 举报

6#
 楼主| 发表于 2019-3-25 09:36:16 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-25 10:20 编辑

刚才测试了下 win10 的 bcd  没有 path 的情况

leagy uefi  模式下 启动项不指定 "path = ....winload.exe"  都可以启动

值得一提的是 uefi 模式下 会在 {bootmgr} 项目中增加一个 path 并指定 bootmgrfw.efi 作为启动 bcd 的引导器,即便把 {bootmgr} 中的这个 path 删掉,系统也会在启动的时候 加上,运行 "bcdedit  /enum “也会自动加上.
不知道是否是个例,还是普遍现象,如果普遍如此,就可以据此判断系统是否为 uefi 模式启动了。

也就是如果 {bootmgr} 项中 有定义 path 且其指向一个 .efi 文件,就说明是 uefi 启动。否则就是 leagy-bios 模式启动。???
似乎也不妥,如果用 bcdedit /deletevalue 的方式删除 {bootmgr} 中的 path ,系统就不会 回填 path 值了。
而且 leagy-bios 模式 下,bcd 中{bootmgr} 的path 没啥用,如果 leagy-bios中 {bootmgr}的path值乱写,也会影响判断。






如下图,{bootmgr} 项目中 path 已经删除,运行 bcdedit 还是会 自动把 path 加上。重启也会。



  1. @echo off
  2. setlocal ENABLEDELAYEDEXPANSION
  3. set /a n=0
  4. set /a current_n=-1
  5. set /a bootmgr_n=-1
  6. for /f "tokens=1* delims= " %%a in ('bcdedit /enum ') do (
  7.   set "lx=%%a" &  if /i "!lx:~0,1!"=="-" set /a n+=1
  8.   set "!n!_%%a=%%b"
  9.   if /i "%%b"=="{current}" (set /a current_n=!n!)
  10.   if /i "%%b"=="{bootmgr}" (set /a bootmgr_n=!n!)
  11. )

  12. set "boot_mode=Legacy-BIOS"
  13. IF defined !bootmgr_n!_path (
  14.   for %%c in (!bootmgr_n!) do ( for %%q in (!%%c_path!) do (
  15.       if /i "%%~xq"==".efi" set "boot_mode=UEFI"
  16.   ))
  17. )

  18. ECHO.
  19. ECHO    BCD启动项列表(!boot_mode!):
  20. for /l %%n in (1,1,!n!) do (
  21.   set "item_type="
  22.   if %%n equ !current_n! ( set "item_type={Current}")
  23.   if %%n equ !bootmgr_n! ( set "item_type={Bootmgr}")
  24.   echo   ---%%n---!item_type!------------
  25.   for %%a in (description,device,systemroot,path) do (
  26.        set "lx=            %%a" & set "lx=!lx:~-12!"
  27.        if defined %%n_%%a  echo   !lx! : "!%%n_%%a!"
  28.   )
  29. )
  30. ECHO ---------------------------
  31. echo.

  32. pause
复制代码
回复

使用道具 举报

7#
 楼主| 发表于 2019-3-25 10:17:36 | 显示全部楼层
2012jiashanni 发表于 2019-3-25 09:44
很简单  判断启动文件是否是EFI  当然了  PE下无效

bcd 中记载的 path 项目 是可以删掉的,丝毫不影响系统启动。
回复

使用道具 举报

8#
 楼主| 发表于 2019-3-25 10:41:53 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-25 10:49 编辑
窄口牛 发表于 2019-3-25 10:23
一般人的电脑上,你这么判断多半不会错,但是鼓捣引导的人机器上就不行了。

是的,奇葩情况比较难搞
看看这个,在探测 bcd 前先删掉 {bootmgr} 中的 path ,如果 bcdedit 能够回填 ,那一定是 efi 的启动模式了。


  1. @echo off
  2. setlocal ENABLEDELAYEDEXPANSION
  3. set /a n=0
  4. set /a current_n=-1
  5. set /a bootmgr_n=-1
  6. bcdedit /deletevalue {bootmgr} path
  7. for /f "tokens=1* delims= " %%a in ('bcdedit /enum ') do (
  8.   set "lx=%%a" &  if /i "!lx:~0,1!"=="-" set /a n+=1
  9.   set "!n!_%%a=%%b"
  10.   if /i "%%b"=="{current}" (set /a current_n=!n!)
  11.   if /i "%%b"=="{bootmgr}" (set /a bootmgr_n=!n!)
  12. )

  13. set "boot_mode=-1"
  14. IF defined !current_n!_path (
  15.   for %%c in (!current_n!) do ( for %%q in (!%%c_path!) do (
  16.       if /i "%%~xq"==".efi" set "boot_mode=UEFI"
  17.       if /i "%%~xq"==".exe" set "boot_mode=Leagcy-Bios"
  18.   ))
  19. )
  20. if !boot_mode! equ -1 (IF defined !bootmgr_n!_path (
  21.   for %%c in (!bootmgr_n!) do ( for %%q in (!%%c_path!) do (
  22.       if /i "%%~xq"==".efi" set "boot_mode=UEFI"
  23.   ))
  24. ))
  25. ECHO.
  26. ECHO    BCD启动项列表(!boot_mode!):
  27. for /l %%n in (1,1,!n!) do (
  28.   set "item_type="
  29.   if %%n equ !current_n! ( set "item_type={Current}")
  30.   if %%n equ !bootmgr_n! ( set "item_type={Bootmgr}")
  31.   echo   ---%%n---!item_type!------------
  32.   for %%a in (description,device,systemroot,path) do (
  33.        set "lx=            %%a" & set "lx=!lx:~-12!"
  34.        if defined %%n_%%a  echo   !lx! : "!%%n_%%a!"
  35.   )
  36. )
  37. ECHO ---------------------------
  38. echo.

  39. pause
复制代码

回复

使用道具 举报

9#
 楼主| 发表于 2019-3-25 10:53:47 | 显示全部楼层
窄口牛 发表于 2019-3-25 10:47
那个第三方可以,我试了,因为我的bcd路径根本就不是常规的位置,它可以正确判断,但是我现在的精简版系统 ...

要是 bcd 位置变动 bcdedit 能读取出来么?
如果能,可以尝试 用 bcdedit /deletevalue 把 {bootmgr} 的 path 删掉,然后再 bcdedit /enum ,看 {bootmgr} 的 path 是什么,如果能回填出来,path 指向的扩展名是 efi 那肯定是 uefi 启动的。

我刚弄了个 cmd 好像可以了,{bootmgr}的内容,开始我是 用 /set 修改 path ,结果不回填,但是要是 path 被干掉了,bcdedit 就会 给再填一个。这个在 leagcy-bios 模式 不回填。
回复

使用道具 举报

10#
 楼主| 发表于 2019-3-25 11:52:29 | 显示全部楼层
窄口牛 发表于 2019-3-25 11:48
找不到,它找不到bcd的正确位置。

那就不好办了
回复

使用道具 举报

11#
 楼主| 发表于 2019-3-25 12:09:29 | 显示全部楼层
窄口牛 发表于 2019-3-25 10:47
那个第三方可以,我试了,因为我的bcd路径根本就不是常规的位置,它可以正确判断,但是我现在的精简版系统 ...

第三方软件 是什么?
回复

使用道具 举报

12#
 楼主| 发表于 2019-3-25 12:11:27 | 显示全部楼层

论坛好像有人问过,也有人用 api 编过程序。还有 au3 的
回复

使用道具 举报

13#
 楼主| 发表于 2019-3-25 12:32:48 | 显示全部楼层
窄口牛 发表于 2019-3-25 12:29
就应该就是楼上这位程大师的。

这个我也关注并下载了。
回复

使用道具 举报

14#
 楼主| 发表于 2019-3-25 12:34:26 | 显示全部楼层
窄口牛 发表于 2019-3-25 11:48
找不到,它找不到bcd的正确位置。

如果 新建一个 bcd ,看是否在  {bootmgr} 里有 path 呢?
回复

使用道具 举报

15#
 楼主| 发表于 2019-3-26 08:25:21 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-26 08:26 编辑

我是发现 bcdedit 会在 uefi 启动模式下 自动给 bcd 的 {bootmgr} 添加 path 值,而在 legacy-bios 模式 就不会,所以,我的思路是将 {bootmgr} 的path 内容保存后删掉,再用 bcdedit /v 看有没有 这个 path 来判断 是什么模式启动的,然后在把 之前保存的 path 恢复回 {bootmgr} 。

只要 bcdedit 能够 正常读取当前 bcd 就可以。但对于那些 bcd 变了位置变了名字的就不行了。
回复

使用道具 举报

16#
 楼主| 发表于 2019-3-26 09:29:29 | 显示全部楼层

好的,看来我要研究下了
回复

使用道具 举报

17#
 楼主| 发表于 2019-3-26 10:28:42 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-26 10:38 编辑

说到 api
刚测试 powershell 3.0 有一句 可以成功

  1. (Get-SecureBootuefi -name setupmode)>$nul
  2. $?
复制代码


还有 ,如果用 rundll32.exe 调用 kernel32.dll 中的 GetFirmwareEnvironmentVariableA 函数,句柄应该怎么写?

点评

基于powershell的方案是不可靠的,很多系统早已精简powershell。  详情 回复 发表于 2019-3-26 10:31
回复

使用道具 举报

18#
 楼主| 发表于 2019-3-26 10:39:07 | 显示全部楼层
liuzhaoyzz 发表于 2019-3-26 10:31
基于powershell的方案是不可靠的,很多系统早已精简powershell。

还有 ,如果用 rundll32.exe 调用 kernel32.dll 中的 GetFirmwareEnvironmentVariableA 函数,句柄应该怎么写?

点评

frg521是不是在楼上给出的有例子,我没有试过。  详情 回复 发表于 2019-3-26 10:57
回复

使用道具 举报

19#
 楼主| 发表于 2019-3-26 11:12:50 | 显示全部楼层
liuzhaoyzz 发表于 2019-3-26 10:57
frg521是不是在楼上给出的有例子,我没有试过。

rundll32 kernel32.dll,GetFirmwareEnvironmentVariableA "","{00000000-0000-0000-0000-000000000000}","",0
可以运行但无法取结果
回复

使用道具 举报

20#
 楼主| 发表于 2019-3-27 09:04:40 | 显示全部楼层
本课题 脚本解决方案 是 调用 pecmd 加载 kernel32.dll 的 api 句柄
pecmd 脚本内容如下:

  1. CALL $  **c **ret:* C:\windows\system32\Kernel32.dll,GetFirmwareEnvironmentVariableW, mode1,,{00000000-0000-0000-0000-000000000000},,4096
  2. CALL $ **ret:mode1 C:\windows\system32\Kernel32.dll,GetLastError
  3. IFEX $%mode1% = 1 ,ENVI MODE1=BIOS! find $%mode1%=998 ,ENVI MODE1=uefi ! ENVI MODE1=Unknow
  4. MESS %mode1%
复制代码
回复

使用道具 举报

21#
 楼主| 发表于 2019-3-27 09:51:33 | 显示全部楼层

主要是 编程序需要编译,所以 第一考虑 用 脚本。

回复

使用道具 举报

22#
 楼主| 发表于 2019-3-27 11:18:20 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2019-3-27 11:44 编辑

我这个 win10 ltsc 2019 是通过 legacy 方式安装的,然后 主板支持 uefi 我就 在另一个 gpt 硬盘上弄了个 引导他的 bcd ,用来通过 uefi 引导这个 win10 ,就是为了 研究两种启动方式,这样通过  uefi 启动进入 ltsc2019 确实 注册表中没有 HKLM\HARDWARE\UEFI 这个分支。


另外
detectefi.exe 这个程序 小巧实用,确实不错


我觉得吧,
如果是 pe环境 用一个 pecmd 脚本 应该是最好的,可以嵌入其它 pecmd 脚本
对于 win 10 系统下,用 powershell 一句话就能搞定,win7 就 不知道了。

回复

使用道具 举报

23#
 楼主| 发表于 2019-3-27 13:51:10 | 显示全部楼层
把几个 方案 都放到 1楼 了,pecmd 脚本方案 也做了些 改进。
回复

使用道具 举报

24#
 楼主| 发表于 2019-3-31 23:26:00 来自手机 | 显示全部楼层
frg521 发表于 2019-3-30 23:36
...

收下,谢谢
回复

使用道具 举报

25#
 楼主| 发表于 2019-4-7 15:19:53 | 显示全部楼层
dsxmg1990 发表于 2019-4-5 15:52
可以通过diskpart 检测有没有esp分区来测试嘛

不准确,uefi 模式下也可以通过 mbr 方式启动,调用 mbr 硬盘上 Fat32 分区上的 efi ,
所谓的 3 启 u 盘 就是这个原理。
回复

使用道具 举报

26#
 楼主| 发表于 2020-5-7 09:47:23 | 显示全部楼层
Anson4 发表于 2020-3-19 14:27
1楼的PECMD代码已经不能适应 Windows 10.0.0.19041,无论是UEFI环境还是LEGACY环境,检测出来的结果都是“1 ...

特意 弄个 1909 的 win10 测试了下,没问题。
有图有真相。


点评

1909的版本号是Windows 10.0.0.18363,2004的版本号才是Windows 10.0.0.19041,其正式版即将到来。  详情 回复 发表于 2020-5-7 22:51
回复

使用道具 举报

27#
 楼主| 发表于 2020-5-8 07:01:52 | 显示全部楼层
本帖最后由 dos时代菜鸟 于 2020-5-8 07:08 编辑
Anson4 发表于 2020-5-7 22:51
1909的版本号是Windows 10.0.0.18363,2004的版本号才是Windows 10.0.0.19041,其正式版即将到来。

好吧,好吧,看来 还要等正式的出来了,才能再确定下。
还要看看,那几个 exe 方案能否有效。另外 1314 码 对应的应该是 权限问题,你不妨 用管理员权限 运行 再试试。

点评

已经用了管理员权限了  详情 回复 发表于 2020-5-8 12:29
回复

使用道具 举报

28#
 楼主| 发表于 2020-5-9 21:18:15 | 显示全部楼层
Anson4 发表于 2020-5-8 12:29
已经用了管理员权限了

pe 下呢?

点评

手上没有2004的PE 不过,我觉得不是权限的问题,因为同样的操作,在1909以及更低版本的系统下运行正常。  详情 回复 发表于 2020-5-10 08:27
回复

使用道具 举报

29#
 楼主| 发表于 2020-7-31 15:20:12 | 显示全部楼层
Anson4 发表于 2020-7-30 20:40
请问楼主,在Win10 2004(19041)下,PECMD脚本返回的结果不正确,这个问题能解决了吗?

没用过 那么先进的 系统呀,我这都是老古董。
也不知道 2004 中 dll 调用 句柄是啥。
回复

使用道具 举报

30#
 楼主| 发表于 2020-7-31 16:24:23 | 显示全部楼层
Anson4 发表于 2020-7-31 16:04
我的移动CPU一代i5都在用2004,还有啥机器用不了呢?

只能说你还是年轻呀。

我用的是 赛扬 G550 @ 2.60GHz .安装个系统 很费劲,下载个系统也很费劲。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2024-6-5 02:57

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表