zVirtualBattery

This software can simulate a battery under Windows 10 x64. It will install WDTF (Windows Device Testing Framework) to your system. After that you can switch DC/AC or set the battery percent in your system.

zvb

download : https://pan.baidu.com/s/1jHAzoXK
password: jz93

Usage:

1.This utility only works in Windows 10 X64. And it requires administrator privileges for running.

bat2

2.If you has’t installed WDTF, zVirtualBattery will install one for you.
bat3

bat4

3.After WDTF installation, you’d better restart your system

bat5

4. You can switch between virtual and real Battery

bat6

Switch between AC and DC mode

bata

bat7

bat9

bat8

5.You can set the virtual battery percent by track bar and “set” button.

batb

========================================================

2024/03/12

I have tried it in Windows 11. It works well.

Step to UEFI (130)NT32 模拟器中的 Debug Message 输出

一年多以前,提出了个奇怪的想法:是否可以在自己编写的Application中输出到 NT32 的模拟器LOG中?当时遇到的问题是,如果想直接输出必须调用 WinNtThunkDxe 这样的Protocol,而在定义Protocol的时候必须使用Windows.h 的头文件,但是 AppPkg 中无法做到这一点,最终找到的解决方法是修改gST->Reset 。但是很显然,这并非完美的解决方法【参考1】。

最近,忽然想起来,可以重新定义一个自己的 Protocol 头,只要需要用到的函数偏移正确,调用时放入正确的参数同样能正常工作。需要用到的最重要的 Protocol 结构体在  \UDK2017\Nt32Pkg\Include\Protocol\WinNtThunk.h :

