💻
UEFI 프로젝트
  • 🧑‍🏫프로젝트 개요
  • 📖UEFI 개념
    • 1. BIOS의 과거
    • 2. UEFI 개념
    • 3. BIOS vs UEFI
    • 4. UEFI 부팅 단계
  • 🖥️UEFI 개발
    • UEFI 개발 시작하기
      • 0. EDK II 빌드 환경 구성
      • 1. 간단한 EFI application 만들기
      • 2. 간단한 Pkg 만들기
      • 3. Hello World 출력하기
      • 4. 라이브러리와 Hello World
      • 5. Conf를 통한 Build 단순화
    • 핸들 및 프로토콜
      • 6. 핸들/프로토콜 데이터 베이스 구조 - Part 1
      • 7. 핸들/프로토콜 데이터 베이스 구조 - Part 2
      • 8. HandleProtocol API 함수 & ImageHandle 프로토콜을 통한 정보
      • 9. ProtocolsPerHandle API를 통한 ImageHandle 프로토콜 가져오기
      • 10. EFI_STATUS 타입 과 EFI_ERROR 매크로
    • 메모리 맵
      • 11. EFI 메모리 맵 정보 얻기
      • 12. EFI 메모리 맵을 리눅스 커널 스타일로 바꾸기
    • 명령줄 인수를 받는 간단한 앱 만들기
      • 13.ShellAppMain Entry point
      • 14.gRT->GetNextVariableName API를 사용하여 모든 변수 이름 및 GUID 가져오기
    • 부팅 옵션
      • 15. gRT->GetVariable API를 사용하여 부팅 변수 가져오기 및 구문 분석
      • 16. OVMF 이미지 내에 부팅 옵션 추가
      • 17. 부팅 옵션에 WaitForEvent 함수 추가
      • 18. ReadKeyStroke 함수로 사용자 입력 처리
      • 19. bcfg 명령어를 사용한 부팅 옵션 수정
    • PCD
      • 20. PCD 소개
      • 21. PCD 변수에 대한 Overriding
      • 22. Feature Flag PCD와 BOOLEAN FixedAtBuild PCD의 비교
      • 23. PatchableInModule PCD 및 GenPatchPcdTable/PatchPcdValue 유틸리티를 통해 PCD를 변경하는 방법
      • 24. Dynamic/DynamiEx PCDs
      • 25. PCD 더 알아보기
    • 테이블
      • 26. EFI_CONFIGURATION_TABLE에서 참조되는 테이블
      • 27. dmem/EFI_SMBIOS_PROTOCOL/smbiosview를 통해서 SMBIOS 정보 가져오기
      • 28. EFI_SHELL_PROTOCOL을 통하여 ACPI 테이블을 파일에 저장하기
      • 29. EFI_ACPI_SDT_PROTOCOL 및 ShellLib를 사용하여 ACPI BGRT 테이블에서 BMP 이미지 저장하기
    • PCI
      • 30. PCI 루트 브리지 찾은 후 시스템의 모든 PCI 기능 가져오기
      • 31. ShellLib/PrintLib 함수를 사용해 PCI Vendor/Device 정보 가져오기
      • 32. EFI_PCI_IO_PROTOCOL을 사용해 PCI Option ROM 이미지 표시
      • 33. EfiRom 유틸리티를 사용한 PCI Option ROM 이미지 파싱 및 생성
    • 드라이버 및 라이브러리
      • 34. 간단한 UEFI 드라이버 생성
      • 35. 애플리케이션에서 사용할 간단한 라이브러리 생성
      • 36. Library의 constructor와 destructor, NULL Library
      • 37. Shell에 acpiview 명령을 추가하는 방법 조사
      • 38. 사용자 지정 프로토콜을 만들고 사용하기
      • 39. RegisterKeyNotify / UnrigisterKeyNotify 함수를 사용해 단축키 기능을 추가하는 드라이버 만들기
      • 40. Key #### NVRAM 변수
    • 디버그
      • 41. DEBUG 출력문 내부 구조와 DEBUG 문 제어를 위한 PCD 분석, 그리고 OVMF 부트 로그 가져오기
      • 42. GDB를 이용한 Driver/Application 및 OVMF Debug
    • HII
      • 43. HII 데이터베이스 개념 및 출력
      • 44. HII 데이터베이스 내부
      • 45. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 문자열 목록 게시
      • 46. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 47. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기
      • 49.MODULE_UNI_FILE/PACKAGE_UNI_FILE/[UserExtensions.TianoCore."ExtraFiles"]의 도움으로 UNI 파일 선언하기
      • 50.UEFI_HII_RESOURCE_SECTION을 사용하여 문자열 패키지와 함께 HII 패키지 목록 게시하기
      • 51. UEFI APP에 메뉴얼 추가하기(shell의 -?와 help 옵션)
      • 52. Russian 글꼴 추가 - Part 1.
      • 53. Russian 글꼴 추가 - Part 2.
      • 54. EFI_HII_STRING_PROTOCOL의 NewString 및 SetString 함수를 사용하여 다른 언어에 대한 문자열 패키지를 동적으로 추가
      • 55. PlatformLangCodes EFI 변수 수정 및 다른 언어를 동적 추가하기
      • 56. 코드에서 FILE_GUID 및 BASE_NAME을 가져오기
    • VFR
      • 57. VFR을 사용해 간단한 폼 생성 및 EFI_FORM_BROWSER2_PROTOCOL.SendForm()를 통해 화면에 폼 표시하기
      • 58. VFR 요소 : subtitle 및 text
      • 59. 간단한 폼 애플리케이션을 UEFI 드라이버 Form으로 변환하기
      • 60. gRT->SetVariable() 함수를 사용한 UEFI 변수 생성, 변경 및 삭제
      • 61.dmpstore 명령을 사용하여 변수를 파일에 저장/로드하기
      • 62. UEFI Device path의 구조
      • 63. checkbox를 가진 HII 폼 만들기
      • 64. checkbox를 가진 HII폼 만들기
      • 65. VFR 추가 입력 요소 Part 1: number
      • 66. VFR 추가 입력 요소 Part 2: string
      • 67. VFR 추가 입력 요소 Part 3: date & time
      • 68. VFR 추가 입력 요소 Part 3: oneof & orderedlist
      • 69. VFR의 조건부 키워드
      • 70. VFR의 상수 및 연산자가 내장된 기본 조건문
      • 71. 기본 VFR 내장 문자열용 함수
      • 72. label 키워드를 이용하여 HII 양식에 동적 요소 추가하기
      • 73. VFR question 기본값 설정
  • 🔐UEFI 보안
    • 1. 개요
    • 2. 공격 벡터
    • 3. mitigation
    • 4. 정적 분석 방법
    • 5. 동적 분석 방법
