Step to UEFI (92)关于 ConOut 的奇怪实验

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;
}

 

运行结果

image001

特别需要注意的地方,如果我们写成

pmyST->ConOut->OutputString(pmyST ->ConOut,L"Test of myConOut 3\n\r");

那么会碰到下面这个错误

image003

产生问题的代码在  \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

《Step to UEFI (92)关于 ConOut 的奇怪实验》有2个想法

  1. 之前可以用,是因为你没有改写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);

回复 ziv2013 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注