typedef struct {
  UINT64                              Signature;

  //
  // Win32 Process APIs
  //
  WinNtGetProcAddress                 GetProcAddress;
  WinNtGetTickCount                   GetTickCount;
  WinNtLoadLibraryEx                  LoadLibraryEx;
  WinNtFreeLibrary                    FreeLibrary;

  WinNtSetPriorityClass               SetPriorityClass;
  WinNtSetThreadPriority              SetThreadPriority;
  WinNtSleep                          Sleep;

  WinNtSuspendThread                  SuspendThread;
  WinNtGetCurrentThread               GetCurrentThread;
  WinNtGetCurrentThreadId             GetCurrentThreadId;
  WinNtGetCurrentProcess              GetCurrentProcess;
  WinNtCreateThread                   CreateThread;
  WinNtTerminateThread                TerminateThread;
  WinNtSendMessage                    SendMessage;
  WinNtExitThread                     ExitThread;
  WinNtResumeThread                   ResumeThread;
  WinNtDuplicateHandle                DuplicateHandle;

  //
  // Wint32 Mutex primitive
  //
  WinNtInitializeCriticalSection      InitializeCriticalSection;
  WinNtEnterCriticalSection           EnterCriticalSection;
  WinNtLeaveCriticalSection           LeaveCriticalSection;
  WinNtDeleteCriticalSection          DeleteCriticalSection;
  WinNtTlsAlloc                       TlsAlloc;
  WinNtTlsFree                        TlsFree;
  WinNtTlsSetValue                    TlsSetValue;
  WinNtTlsGetValue                    TlsGetValue;
  WinNtCreateSemaphore                CreateSemaphore;
  WinNtWaitForSingleObject            WaitForSingleObject;
  WinNtReleaseSemaphore               ReleaseSemaphore;

  //
  // Win32 Console APIs
  //
  WinNtCreateConsoleScreenBuffer      CreateConsoleScreenBuffer;
  WinNtFillConsoleOutputAttribute     FillConsoleOutputAttribute;
  WinNtFillConsoleOutputCharacter     FillConsoleOutputCharacter;
  WinNtGetConsoleCursorInfo           GetConsoleCursorInfo;
  WinNtGetNumberOfConsoleInputEvents  GetNumberOfConsoleInputEvents;
  WinNtPeekConsoleInput               PeekConsoleInput;
  WinNtScrollConsoleScreenBuffer      ScrollConsoleScreenBuffer;
  WinNtReadConsoleInput               ReadConsoleInput;

  WinNtSetConsoleActiveScreenBuffer   SetConsoleActiveScreenBuffer;
  WinNtSetConsoleCursorInfo           SetConsoleCursorInfo;
  WinNtSetConsoleCursorPosition       SetConsoleCursorPosition;
  WinNtSetConsoleScreenBufferSize     SetConsoleScreenBufferSize;
  WinNtSetConsoleTitleW               SetConsoleTitleW;
  WinNtWriteConsoleInput              WriteConsoleInput;
  WinNtWriteConsoleOutput             WriteConsoleOutput;

  //
  // Win32 File APIs
  //
  WinNtCreateFile                     CreateFile;
  WinNtDeviceIoControl                DeviceIoControl;
  WinNtCreateDirectory                CreateDirectory;
  WinNtRemoveDirectory                RemoveDirectory;
  WinNtGetFileAttributes              GetFileAttributes;
  WinNtSetFileAttributes              SetFileAttributes;
  WinNtCreateFileMapping              CreateFileMapping;
  WinNtCloseHandle                    CloseHandle;
  WinNtDeleteFile                     DeleteFile;
  WinNtFindFirstFile                  FindFirstFile;
  WinNtFindNextFile                   FindNextFile;
  WinNtFindClose                      FindClose;
  WinNtFlushFileBuffers               FlushFileBuffers;
  WinNtGetEnvironmentVariable         GetEnvironmentVariable;
  WinNtGetLastError                   GetLastError;
  WinNtSetErrorMode                   SetErrorMode;
  WinNtGetStdHandle                   GetStdHandle;
  WinNtMapViewOfFileEx                MapViewOfFileEx;
  WinNtReadFile                       ReadFile;
  WinNtSetEndOfFile                   SetEndOfFile;
  WinNtSetFilePointer                 SetFilePointer;
  WinNtWriteFile                      WriteFile;
  WinNtGetFileInformationByHandle     GetFileInformationByHandle;
  WinNtGetDiskFreeSpace               GetDiskFreeSpace;
  WinNtGetDiskFreeSpaceEx             GetDiskFreeSpaceEx;
  WinNtMoveFile                       MoveFile;
  WinNtSetFileTime                    SetFileTime;
  WinNtSystemTimeToFileTime           SystemTimeToFileTime;

  //
  // Win32 Time APIs
  //
  WinNtLocalFileTimeToFileTime        LocalFileTimeToFileTime;
  WinNtFileTimeToLocalFileTime        FileTimeToLocalFileTime;
  WinNtFileTimeToSystemTime           FileTimeToSystemTime;
  WinNtGetSystemTime                  GetSystemTime;
  WinNtSetSystemTime                  SetSystemTime;
  WinNtGetLocalTime                   GetLocalTime;
  WinNtSetLocalTime                   SetLocalTime;
  WinNtGetTimeZoneInformation         GetTimeZoneInformation;
  WinNtSetTimeZoneInformation         SetTimeZoneInformation;
  WinNttimeSetEvent                   timeSetEvent;
  WinNttimeKillEvent                  timeKillEvent;

  //
  // Win32 Serial APIs
  //
  WinNtClearCommError                 ClearCommError;
  WinNtEscapeCommFunction             EscapeCommFunction;
  WinNtGetCommModemStatus             GetCommModemStatus;
  WinNtGetCommState                   GetCommState;
  WinNtSetCommState                   SetCommState;
  WinNtPurgeComm                      PurgeComm;
  WinNtSetCommTimeouts                SetCommTimeouts;

  WinNtExitProcess                    ExitProcess;

  WinNtSprintf                        SPrintf;

  WinNtGetDesktopWindow               GetDesktopWindow;
  WinNtGetForegroundWindow            GetForegroundWindow;
  WinNtCreateWindowEx                 CreateWindowEx;
  WinNtShowWindow                     ShowWindow;
  WinNtUpdateWindow                   UpdateWindow;
  WinNtDestroyWindow                  DestroyWindow;
  WinNtInvalidateRect                 InvalidateRect;
  WinNtGetWindowDC                    GetWindowDC;
  WinNtGetClientRect                  GetClientRect;
  WinNtAdjustWindowRect               AdjustWindowRect;
  WinNtSetDIBitsToDevice              SetDIBitsToDevice;
  WinNtBitBlt                         BitBlt;
  WinNtGetDC                          GetDC;
  WinNtReleaseDC                      ReleaseDC;
  WinNtRegisterClassEx                RegisterClassEx;
  WinNtUnregisterClass                UnregisterClass;

  WinNtBeginPaint                     BeginPaint;
  WinNtEndPaint                       EndPaint;
  WinNtPostQuitMessage                PostQuitMessage;
  WinNtDefWindowProc                  DefWindowProc;
  WinNtLoadIcon                       LoadIcon;
  WinNtLoadCursor                     LoadCursor;
  WinNtGetStockObject                 GetStockObject;
  WinNtSetViewportOrgEx               SetViewportOrgEx;
  WinNtSetWindowOrgEx                 SetWindowOrgEx;
  WinNtMoveWindow                     MoveWindow;
  WinNtGetWindowRect                  GetWindowRect;

  WinNtGetMessage                     GetMessage;
  WinNtTranslateMessage               TranslateMessage;
  WinNtDispatchMessage                DispatchMessage;

  WinNtGetProcessHeap                 GetProcessHeap;
  WinNtHeapAlloc                      HeapAlloc;
  WinNtHeapFree                       HeapFree;
  
  WinNtQueryPerformanceCounter        QueryPerformanceCounter;
  WinNtQueryPerformanceFrequency      QueryPerformanceFrequency;
  
} EFI_WIN_NT_THUNK_PROTOCOL;