Powered by GitBook
On this page
  1. UEFI 개발
  2. VFR

60. gRT->SetVariable() 함수를 사용한 UEFI 변수 생성, 변경 및 삭제

비휘발성 UEFI 변수(재부팅 후에도 지속되는 변수)와 함께 작동하는 HII 폼을 만들기 전에 UEFI 변수 서비스를 한 번 더 확인해야 한다.

지금까지는 gRT->GetVariable 및 gRT->GetNextVariableName 서비스를 통해 이미 존재하는 변수만 확인했다.

이번 장에서는 사용자 지정 UEFI 변수를 생성, 변경 및 삭제할 수 있는 애플리케이션을 만들어 본다.

이번 장에서는 gRT->SetVariable 함수를 사용한다.

SetVariable()

Summary:
Sets the value of a variable.

Prototype:
typedef
EFI_STATUS
SetVariable (
 IN CHAR16 *VariableName,
 IN EFI_GUID *VendorGuid,
 IN UINT32 Attributes,
 IN UINTN DataSize,
 IN VOID *Data
 );

Parameters:
VariableName 	A Null-terminated string that is the name of the vendor’s variable.
		Each VariableName is unique for each VendorGuid.
		VariableName must contain 1 or more characters. If VariableName is an empty string, then
		EFI_INVALID_PARAMETER is returned
VendorGuid 	A unique identifier for the vendor
Attributes 	Attributes bitmask to set for the variable
DataSize 	The size in bytes of the Data buffer ... A size of zero causes the variable to be deleted.
Data 		The contents for the variable

이제 애플리케이션을 생성한다.

$ createNewApp.sh SetVariableExample

UefiLessonsPkg/UefiLessonsPkg.dsc

[Components]
  UefiLessonsPkg/SetVariableExample/SetVariableExample.inf

사용자에게 사용자 정의 UEFI 변수를 만들거나 삭제할 수 있는 기능을 제공한다. 단순화를 위해 변수 값은 사용자가 제공하는 문자열이다. 아래는애플리케이션에 대한 도움말이다. UefiLessonsPkg/SetVariableExample/SetVariableExample.c

