标题看起来非常拗口,具体来说描述起来就是下面的问题:
“我想写一个简单的程序,先把某个app的Load进内存,然后在内存里爆搜一个特征字串,搜到之后将该内存第一个字节替换。以下为代码片段,碰到一个问题就是,我搜到特征字串之后,修改其内存的内容一直改不了,请问各位大大,是不是UEFI有相应的保护策略,不能修改LoadImage的内存?我个人觉得是不应该,因为我是LoadImage的宿主,我Load的内存应该是可以被我修改的。请大牛们指教啊!!!
Status=gBS->LoadImage(TRUE, ImageHandle, DstDevicePath, NULL, 0, &DstImageHandle); //LoadImage
if (!EFI_ERROR(Status))
{
Print(L”Load Image success\n”);
}
Status=gBS->HandleProtocol(DstImageHandle, &gEfiLoadedImageProtocolGuid,(void **) &LoadedImage);
if (EFI_ERROR(Status)) {
Print(L”Can not retrieve a LoadedImageProtocol handle for ImageHandle\n”);
gBS->Exit(ImageHandle,EFI_SUCCESS,0,NULL);
}
//Get the loaded image base address
imageBase=LoadedImage->ImageBase;
size=LoadedImage->ImageSize;
temp=(char *)imageBase;
//Search the sig;, replace the first byte
for (; temp<(char *)imageBase+size; temp++)
{
if (*temp==0x55 && *(temp+1)==0x00 && *(temp+2)==0x45 && *(temp+3)==0x00)
{
Print(L"Find sig\n");
*temp=0x45;
Print(L"addr %x\n",temp);
break;
}
}
上述问题来自【参考1】
这个一个有趣的问题,也不知道那个朋友最后是否成功。根据上面的问题,我做一下实验。
首先,准备一个被修改的 App。当然,根据之前的知识,这个 App 不能使用 CLIB 库,目前为止我还是不知道为什么无法加载调用这个库的 Application.
#include <Uefi.h> #include <Library/PcdLib.h> #include <Library/UefiLib.h> #include <Library/UefiApplicationEntryPoint.h> /** The user Entry Point for Application. The user code starts with this function as the real entry point for the application. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { CHAR8 *s1= "This code comes from www.lab-z.com"; CHAR16 *s2=L" "; CHAR16 *Result; Result=AsciiStrToUnicodeStr(s1,s2); Print(L"%s\n",s2); return EFI_SUCCESS; }
代码非常简单,将一个 ASCII 字符串转化为 Unicode 的,然后显示出来。使用 ASCII 的原因是为了便于查找。
上面的程序编译之后,使用十六进制工具打开可以直接查看到 ASCII 字符。
然后,继续编写加载和修改的程序如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_SHELL_PROTOCOL *gEfiShellProtocol; extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2; extern EFI_HANDLE gImageHandle; /** GET DEVICEPATH **/ EFI_DEVICE_PATH_PROTOCOL * EFIAPI ShellGetDevicePath ( IN CHAR16 * CONST DeviceName OPTIONAL ) { // // Check for UEFI Shell 2.0 protocols // if (gEfiShellProtocol != NULL) { return (gEfiShellProtocol->GetDevicePathFromFilePath(DeviceName)); } // // Check for EFI shell // if (mEfiShellEnvironment2 != NULL) { return (mEfiShellEnvironment2->NameToPath(DeviceName)); } return (NULL); } int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE NewHandle; EFI_STATUS Status; UINTN ExitDataSizePtr; CHAR16 *R=L"HelloWorld.efi"; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo = NULL; CHAR8 *temp; Print(L"File [%s]\n",R); DevicePath=ShellGetDevicePath(R); // // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, gImageHandle, DevicePath, NULL, 0, &NewHandle); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during LoadImage [%X]\n",Status); return (Status); } Status = gBS -> HandleProtocol ( NewHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo ); Print(L"ImageBase [%lX]\n",ImageInfo->ImageBase); Print(L"ImageSize [%lX]\n",ImageInfo->ImageSize); temp=(char *)ImageInfo->ImageBase; //Search the sig;, replace the first byte for (; temp<(char *)ImageInfo->ImageBase+ImageInfo->ImageSize; temp++) { //"lab" 6C 61 62 if (*temp==0x6C && *(temp+1)==0x61 && *(temp+2)==0x62) { Print(L"Find sig\n"); Print(L"addr %x\n",temp); *(temp )=0x2D; *(temp+1)=0x2D; *(temp+2)=0x2D; break; } } // // now start the image, passing up exit data if the caller requested it // Status = gBS->StartImage( NewHandle, &ExitDataSizePtr, NULL ); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during StartImage [%X]\n",Status); return (Status); } gBS->UnloadImage (NewHandle); return EFI_SUCCESS; }
加载部分的代码使用的是 【参考2】的框架,搜索部分的代码用的是前面问题中给出来的示例。我们在加载后的 Application 的空间中搜索 “lab” 字符串并且替换为 “—”。
运行结果如下,我们先执行了一次 HelloWorld.efi,可以看到他能正常打印字符串,之后再用我们的程序加载一次,可以看到字符串被修改掉了。
看起来并没有什么保护之类的,轻而易举的改掉了 Application 的内容。猜测之前提出问题的朋友有可能是被加载的代码用到了 CLIB, 或者是代码中的字符串是按照 UNICODE 给出来的,所以无法找到。
这样的动态加载可以用在一些特殊的地方,比如,我见过一款 DOS 下的测试软件,有一个主程序 EXE 和 n多个独立的 EXE 构成。主程序可以调用其他的 EXE 进行测试,但是单独的 EXE 无法执行,这样的好处是开发时可以独立开发单独模块,分发之后有主程序进行控制只在需要的环境中运行。
本文提到的代码下载
参考:
1. http://biosren.com/thread-4564-1-31.html
2. http://www.lab-z.com/efiloadedimageprotocol/ Step to UEFI (46) —– EFILOADEDIMAGEPROTOCOL的使用
能否用來檢查 startImage出來的字串呢?
EX: Fail:…..
我只要檢查再startImage之後有Fail就跳出迴圈
StartImage 有返回参数的,直接检查返回值应该就可以的
都會是success 因為 fail 是我呼叫的function 顯示fail
StartImage都是成功的
了解,我想想哈