修改之后的可以直接在 UEFI Application 中进行定义的结构体如下

typedef struct {
  UINT64                              Signature;
  //
  // Win32 Process APIs
  //
  UINTN API1[17];
  //
  // Wint32 Mutex primitive
  //
  UINTN API2[11];  
  //
  // Win32 Console APIs
  //
  UINTN API3[15];    
  //
  // Win32 File APIs
  //
  UINTN API41[16];  
  MyWinNtGetStdHandle                 GetStdHandle;
  UINTN API42[4];   
  MyWinNtWriteFile                    WriteFile;
  UINTN API5[6];   
  //
  // Win32 Time APIs
  //
  UINTN API6[10];   
  //
  // Win32 Serial APIs
  //
  UINTN API7[44];     
} MY_EFI_WIN_NT_THUNK_PROTOCOL;

 

这个结构体中,对我们有用的是  WinNtWriteFile   WriteFile  还有  WinNtGetStdHandle GetStdHandle。只要这两个的偏移正确即可进行调用。对于这两个函数,我们还需要重新改写一下原型,比如之前的定义为:

typedef
WINBASEAPI
BOOL
(WINAPI *WinNtWriteFile) (
  HANDLE        FileHandle,
  LPCVOID       Buffer,
  DWORD         NumberOfBytesToWrite,
  LPDWORD       NumberOfBytesWritten,
  LPOVERLAPPED  Overlapped
  );

 

经过修改之后的如下

typedef
EFI_STATUS
(EFIAPI *MyWinNtWriteFile) (
  IN EFI_HANDLE FileHandle,
  CHAR8*        Buffer,
  UINT32        NumberOfBytesToWrite,
  UINT32*       NumberOfBytesWritten,
  UINT32        Overlapped  
  );

 

就是这样,只要我们定义出正确的函数偏移,再喂给他正确的参数就能够完成调用。最后,完整的代码如下:

/** @file

A simple, basic, application showing how the Hello application could be

built using the "Standard C Libraries" from StdLib.

 

Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>

This program and the accompanying materials

are licensed and made available under the terms and conditions of the BSD License

which accompanies this distribution. The full text of the license may be found at

http://opensource.org/licenses/bsd-license.

 

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <Library/BaseLib.h>

#include <Uefi.h>

#include <Library/UefiLib.h>

#include <Library/PrintLib.h>

#include <Library/ShellCEntryLib.h>

 

#define STD_OUTPUT_HANDLE -11

 

extern EFI_BOOT_SERVICES         *gBS;

 

EFI_GUID gEfiWinNtThunkProtocolGuid     =

{ 0x58C518B1, 0x76F3, 0x11D4,

{ 0xBC, 0xEA, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};

 

typedef

EFI_STATUS

(EFIAPI *MyWinNtWriteFile) (

IN EFI_HANDLE FileHandle,

CHAR8*        Buffer,

UINT32        NumberOfBytesToWrite,

UINT32*       NumberOfBytesWritten,

UINT32        Overlapped

);

 

typedef

EFI_HANDLE

(EFIAPI *MyWinNtGetStdHandle) (

EFI_HANDLE   StdHandle

);

typedef struct {

UINT64                              Signature;

//

// Win32 Process APIs

//

UINTN API1[17];

//

// Wint32 Mutex primitive

//

UINTN API2[11];

//

// Win32 Console APIs

//

UINTN API3[15];

//

// Win32 File APIs

//

UINTN API41[16];

MyWinNtGetStdHandle                 GetStdHandle;

UINTN API42[4];

MyWinNtWriteFile                    WriteFile;

UINTN API5[6];

//

// Win32 Time APIs

//

UINTN API6[10];

//

// Win32 Serial APIs

//

UINTN API7[44];

} MY_EFI_WIN_NT_THUNK_PROTOCOL;

 

/***

Demonstrates basic workings of the main() function by displaying a

welcoming message.

 

Note that the UEFI command line is composed of 16-bit UCS2 wide characters.

The easiest way to access the command line parameters is to cast Argv as:

wchar_t **wArgv = (wchar_t **)Argv;

 

@param[in]  Argc    Number of argument tokens pointed to by Argv.

@param[in]  Argv    Array of Argc pointers to command line tokens.

 

@retval  0         The application exited normally.

@retval  Other     An error occurred.

***/

int

main (

IN int Argc,

IN char **Argv

)

{

EFI_STATUS                      Status;

MY_EFI_WIN_NT_THUNK_PROTOCOL    *MyWinNTThunkProtocol;

//

// Cache of standard output handle .

//

EFI_HANDLE                      mStdOut;

 

CHAR8           Buffer[200];

UINT32          CharCount;

 

//

// Look for Ram Disk Protocol

//

Status = gBS->LocateProtocol (

&gEfiWinNtThunkProtocolGuid,

NULL,

(VOID **)&MyWinNTThunkProtocol

 

);

if (EFI_ERROR (Status)) {

Print(L"Couldn't find WinNtThunkProtocol\n");

return EFI_ALREADY_STARTED;

}

 

Print(L"Found WinNt Thunk\n");

//

// Cache standard output handle.

//

mStdOut = MyWinNTThunkProtocol-> GetStdHandle

((EFI_HANDLE)STD_OUTPUT_HANDLE);

Print(L"mStdOut=%X\n",mStdOut);

 

CharCount = (UINT32)AsciiSPrint (

Buffer,

sizeof (Buffer),

"www.lab-z.com %X\n\r",

2017

);

 

//

// Callout to standard output.

//

MyWinNTThunkProtocol->WriteFile (

mStdOut,

Buffer,

CharCount,

&CharCount,

0

);

 

return EFI_SUCCESS;

}

 

我们在 NT32 的Shell中调用编写好的 Application 2次:

nt1

对应在Log中可以看到有2次输出
nt2

参考:

  1. http://www.lab-z.com/stu82/ NT32Pkg的Debug Message

2018年1月4日 krishnaLee 给出了一个更简单高效的方法,亲测有效:

1,在Nt32Pkg.dsc的【component】区域写:
SampleAppDebug/SampleApp.inf {

gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xff
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xffffffff
}
2,编译:build -p Nt32Pkg\Nt32Pkg.dsc -m SampleAppDebug\SampleApp.inf
3,运行。

附件是例子的 application:

SampleAppDebug

Step to UEFI (129)EFI_CPU_ARCH_PROTOCOL

最近在看 EFI_CPU_ARCH_PROTOCOL 相关内容,相关定义只在 PI Specification的DXE_CIS 章节,和 UEFI Specification 无关。具体如下:

car1

这次先试试其中的NumberOfTimers 和GetTimerValue 。NumberOfTimers 返回的是当前系统中的Timer数量,从我的几个机器的实验看,目前都是 1。GetTimerValue定义如下:

car2

其中的 TimerValue 给出当前系统Timer的值;TimerPeriod给出当前系统Timer值的单位,这个单位是飞秒(10^-15)。比如,TimerPeriod=10^6 (1ns),TimerValue=10^3,那么 Timer 经过的事件是两者相乘的 10^9, 1 微秒(1 microsecond)。

我们编写一个代码来进行实验,测试一个 gBS->stall 1秒,经过的时间。代码如下 :

 

