UEFI System Table 中的 ConOut-> OutputString 能够让我们直接在屏幕上输出字符串。这里介绍一种方法,能够让我们截获并且修改这个函数输出的字符串。
首先看一下 System Table 的定义在 \MdePkg\Include\Uefi\UefiSpec.h
/// /// EFI System Table /// typedef struct { /// /// The table header for the EFI System Table. /// EFI_TABLE_HEADER Hdr; /// /// A pointer to a null terminated string that identifies the vendor /// that produces the system firmware for the platform. /// CHAR16 *FirmwareVendor; /// /// A firmware vendor specific value that identifies the revision /// of the system firmware for the platform. /// UINT32 FirmwareRevision; /// /// The handle for the active console input device. This handle must support /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. /// EFI_HANDLE ConsoleInHandle; /// /// A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL interface that is /// associated with ConsoleInHandle. /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; /// /// The handle for the active console output device. /// EFI_HANDLE ConsoleOutHandle; /// /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface /// that is associated with ConsoleOutHandle. /// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; /// /// The handle for the active standard error console device. /// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. /// EFI_HANDLE StandardErrorHandle; /// /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface /// that is associated with StandardErrorHandle. /// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; /// /// A pointer to the EFI Runtime Services Table. /// EFI_RUNTIME_SERVICES *RuntimeServices; /// /// A pointer to the EFI Boot Services Table. /// EFI_BOOT_SERVICES *BootServices; /// /// The number of system configuration tables in the buffer ConfigurationTable. /// UINTN NumberOfTableEntries; /// /// A pointer to the system configuration tables. /// The number of entries in the table is NumberOfTableEntries. /// EFI_CONFIGURATION_TABLE *ConfigurationTable; } EFI_SYSTEM_TABLE;
其中的ConOut 是指向 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 的指针
具体定义可以在 \MdePkg\Include\Protocol\SimpleTextOut.h 查到
/// /// The SIMPLE_TEXT_OUTPUT protocol is used to control text-based output devices. /// It is the minimum required protocol for any handle supplied as the ConsoleOut /// or StandardError device. In addition, the minimum supported text mode of such /// devices is at least 80 x 25 characters. /// struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { EFI_TEXT_RESET Reset; EFI_TEXT_STRING OutputString; EFI_TEXT_TEST_STRING TestString; EFI_TEXT_QUERY_MODE QueryMode; EFI_TEXT_SET_MODE SetMode; EFI_TEXT_SET_ATTRIBUTE SetAttribute; EFI_TEXT_CLEAR_SCREEN ClearScreen; EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition; EFI_TEXT_ENABLE_CURSOR EnableCursor; /// /// Pointer to SIMPLE_TEXT_OUTPUT_MODE data. /// EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; };
我们需要的是将 EFI_TEXT_STRING OutputString; 替换为我们自己的函数。
最终的代码如下
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; EFI_SYSTEM_TABLE myST; EFI_SYSTEM_TABLE *pmyST=&myST; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL myConOut; EFI_STATUS myOut ( IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, IN CHAR16 *String ) { //Just a experiment, add a String to the output CHAR16 R[40]=L"LAB-Z:"; StrCat(R,String); gST->ConOut->OutputString(This,R); return EFI_SUCCESS; } int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { //Create a fake EFI_SYSTEM_TABLE named myST memcpy(&myST,gST,sizeof(EFI_SYSTEM_TABLE)); //Test this EFI_SYSTEM_TABLE gST->ConOut->OutputString( gST->ConOut,L"Test of gSt 1\n\r"); pmyST->ConOut->OutputString(pmyST->ConOut,L"Test of pmyST 2\n\r"); //Create a fake EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL memcpy(&myConOut,gST->ConOut,sizeof(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL)); //Test the fake ConOut pmyST->ConOut=&myConOut; //If we use pmyST->ConOut it will be an error pmyST->ConOut->OutputString(gST->ConOut,L"Test of myConOut 3\n\r"); //Replace OutputString function with our function pmyST->ConOut->OutputString=&myOut; pmyST->ConOut->OutputString(gST->ConOut,L"Test of myConOut 4\n\r"); return EFI_SUCCESS; }
运行结果
特别需要注意的地方,如果我们写成
pmyST->ConOut->OutputString(pmyST ->ConOut,L"Test of myConOut 3\n\r");
那么会碰到下面这个错误
产生问题的代码在 \ShellPkg\Application\Shell\ConsoleLogger.c 中,看起来是向 Handle 安装另外一个 Protocol的时候出现报错信息。
Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut)); if (EFI_ERROR(Status)) { SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer); SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes); SHELL_FREE_NON_NULL((*ConsoleInfo)); *ConsoleInfo = NULL; return (Status); } gST->ConsoleOutHandle = gImageHandle; gST->ConOut = &(*ConsoleInfo)->OurConOut; return (Status);
完整的代码下载:
MySt
之前可以用,是因为你没有改写pmyST->ConOut 的地址,这时地址和gST->ConOut是相等的,
后来你改写了ConOut 这样CR 就通不过验证了
如果你把改写后pmyST->ConOut 地址前的 UINTN 字节(Signature)也复制过来就应该不会报错了。
但不保证其他地方不出错,或者你声明一个 自己的 myCONSOLE_LOGGER_PRIVATE_DATA
#define CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('c', 'o', 'P', 'D')
typedef struct _CONSOLE_LOGGER_PRIVATE_DATA{
UINTN Signature;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut;
EFI_HANDLE OldConHandle;
UINTN ScreenCount;
CHAR16 *Buffer;
UINTN BufferSize;
// start row is the top of the screen
UINTN OriginalStartRow;
UINTN CurrentStartRow;
UINTN RowsPerScreen;
UINTN ColsPerScreen;
INT32 *Attributes;
UINTN AttribSize;
EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode;
BOOLEAN Enabled;
UINTN RowCounter;
} CONSOLE_LOGGER_PRIVATE_DATA;
#define CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(a) CR (a, CONSOLE_LOGGER_PRIVATE_DATA, OurConOut, CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE);
谢谢哈 回头我就试试看