VOID Usage()
{
  Print(L"Delete variable\n");
  Print(L"   SetVariableExample <variable name>\n");
  Print(L"\n");
  Print(L"Set variable\n");
  Print(L"   SetVariableExample <variable name> <attributes> <value>\n");
  Print(L"\n");
  Print(L"<attributes> can be <n|b|r>\n");
  Print(L"n - NON_VOLATILE\n");
  Print(L"b - BOOTSERVICE_ACCESS\n");
  Print(L"r - RUNTIME_ACCESS\n");
}

변수를 생성할 때 변수 속성을 제공해야 한다는 것을 알 수 있다. 아래는 플래그의 조합일 수 있다.

  • EFI_VARIABLE_NOW_VOLATILE - 재부팅 후에도 변수가 남아있다.

  • EFI_VARIABLE_BOOTSERVICE_ACCESS - 변수를UEFI 단계에서 사용할 수 있다.

  • EFI_VARIABLE_RUNTIME_ACCESS - 변수를 OS단계에서 사용할 수 있다. (EFI_BOOT_SERVICES.ExitBootServices() 호출이 성공한 후)

위에서 살펴본 3개의 플래그가 있는 경우 아래 조합으로만 사용할 수 있다.

NON_VOLATILE
BOOTSERVICE_ACCESS
RUNTIME_ACCESS

+

+

+

+

+

+

+

+

///
/// Variable Attribute combinations.
///
#define VARIABLE_ATTRIBUTE_NV_BS           (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
#define VARIABLE_ATTRIBUTE_BS_RT           (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)
#define VARIABLE_ATTRIBUTE_NV_BS_RT        (VARIABLE_ATTRIBUTE_BS_RT | EFI_VARIABLE_NON_VOLATILE)
...

이제 다시 애플리케이션을 이어간다. 애플리케이션에서 명령 인자를 처리할 수 있도록 코드를 작성한다.

UefiLessonsPkg/SetVariableExample/SetVariableExample.c

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>


VOID Usage()
{
  ...
}

INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
{
  EFI_STATUS Status;

  if (Argc==2) {
    CHAR16* VariableName = Argv[1];
    Status = gRT->SetVariable (
                VariableName,
                &gEfiCallerIdGuid,
                0,
                0,
                NULL
                );
    if (EFI_ERROR(Status)) {
      Print(L"%r\n", Status);
    } else {
      Print(L"Variable %s was successfully deleted\n", VariableName);
    }
    return Status;
  } else if (Argc==4) {
    CHAR16* VariableName = Argv[1];
    CHAR16* AttributesStr = Argv[2];
    CHAR16* Value = Argv[3];
    UINT32  Attributes = 0;
    for (UINTN i=0; i<StrLen(AttributesStr); i++) {
      switch(AttributesStr[i]) {
        case L'n':
          Attributes |= EFI_VARIABLE_NON_VOLATILE;
          break;
        case L'b':
          Attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
          break;
        case L'r':
          Attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
          break;
        default:
          Print(L"Error! Unknown attribute!");
          return EFI_INVALID_PARAMETER;
      }
    }
    Status = gRT->SetVariable (
                VariableName,
                &gEfiCallerIdGuid,
                Attributes,
                StrSize(Value),
                Value
                );
    if (EFI_ERROR(Status)) {
      Print(L"%r\n", Status);
    } else {
      Print(L"Variable %s was successfully changed\n", VariableName);
    }
    return Status;
  } else {
    Usage();
  }

  return EFI_SUCCESS;
}

UefiLessonsPkg/SetVariableExample/SetVariableExample.inf