/** @file

    A simple, basic, application showing how the Hello application could be

    built using the "Standard C Libraries" from StdLib.



    Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>

    This program and the accompanying materials

    are licensed and made available under the terms and conditions of the BSD License

    which accompanies this distribution. The full text of the license may be found at

    http://opensource.org/licenses/bsd-license.



    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <Library/BaseLib.h>

#include <Uefi.h>

#include <Library/UefiLib.h>

#include <Library/PrintLib.h>

#include <Library/ShellCEntryLib.h>



#include <Protocol/Cpu.h>



EFI_GUID gEfiCpuArchProtocolGuid =

                { 0x26BACCB1, 0x6F42, 0x11D4,

                        { 0xBC, 0xE7, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};



extern EFI_BOOT_SERVICES         *gBS;



/***

  Demonstrates basic workings of the main() function by displaying a

  welcoming message.



  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.

  The easiest way to access the command line parameters is to cast Argv as:

      wchar_t **wArgv = (wchar_t **)Argv;



  @param[in]  Argc    Number of argument tokens pointed to by Argv.

  @param[in]  Argv    Array of Argc pointers to command line tokens.



  @retval  0         The application exited normally.

  @retval  Other     An error occurred.

***/

int

main (

  IN int Argc,

  IN char **Argv

  )

{

  EFI_CPU_ARCH_PROTOCOL  *Cpu;

  UINT64                 TimerValue;

  UINT64                 StartTimerValue; 

  UINT64                 EndTimerValue; 

  UINT64                 TimerPerioid;

  EFI_STATUS             Status;

  UINT64                 tmp;



  //

  // Locate the Cpu Arch Protocol.

  //

  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, &Cpu);

  if (EFI_ERROR (Status)) {

    Print(L"Can't find EFI_CPU_ARCH_PROTOCOL\n");

    return Status;

  }



  //Get some base information

  Cpu->GetTimerValue(

        Cpu,

        0, //There is only one timer in system

        &TimerValue,

        &TimerPerioid

  );



  //Usually there is only one timer in this protocol

  Print(L"NumberOfTimers: %d\n",Cpu->NumberOfTimers); 

  //The unite of one timer ,femtoseconds 10^(-15)s

  Print(L"Timer period  : %ld\n",TimerPerioid);



  //Make a simple test here, delay 1 second

  //And check how long it is in the timer

  Print(L"Timer Test Start delay 1s\n");

  Cpu->GetTimerValue(

                Cpu,

                0, //There is only one timer in system

                &StartTimerValue,

                &TimerPerioid

        ); 

  Print(L"   Start at: %ld\n",StartTimerValue);

  gBS->Stall(1000000UL);

  Cpu->GetTimerValue(

                Cpu,

                0, //There is only one timer in system

                &EndTimerValue,

                &TimerPerioid

        ); 

  Print(L"     End at: %ld\n",EndTimerValue);

  tmp = MultU64x64(EndTimerValue-StartTimerValue,TimerPerioid);

  tmp = DivU64x32 (tmp,1000000000UL);

  Print(L"    Elapsed: %ld us\n",tmp);



  return 0;

}

NT32 的虚拟机实验结果如下:

car3

实体机上实验结果如下(看起来实体机上偏差更大?):

car4

X64 EFI Application下载:

cat1

完整代码下载:

CPUArchTest1

时间单位的简单介绍【参考1】:

second millisecond microsecond nanosecond picosecond femtosecond

1ms (毫秒) 1毫秒=0.001秒=10-3
1μs (微秒)   1微秒=0.000001=10-6
1ns (纳秒)  1纳秒=0.0000000001秒=10-9
1ps (皮秒)  1皮秒=0.0000000000001秒=10-12
1fs (飞秒)  1飞秒=0.000000000000001秒=10-15

参考:

  1. http://blog.163.com/digoal@126/blog/static/163877040201062810748700/

 

Arduino Leonardo 串口起始条件

之前提到过,在编程做Arduino Leonrado 的串口通讯时,需要特别设定打开流控制才能正常工作

us

经过研究发现,这是因为在 \arduino-1.8.4\hardware\arduino\avr\cores\arduino\CDC.cpp 下面的代码导致的。这段代码会检测当前的串口是否有 lineState的设定,如果没有的话,返回 False (应该是Serial就无法正常的初始化)。这段代码的初衷应该是保证从IDE刷新代码之后,直到打开串口才会执行,但是如果你并非使用IDE就会很麻烦。所以可以根据情况移除。

