UEFI 스터디 11차 - 타 조원들과
Published:
검증, 자동화 측과 대화, 완성!
<~3.17>
검증 측 전달전 기존 작성한 코드를 한차례 실행 없이 검증. -> 중복되거나 필요없는 로직 제거, 주석추가.
분석시 사용할 기초? 드라이버
스크립트의 검출 범위 txt로 임시 작성(추후 수정 예정)
(OobHunter_v1_0_0)<~3.18>
검사 결과 json 반환 로직 추가
개개인 별로 json 반환 로직 추가시 충돌이 너무 심해서
일광이가 3.18일까지 사용하던 [SmmHunter_v4_0_1] json 반환 로직을 기반으로 추가. 일광이에게 패스 후 서웅이의 코드와 통합하여 문제없이 잘 작동함을 확인 받음. (OobHunter_v1_0_1) json 추가 로직은 이후도 그대로 사용위해 따로 주석을 추가해 txt 파일로 별도 저장. (json 반환 로직 변화시 자동화측의 공지 필요)
이후 해당 두 버전을 프로젝트 버전관리용 깃허브에 업로드!

정상작동은 하나, 적절한 테스트 데이터가 아직 전무하다. 추후 스크립트를 개선하며 해당부분도 생각해봐야할듯.
진행방향의 문제를 찾고 개선하기.
저번주에 스크립트의 작성계획에서의 방향성에 대하여 문제를 발견하였고.
검증측에서도 스크립트를 리뷰하고, 앞으로 검증의 방향성에 대하여 제시하였다.
이를 통합하여 나의 방향성을 바로 잡고자 하였다.
뭐가 문제였나?
- 실제 스크립트의 목적은 ‘위험 연산 탐지’이다.
- 진행 중에 막힌 ‘실제 CVE 속 드라이버에서의 정상탐지 여부’에 매몰되었다.
- 주제와 동떨어진, 난이도가 높은 해당 부분에서 시간이 모두 날아갔다.
이외에도
- 각 part로 쪼개는건 좋았으나, 핵심기능, 추가기능을 함께 구현하여, 시간을 많이 잡아먹었다 (이 때문에 막상 제대로 작동하는 부분만두니 핵심만 남았다.)
- 잡다한 것 추가 때문에 퀼리티가 점점 떨어지는 핵심 기능
등의 문제가 있었던 것 같다.
어떻게 개선해야할까
이문제의 출발은 실제 CVE 드라이버 ‘만’ 이용한 것이다.
실제 드라이버를 통해서 알게 된점은 많았다.
- 복잡한 형변환, 사이에 낀 잡다한 연산등으로, 예외상황에 강건한 스크립트가 작성가능했다.
- 단순히 만들어진 스크립트에선 알수 없는 여러 상황에 마주쳤다.
이점에선 계속 이용은 할 것 같으나. 단점으로는
- 너무 복잡한 함수들간 요청 구조 => 경로폭발 (물론, 이래도 돌아가야겠지만, 디버깅이 힘들었다.)
- 핵심로직과 관련없으나, 난이도가 높은 함정들.
=> 실제 CVE의 드라이버를 이용하는 것도 좋으나, 해결할 수 없는 부분을 감안해 직접만든 드라이버 적극 활용.
매우 단순한 드라이버 만들기
서웅이의 블로그글과 gemini의 도움으로 EDK2를 이용한 드라이버를 빌드했다.
가장 큰 이슈는 작성한 함수들이 EDK2가 멋대로 최적화를하여 함수 인라이닝이 발생하였다. 이후
build -a X64 -p OvmfPkg/OvmfPkgX64.dsc -m OvmfPkg/test_1/test_1.inf -t GCC5 -b NOOPT
(-b NOOPT 해당 옵션을 빌드 명령어 추가하여 해당 문제를 해결하였다)
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
// 1. 프로토콜 구조체 정의 (다시 심플하게 1개만!)
typedef struct _VULN_TEST_PROTOCOL {
VOID* (EFIAPI *ProcessImageSize) (UINT32 X, UINT32 Y);
} VULN_TEST_PROTOCOL;
// =======================================================
// [SINK] 메모리 할당만 전담하는 래퍼 함수 (인자 1개)
// =======================================================
__attribute__((noinline, used, optimize("O0")))
VOID* EFIAPI AllocatePoolWrapper (IN UINT32 Size) {
VOID* Buffer = NULL;
// 실제 UEFI 서비스를 여기서 호출
gBS->AllocatePool(EfiBootServicesData, (UINTN)Size, &Buffer);
return Buffer;
}
// =======================================================
// [CORE] 정수 오버플로우 계산 후 래퍼를 호출하는 함수
// =======================================================
__attribute__((noinline, used, optimize("O0")))
VOID* EFIAPI VulnerableProtocolFunction (IN UINT32 X, IN UINT32 Y) {
UINT32 AllocationSize = X * Y * 4;
VOID* Buffer = NULL;
if (AllocationSize > 0) {
// 직접 호출하는 대신, 인자 하나(Size)만 넣어서 래퍼 함수를 호출!
Buffer = AllocatePoolWrapper(AllocationSize);
}
return Buffer;
}
// =======================================================
// [WRAPPER] 눈치 볼 필요 없는 아주 깔끔한 래퍼 함수
// =======================================================
__attribute__((noinline, used, optimize("O0")))
VOID* EFIAPI ProtocolWrapperFunction (IN UINT32 ParamX, IN UINT32 ParamY) {
// 꼬리 물기 방지? 더미 연산? 이제 다 필요 없어! 바로 리턴해!
return VulnerableProtocolFunction(ParamX, ParamY);
}
// 2. 구조체 변수에 래퍼 하나만 깔끔하게 등록
VULN_TEST_PROTOCOL mMyVulnProtocol = {
ProtocolWrapperFunction
};
// -------------------------------------------------------
// [ENTRY] 드라이버 엔트리 포인트
// -------------------------------------------------------
EFI_STATUS EFIAPI test_1EntryPoint (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
EFI_HANDLE Handle = NULL;
EFI_GUID gGuid = { 0x11223344, 0x5566, 0x7788, { 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00 } };
return gBS->InstallMultipleProtocolInterfaces(&Handle, &gGuid, &mMyVulnProtocol, NULL);
}
코드 개선하기
코드 개선은 간단하다. 용진이 형이 제시한 ‘관련 CVE를 반영하여 좀더 제너럴하게 확장시키기’를 위해서,
탐지의 메인로직과 크게 관련 없으며 벌써부터 많은 자원을 먹고 있는 스택 내 변수 주소 추적 부분을 날릴 수 밖에 없을듯 하다. 추후 능력이된다면 오탐을 각오하고 오버헤드를 낮춰서 이 로직을 다소 살릴 수 도 있겠지만 지금은…
그래서 관련 로직을 지웠고 약 2~300줄이 사라졌다..
잘작동한다.

해당 코드를 이용해서 간단한 요구사항 검증도 해보려고 했으나
검증을 위해 추가한 임의로 추가한 코드를 누군가 다 잘라버려서
오늘 검증측 조언을 받아서 시도해보려고한다.