[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = SetVariableExample
  FILE_GUID                      = bb2a829f-7943-4691-a03a-f1f48519d7e6
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  SetVariableExample.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiLib
  ShellCEntryLib

Argc==4일 때, 들어오는 인자를 변수 생성을 위한 입력으로 처리한다. 인자를 파싱하고 gRT->SetVariable 서비스를 호출한다.

Argc==2일 때는 사용자가 변수 이름만 제공했음을 의미하여 도움말 메시지에 따라 해당 변수를 삭제해야 한다. 이를 위해 DataSize가 0인 gRT->SetVariable을 호출한다.

모든 변수는 gEfiCallerIdGuid GUID에서 생성되며, 여기서는 bb2a829f-7943-4691-a03a-f1f48519d7e6를 의미한다.

애플리케이션을 빌드 후 UEFI 공유 폴더에 복사한다.

이번에는 UEFI 변수를 다루기 때문에 아래 명령을 통해 QEMU를 실행한다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \

존재하는 UEFI 변수를 보려면 UEFI Shell에서 dmpstore 명령을 사용할 수 있다. dmpstore 명령어에는 특정 GUID와 관련된 변수만 출력하는 옵션이 있다. UEFI Shell에서 바로 실행하면 GUID와 관련된 내용이 없어 확인할 수 없다.

FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
dmpstore: No matching variables found. Guid BB2A829F-7943-4691-A03A-F1F48519D7E6

SetVariableExample.efi 애플리케이션을 통해 Hello 라는 내용을 가진 MyVar 변수를 생성한다.

FS0:\> SetVariableExample.efi MyVar b "Hello"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x0C
  00000000: 48 00 65 00 6C 00 6C 00-6F 00 00 00              *H.e.l.l.o...*

dmpstore 명령을 통해 변수가 생성된 것을 확인할 수 있다.

MyVar 변수를 수정한다.

FS0:\> SetVariableExample.efi MyVar b "Hello World"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x18
  00000000: 48 00 65 00 6C 00 6C 00-6F 00 20 00 57 00 6F 00  *H.e.l.l.o. .W.o.*
  00000010: 72 00 6C 00 64 00 00 00-                         *r.l.d...*

MyVar 변수를 삭제한다.

FS0:\> SetVariableExample.efi MyVar
Variable MyVar was successfully deleted
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
dmpstore: No matching variables found. Guid BB2A829F-7943-4691-A03A-F1F48519D7E6

또한 변수를 생성할 때 위에서 확인했던 조합이 아닌 경우에는 변수를 생성할 수 없다.

FS0:\> SetVariableExample.efi MyVar nr "Hello"
Invalid Parameter

NV+BS와 BS 속성을 가진 두 개의 변수를 생성한다.

FS0:\> SetVariableExample.efi MyPersistentVar nb "Persistent variable"
Variable MyPersistentVar was successfully changed
FS0:\> SetVariableExample.efi MyVar b "Memory variable"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable NV+BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar' DataSize = 0x28
  00000000: 50 00 65 00 72 00 73 00-69 00 73 00 74 00 65 00  *P.e.r.s.i.s.t.e.*
  00000010: 6E 00 74 00 20 00 76 00-61 00 72 00 69 00 61 00  *n.t. .v.a.r.i.a.*
  00000020: 62 00 6C 00 65 00 00 00-                         *b.l.e...*
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x20
  00000000: 4D 00 65 00 6D 00 6F 00-72 00 79 00 20 00 76 00  *M.e.m.o.r.y. .v.*
  00000010: 61 00 72 00 69 00 61 00-62 00 6C 00 65 00 00 00  *a.r.i.a.b.l.e...*

QEMU를 재실행 하고 dmpsote 명령을 확인하면 MyPersistentVar 변수가 남아있는 것을 확인할 수 있다.

FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable NV+BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar' DataSize = 0x28
  00000000: 50 00 65 00 72 00 73 00-69 00 73 00 74 00 65 00  *P.e.r.s.i.s.t.e.*
  00000010: 6E 00 74 00 20 00 76 00-61 00 72 00 69 00 61 00  *n.t. .v.a.r.i.a.*
  00000020: 62 00 6C 00 65 00 00 00-                         *b.l.e...*

또한 이미 생성된 변수의 속성을 변경할 수 없는지 확인할 수 있다.

FS0:\> SetVariableExample.efi MyPersistentVar b "Persistent variable"
Invalid Parameter

dmpstore 명령을 사용해 변수를 삭제할 수도 있다.

FS0:\> dmpstore -d -guid bb2a829f-7943-4691-a03a-f1f48519d7e6 MyPersistentVar
Delete variable 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar': Success

dmpsotre -d <GUID>명령을 사용해 GUID 뒤에 있는 모든 변수를 삭제할 수 있다.

여기서는 예시로 애플리케이션에서 변수 내용을 단순 문자열로 하여 진행했지만 변수에는 복잡한 구조가 저장될 수 있다.

Previous59. 간단한 폼 애플리케이션을 UEFI 드라이버 Form으로 변환하기Next61.dmpstore 명령을 사용하여 변수를 파일에 저장/로드하기

Last updated 2 years ago

이러한 플래그는 아래 링크에 정의되어 있으며, 지금은 다루지 않을 다른 플래그도 있다.

때로는 코드에서 아래와 같은 변수 속성 조합을 볼 수 있다.

🖥️
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiMultiPhase.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h