Serial_::operator bool() {
bool result = false;
if (_usbLineInfo.lineState > 0)
result = true;
delay(10);
return result;
}

UEFI Tips: 介绍一个新功能:RamDisk

2015年底的时候, UEFI Specification 2.6中新加入了一个 Ram Disk Protocol,但是一直以来我搞不清楚如何使用。到了 UDK2017 有了Demo,终于可以弄清楚使用方法。
实验环境是 QEMU X64。用到UDK2017中的OvmfPkgX64.dsc,编译之后生成 ovmf.fd 使用 qemu-system-x86_64.exe –bios ovmf.fd 进行启动。启动之后,可以在Setup中刚看到 RamDisk 的相关设定。

image001

从下面可以看到有2种创建方式,一种是直接创建一个空的 Ram Disk。另外一种是读取一个文件放在内存中作为 RamDisk

image002

先试试第一种,选择创建一个 RAW 的Ram Disk大小为 4KB。
image003

启动之后可以在Shell 下面看到(图中 BLK3)。
image004

因为是 RAW,所以没有 FAT分区,也就不会出现 FSx: 这样的盘符。
此外,还可以使用 Create from file 来安装一个带有 Fat 的盘。我做了一个 MemTest 的镜像,安装之后会出现 Fs0: , 但是 MemTest 软件本身在运行的时候会死机(可能是 QEMU 模拟的硬件太老和MemTest有兼容性问题):

image005

同样的方法在实体机上能够正常工作,下图是在 KabyLake-Y上运行的结果:

image006

image007

可以看到,这个功能能够让一些已经编译好的UEFI Application 顺利执行,最直接的应用就是在Boot From WIFI 。

本文提到的 MemTest 工具可以在这里下载 MemTest

这里放一个UDK2017 编译生成的 OVMF.FD ,可以进行试验。需要注意的是,启动速度比较慢,请耐心等待
OVMF

Arduino LED 的思考题

之前做了几块 LED Shield,焊接的是高亮的LED,在使用的发现实在是太亮了,排在一起很难分辨清楚,于是,重新买的普通LED焊接了一下。

image001

0-7放的是红色LED,8-13放的是蓝色LED。可以看到,他们都能工作。

led2

led3

但是如果写出来下面这样的代码,只有红色LED工作:
for (int i=0;i<14;i++) {
pinMode(i,OUTPUT);
digitalWrite(i,HIGH);
}
进一步实验发现,只要有一个红色的亮了,那么蓝色的就没有办法亮起来。

这就是我碰到的问题,有兴趣的朋友可以开始思考原因了。答案在下面。














最终,发现这个问题是因为红色LED压降低导致的,比如,亮起来一个红色之后,对GND的电阻上的电压只有3.3V了,而蓝色LED需要2.6V左右的压降才能工作,因此蓝色LED就没有办法亮起来了。
从设计上来说,这个问题是因为LED的地都接到一起导致的,如果能够独立就不会有这样的问题。

Step to UEFI (128)汇编到机器码的转换

在很长一段时间里面因为有编译器的帮助,完全没有手工进行汇编语言和机器语言转化的需求。不过前面碰到了RDRAND 这样的指令,一方面说明CPU指令还在增长,另一方面也发觉还有这样的要求,于是进行一些探究。
从资料上看,目前还活着的汇编工具有 NASM。先查阅一下支持的指令【参考1】,确定支持了 RDRAND。
a2s1

编写一个简单的 asm 程序,里面有六条指令:
rdrand eax
rdrand ebx
rdrand edi
rdrand ax
rdrand bx
rdrand di使用nasm进行编译,未指定格式默认编译目标为 Binary, -l test.lst 表示生成一个 list 文件
a2s2

打开生成的 test.lst,可以看到下面每条语句对应的机器码(0x66 是32位操作的前缀)
1 00000000 660FC7F0 rdrand eax
2 00000004 660FC7F3 rdrand ebx
3 00000008 660FC7F7 rdrand edi
4 0000000C 0FC7F0 rdrand ax
5 0000000F 0FC7F3 rdrand bx
6 00000012 0FC7F7 rdrand di
同样的,NASM 还有一个反编译工具 ndisasm.exe:
a2s3

