티스토리 뷰
728x90
커플링 심화를 이용한
DLL 포인터 마스킹 후 역 마스킹하여 Caller 검증 방법
요구 사항
- CLASS A를 만들고, 함수
BOO
,FOO
구현 - CLASS
AImpl
을new
로 할당 - 할당된
AImpl
에 특정 수로 마스크 처리 - DLL에서 Caller에게 마스크된 포인터를 리턴
- Caller는
BOO
,FOO
호출 시 해당 포인터를 전달 - DLL에서 마스킹을 풀어 리턴된 포인터가 올바른지 체크
- 정상적이면
AImpl
내부 함수 호출
코드 예제
1. DLL 내 CLASS A 정의 및 포인터 마스킹 처리
DLL에서 CLASS A
와 AImpl
을 정의하고, 포인터를 특정 값으로 마스킹 처리한 후 Caller에게 반환합니다.
// MyDLL.cpp
#include <windows.h>
#include <iostream>
class A {
public:
virtual void BOO() = 0;
virtual void FOO() = 0;
virtual ~A() {}
};
class AImpl : public A {
public:
void BOO() override {
std::cout << "AImpl::BOO 호출됨!" << std::endl;
}
void FOO() override {
std::cout << "AImpl::FOO 호출됨!" << std::endl;
}
};
// 포인터 마스킹에 사용할 값
constexpr uintptr_t MASK = 0x5A5A5A5A;
// 포인터를 마스킹하여 반환
extern "C" __declspec(dllexport) uintptr_t CreateClassA() {
AImpl* pImpl = new AImpl();
return reinterpret_cast<uintptr_t>(pImpl) ^ MASK; // 마스킹 처리
}
// 마스킹된 포인터를 역마스킹하여 실제 포인터로 변환
AImpl* UnmaskPointer(uintptr_t maskedPtr) {
return reinterpret_cast<AImpl*>(maskedPtr ^ MASK); // 역마스킹
}
// BOO 함수 호출
extern "C" __declspec(dllexport) void CallBOO(uintptr_t maskedPtr) {
AImpl* pImpl = UnmaskPointer(maskedPtr);
if (pImpl) {
pImpl->BOO();
}
}
// FOO 함수 호출
extern "C" __declspec(dllexport) void CallFOO(uintptr_t maskedPtr) {
AImpl* pImpl = UnmaskPointer(maskedPtr);
if (pImpl) {
pImpl->FOO();
}
}
// 클래스 해제
extern "C" __declspec(dllexport) void DeleteClassA(uintptr_t maskedPtr) {
AImpl* pImpl = UnmaskPointer(maskedPtr);
if (pImpl) {
delete pImpl;
}
}
2. DEF 파일 설정 (NONAME 방식으로 export)
DLL의 DEF
파일에서 CreateClassA
와 CallBOO
, CallFOO
등을 NONAME 방식으로 export 합니다.
LIBRARY MyDLL
EXPORTS
CreateClassA @1 NONAME
CallBOO @2 NONAME
CallFOO @3 NONAME
DeleteClassA @4 NONAME
3. Caller가 DLL의 함수 호출
Caller는 마스킹된 포인터를 사용하여 DLL의 함수 BOO
와 FOO
를 호출합니다. DLL 내부에서 포인터가 올바른지 확인한 후 함수가 실행됩니다.
// main.cpp
#include <windows.h>
#include <iostream>
typedef uintptr_t (*CreateClassA)();
typedef void (*CallBOO)(uintptr_t);
typedef void (*CallFOO)(uintptr_t);
typedef void (*DeleteClassA)(uintptr_t);
int main() {
// DLL 로드
HMODULE hModule = LoadLibrary("MyDLL.dll");
if (!hModule) {
std::cerr << "DLL 로드 실패" << std::endl;
return 1;
}
// Ordinal 값으로 함수 가져오기
CreateClassA createClassA = (CreateClassA)GetProcAddress(hModule, MAKEINTRESOURCE(1)); // @1
CallBOO callBOO = (CallBOO)GetProcAddress(hModule, MAKEINTRESOURCE(2)); // @2
CallFOO callFOO = (CallFOO)GetProcAddress(hModule, MAKEINTRESOURCE(3)); // @3
DeleteClassA deleteClassA = (DeleteClassA)GetProcAddress(hModule, MAKEINTRESOURCE(4)); // @4
if (createClassA && callBOO && callFOO && deleteClassA) {
// 마스킹된 포인터 받기
uintptr_t maskedPtr = createClassA();
// BOO와 FOO 호출
callBOO(maskedPtr);
callFOO(maskedPtr);
// 객체 해제
deleteClassA(maskedPtr);
}
// DLL 언로드
FreeLibrary(hModule);
return 0;
}
보안 및 관리 측면
- 포인터 검증: 포인터를 마스킹하고, 역마스킹하여 DLL 내부에서만 사용되도록 하여 보안을 강화합니다. 마스킹된 포인터는 외부에서 조작하기 어렵습니다.
- 메모리 관리: DLL에서 객체의 생성과 해제를 모두 처리하므로, 메모리 누수나 충돌을 방지할 수 있습니다.
- 보안 강화: NONAME 기술을 사용해 함수 이름을 숨기고, ordinal 값으로 함수에 접근하게 하여 외부에서 함수의 이름을 알아내기 어렵습니다.
결론
NONAME 기술을 사용하여 DLL에서 포인터 마스킹과 역마스킹을 통해 보안을 강화하는 방법은 외부에서 조작된 포인터로 인한 공격을 방지하고, 메모리 관리의 일관성을 유지하는 데 매우 효과적입니다. 이 기법을 통해 DLL 함수 호출 시 포인터가 정상적인지 확인하고, 신뢰할 수 있는 경우에만 객체의 메서드를 호출할 수 있습니다.
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 블루버블
- OpenSource
- 암호화
- CMake
- 제주도
- 패턴
- 울릉도
- 스쿠버 다이빙
- 서귀포블루버블
- PowerShell
- effective
- 성산블루버블
- C
- script
- 외돌개
- C# 고급 기술
- 블루버블다이브팀
- DLL
- Linux
- 스쿠버다이빙
- 현포다이브
- 서귀포
- C#.NET
- Effective c++
- 네트워크 정보
- Build
- Windows
- C++
- C#
- 블루버블다이빙팀
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함
250x250