从上面可以看出 NASM 可以用来生成指令对应的机器码。在没有涉及到内存地址的情况下是很好的工具。另外一个方面,使用他自带的反编译工具也可以方便的完成机器码到汇编指令的转换。
下载:http://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D

参考:
1. http://www.nasm.us/xdoc/2.13.01/html/nasmdocb.html

20190905 补充: 这里还有一个在线的汇编语言转机器的工具,可以方便的进行查询:

https://defuse.ca/online-x86-assembler.htm#disassembly

VC Console 读取 WMI 的例子

来自 https://msdn.microsoft.com/en-us/library/aa390423(v=vs.85).aspx

特别注意取得的 WMI 的 String 是 Unicode 的,输出时需要使用 %ls

#include “stdafx.h”

#define _WIN32_DCOM
#include
#include

#pragma comment(lib, “wbemuuid.lib”)

int main(int argc, char **argv)
{
HRESULT hres;

// Step 1: ————————————————–
// Initialize COM. ——————————————

hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
printf(“Failed to initialize COM library. Error code”);
return 1; // Program has failed.
}

// Step 2: ————————————————–
// Set general COM security levels ————————–

hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);

if (FAILED(hres))
{
printf(“Failed to initialize security. Error code = 0x”);
CoUninitialize();
return 1; // Program has failed.
}

// Step 3: —————————————————
// Obtain the initial locator to WMI ————————-

IWbemLocator *pLoc = NULL;

hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *)&pLoc);

if (FAILED(hres))
{
printf(“Failed to create IWbemLocator object.”);
CoUninitialize();
return 1; // Program has failed.
}

// Step 4: —————————————————–
// Connect to WMI through the IWbemLocator::ConnectServer method

IWbemServices *pSvc = NULL;

// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L”ROOT\\CIMV2″), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);

if (FAILED(hres))
{
printf(“Could not connect. Error code = 0x”);
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

printf(“Connected to ROOT\\CIMV2 WMI namespace”);

// Step 5: ————————————————–
// Set security levels on the proxy ————————-

hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);

if (FAILED(hres))
{
printf(“Could not set proxy blanket. Error code = 0x”);
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

// Step 6: ————————————————–
// Use the IWbemServices pointer to make requests of WMI —-

// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(“WQL”),
bstr_t(“SELECT * FROM Win32_OperatingSystem”),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);

if (FAILED(hres))
{
printf(“Query for operating system name failed.”);
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}

// Step 7: ————————————————-
// Get the data from the query in step 6 ——————-

IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;

while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);

if (0 == uReturn)
{
break;
}

VARIANT vtProp;

// Get the value of the Name property
hr = pclsObj->Get(L”Name”, 0, &vtProp, 0, 0);
//wcout << " OS Name : " << vtProp.bstrVal << endl; printf(" OS Name : %ls\n", vtProp.bstrVal); VariantClear(&vtProp); pclsObj->Release();
}

// Cleanup
// ========

pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();

system(“PAUSE”);
return 0; // Program successfully completed.

}

运行结果 (VS2015 Windows 10 RS1)

Capture

Step to UEFI (127)RDRAND 再研究

前面提到过 RDRAND指令,最近再拿出来看一下,思考了更多问题。
前情提要:
具体的RDRAND指令是通过内嵌汇编实现的,在ASM中直接定义如下

        ; rdrand   ax                  ; generate a 16 bit RN into ax, CF=1 if RN generated ok, otherwise CF=0
        db     0fh, 0c7h, 0f0h         ; rdrand r16:  "0f c7 /6  ModRM:r/m(w)"

 

第一个问题是:这些数字是哪里来的,如何对应到这个指令上的?这种事情必须查阅 Intel 手册了,在“Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z” 有如下介绍:

rd1

从上面可以看到 0F C7 /6 就是 RDRAND指令。
接下来是第二个问题:这组数字是如何决定指令使用的寄存器的?意思是:如何确定当前机器代码是rdrand ax 还是rdrand bx?
这个问题就涉及到了指令编码 ModR/M 的知识。在研究了下面的资料之后
http://www.mouseos.com/x64/index.html x86/x64 指令编码内幕(适用于 AMD/Intel)
http://blog.csdn.net/aap159951/article/details/48706207 自己的反汇编引擎–Intel指令编码 (2)
配合下面的表格:0F C7 /6 中的 /6 决定列,然后红色框中的是对应的寄存器

rd2

具体来说0F C7 F1 表示 RDRAND ecx;0F C7 F7 表示 RDRAND edi。
接下来就是如何验证上面的猜想。我们使用 0f 0c7 f3 来验证是否为 RDRAND EBX,使用 0f 0c7 f7 来验证是否为 RDRAND EDI。
为了配合内嵌汇编,我在INF 中设置打开生成的 COD文件,这样可以方便的看清楚参数的传递。

[BuildOptions]
  MSFT:*_*_X64_CC_FLAGS = /Oi- /FAcs /Od

 

这样在编译的时候会生成 cod文件,可以在\Build\AppPkg\DEBUG_VS2013x86\X64\AppPkg\Applications\ 的对应目录下找到
从里面可以看到,通过 rcx传递进去地址,返回值也会在对应的地址中,同时判断 eax是否成功。

; 143  :     if (RdRand32StepEDI (Rand)) {

  00046	48 8b 4c 24 40	 mov	 rcx, QWORD PTR Rand$[rsp]
  0004b	e8 00 00 00 00	 call	 RdRand32StepEDI
  00050	0f b6 c0	 movzx	 eax, al
  00053	85 c0		 test	 eax, eax
  00055	74 04		 je	 SHORT $LN1@RdRand32ED

; 144  :       return EFI_SUCCESS;

 

有了上面的实验,即可编写代码。需要特别注意三点:一个是不要破坏寄存器,否则可能出现不可预料的后果;另外一个是当函数接收参数只有一个时,会使用rcx来传递参数【这部分的详细资料目前没有查到,有了解的朋友欢迎在留言中补充】;最后的结果会从 rcx 传出,在这个例子中是传输到 rcx 给出的地址中。
最终的代码如下:

;------------------------------------------------------------------------------
;  Generate a 32 bit random number
;    Return TRUE if Rand generated successfully, or FALSE if not
;
;  BOOLEAN EFIAPI RdRand32Step (UINT32 *Rand);   RCX
;------------------------------------------------------------------------------
RdRand32StepEBX  PROC
    ; rdrand   ebx                 ; generate a 32 bit RN into eax, CF=1 if RN generated ok, otherwise CF=0
    push   rbx
    db     0fh, 0c7h, 0f3h         ; rdrand r32:  "0f c7 /6  ModRM:r/m(w)"
    jb     rn32_ok                 ; jmp if CF=1
    xor    ebx, ebx                ; reg=0 if CF=0
    pop    rbx
    ret                            ; return with failure status
rn32_ok:
    mov    [rcx], ebx
    mov    rax,  1
    pop    rbx
    ret
RdRand32StepEBX ENDP

;------------------------------------------------------------------------------
;  Generate a 32 bit random number
;    Return TRUE if Rand generated successfully, or FALSE if not
;
;  BOOLEAN EFIAPI RdRand32Step (UINT32 *Rand);   RCX
;------------------------------------------------------------------------------
RdRand32StepEDI  PROC
    ; rdrand   edi                 ; generate a 32 bit RN into eax, CF=1 if RN generated ok, otherwise CF=0
    push   rdi
    db     0fh, 0c7h, 0f7h         ; rdrand r32:  "0f c7 /6  ModRM:r/m(w)"
    jb     rn32_ok                 ; jmp if CF=1
    xor    edi, edi                ; reg=0 if CF=0
    pop    rdi
    ret                            ; return with failure status
rn32_ok:
    mov    [rcx], edi
    mov    rax,  1
    pop    rdi
    ret
RdRand32StepEDI ENDP

 

运行结果如下:

rd3

完整的代码下载:

RdRand2

最后还有第三个问题:从上面可以看到0fh, 0c7h, 0f7h 可以对应 RDRAND eax 也可以对应到 RDRAND ax,那么是如何进行区别的呢?
我猜测是根据当前CPU 的状态决定的,当现在的代码段是32位,这个机器码操作的目标是 EAX,如果是16位,操作目标就是ax。 当然,对于这一点我现在无法确认,后面会想办法进行验证。