SetWindowsHookEx 으로 dll injection 공격시 방어 방법에 대해 문의드립니다.
글쓴이: jongmiss / 작성시간: 목, 2016/01/21 - 4:52오후
DLL Injection 공격시 방어하는 방법에 대해 궁금한데요.
여기저기 찾아보았더니, 아래와 같은 방법으로 방어할수 있다고 합니다.
1. SetWindowsHookEx 호출시 내부적으로 user32.dll의 ClientLoadLibrary 를 먼저 호출
2. ClientLoadLibrary 주소에 RET 어셈블리어 코드를 삽입
(출처: http://ryangs.egloos.com/v/758078)
심플한것 같아 시도해봤지만, 해당 함수는 존재하지 않아, 주소를 가져올 방법이 없었습니다. (dumpbin 으로 함수리스트 확인)
ex) FARPROC pAddress = (FARPROC)::GetProcAddress(::GetModuleHandleA("user32.dll"), "ClientLoadLibrary");
혹시, 다른방법이 있을까요? 도움부탁드립니다~
Forums:
굳이 함수 자체를 방어할 필요가 있는지?
그런식으로 방어를 한다는건 수 많은 문제를 야기할 수 있기에, 다른방법으로 시도를 해보시는건 어떠신지요?
어짜피 다른 프로세스에 모듈을 올릴려면 그 프로세스에서 LoadLibrary나 LdrLoadDll등의 방법을 써야 할것인데
이러한 부분에 대해 후킹을 걸어서 콜러의 주소를 검사하는 방법도 있겠고
또 다른 방법은 TH32CS_MODULE 등을 이용해서 모듈 비교를 해도 괜찮고
정식적으로 배포되는 소프트웨어에서는 배포하는 바이너리들을 전부 사인해놓고 사인되지 않은 DLL이 존재하면 DLL Injection이라고 감지해도되겠죠
혹은 스레드를 검사해서 스레드 영역을 스캔하는것도 괜찮고
여러가지 방법이 있지만 함수자체를 무조건 그냥 호출하자마자 RET로 만들어버리는건 위험한 방법입니다. 잘못하면 무한루프에 빠질수도 있는 여지가 있어서
답변 감사합니다. 우선 리스크에 대해선 고려하지
답변 감사합니다.
우선 리스크에 대해선 고려하지 못했네요.(_ _)
해당 함수(SetWindowsHookEx) 사용 시 내부적으로 LoadLibrary 를 호출하나요?
말씀해주신 방법 중 몇 가지 시도해 봐야겠습니다.
네 내부적으로 LoadLibrary 를 호출합니다.
네 내부적으로 LoadLibrary 를 호출합니다.
dll을 인젝션해서 후킹기능을 제공합니다.
방어하는 방법은
방어하는 방법은 정말 여러가지가 있습니다. 하지만 이게 이러한 상황에서는 이렇게 방어해야 한다! 라고 정해진게 없습니다.
어떤 하나의 공격에 대해서 방어법도 무수히 많고, 어떤 하나의 방어에 대해서 공격법도 수없이 많습니다. 그래서 이건 실전에 적용해봐야 아는것이구요
곰곰히 생각해보시면 DLL이 어떻게 대상 "프로세스"의 메모리에 적재되고 실행이 되는지 생각해보시면 되겠죠?
기본적으로 어짜피 SetWindowsHookEx(A/W)함수를 이용해 DLL Injection을 한다고해도 이 이벤트를 처리하는 커널 담당자는 대상 프로세스의 메모리에 공간을 할당받고 매핑시켜야 할것입니다.
그러면 LoadLibrary(A/W)를 직접 호출하던지 아니면 LoadLibrary처럼 그러한 매커니즘을 구현해서 올리던지 해야 하겠죠?
참고로 jongmiss님이 SetWindowsHookEx 함수의 진입부를 못찾은것은 다음과 같습니다.
윈도우에서 호환성을 높이기 위해 함수 여러가지를 멀티바이트나 유니코드에 맞게 만들었습니다.
대표적으로 MessageBoxA 이건 멀티바이트, MessageBoxW는 유니코드죠.
SetWindowsHookEx도 마찬가지입니다.
SetWindowsHookExA가 있고, SetWindowsHookExW가 있고.
실제로 OllyDbg로 Go to -> Expression -> user32.SetWindowsHookExA 또는 유니코드 버전으로 가보시면 함수의 메인부가 나옵니다. 쭉 따라서 가보면 결국
다른 함수들처럼 ntdll.dll 호출하거나 커널부로 넘겨서 처리하고 받아오겠죠?
DLL Injection을 방어하려면 공격법 하나 하나에 대응하려고 들지마시고 DLL이 삽입되었을때를 기준으로 생각해보세요.
물론 이 루틴은 보호를 받아야되겠지요.
DLL이 삽입된다면 메모리 할당영역이 새로 생기겠죠? 그리고 LoadLibraryEx로 올리지 않는이상 자동으로 DLL에 대한 DLL_PROCESS_ATTACH Flag가 적용된 스레드가 생성될 것이구요.
스레드를 감시해도 괜찮고, 마이크로소프트에서 배포하는 주요 모듈에는 전부다 사인이 들어가있는걸로 압니다. 뭐 AMD나 NVIDIA에서 제공하는 녹화기능들도 전부 그렇겠구요.
그래서 사인이되어 있지 않은 모듈들을 따로 검사하셔도 될것같고요, 정 안되시면 존재하는 DLL에 대해서 휴리스틱으로 접근해서 검사하는것도 괜찮겠죠.
A라는 DLL이 중요한 기능이 있는 B라는 모듈을 참조해서 이 영역에 어떠한 값을 쓰는 명령어가 발견되었다! 이렇다하면 감지해도되고 일정 수준이상 카운팅을해서 하는것도 괜찮을거라 봅니다.
마지막으로 짧은 견해서 한 말씀 드려보자면
정말 보안영역은 어떠한 방법이 딱 없다고 말씀드리고 싶네요.
기술이 기술을 뚫고 기술이 기술을 막고, 더 이상 서로 쓸 기술이 없어지면 정말 그때부터는 두뇌싸움으로 번지는... 어디다 트랩을 깔지 등등의 머리를 싸매고...
어쨌든 화이팅입니다.
고맙습니다.
먼저 친절한 답변 진심으로 고맙습니다.
마지막으로 하신 말씀 충분히 이해하구요.
한가지씩 방어하는법을 시작해보려고 했었던것입니다 ^^;
말씀해주신 방법중
1. DLL_PROCESS_ATTACH
2. 로드되어있는 모듈들의 코드사인 체크
3. dll 휴리스틱(?)에 대해서는 처음 들어봤네요 ^^;
으로 시도 중입니다~
다시한번 도움주신 부분 고맙습니다~!
휴리스틱은 어림짐작으로 잡아내는 기술인데 예로들어
휴리스틱은 어림짐작으로 잡아내는 기술인데 예로들어 무조건적으로
A의 DLL이 보이는데 이게 함수 ReadProcessMemory를 임포트 한다고해서 불법으로 가장할 순 없지 않겠습니까?
그래서 ReadProcessMemory의 함수가 호출되었을 때 이때 이 함수의 접근 대상이 중요한 모듈에 존재하는 영역이라면 어느정도 횟수를 카운팅해 잡거나 뭐 이런식입니다.
고맙습니다.
2번이 가장 쉬워보여서 해보았더니.. 코드사인 검증은 인터넷이 연결되어 있어야된다는 단점이 있네요 ㅋ
다른 방법으로 우회합니다.
뭐.. 변조 되지 않을 만한 데이터시스템이 있다면
뭐.. 변조 되지 않을 만한 데이터시스템이 있다면 하나 만들어서 거기에 사인정보 넣어두고 해보는것도 나쁘지 않을거라봅니다.
어떤분은 SetWindowsHookEx A W 계열로 인한 DLL Injection을 방지하려고 그 하위하위하위의 함수를 후킹하더라구요
후킹은 썩 좋은법은 아니지만 대응책으로는 충분하기에
여러가지 방법이 있으니 잘 찾아보셔요
https://warroom.securestate.com/dll-injection-part-1-setwindowshookex/
공격법을 알면 방어법이 보입니다. 위의 분 블로그에 DLL Injection에 대한 방법을 파트별로 다루는것 같으니 천천히 둘러보시는것도 괜찮을듯 합니다.
고맙습니다.
먼저 친절한 답변 진심으로 고맙습니다.
마지막으로 하신 말씀 충분히 이해하구요.
한가지씩 방어하는법을 시작해보려고 했었던것입니다 ^^;
말씀해주신 방법중
1. DLL_PROCESS_ATTACH
2. 로드되어있는 모듈들의 코드사인 체크
3. dll 휴리스틱(?)에 대해서는 처음 들어봤네요 ^^;
으로 시도 중입니다~
다시한번 도움주신 부분 고맙습니다~!
과거 고등학생 때 구현했던 것 중에, Caller
과거 고등학생 때 구현했던 것 중에,
Caller Address를 체크해서 DLL Injection을 방지하는 라이브러리를 만든 적이 있었는데,
user32.dll의 ClientLoadLibrary는 외부로 노출된(export) 함수가 아닌 내부 함수이므로 GetProcAddress API 등으로
직접 주소 값을 얻는 것은 불가능하고, 고로 user32.dll의 외부로 노출된 API 의 코드를 베이스로 해서 코드를 순회해서
ClientLoadLibrary 함수의 주소를 찾아내야 하는데 이도 쉬운 방법은 아닙니다.
(호환성을 보장하려면 2000/XP/2003/Vista/7/8/8.1/10 x86/x64 모두 테스트해봐야 하죠)
대신 ClientLoadLibrary 함수의 코드 패턴은 거의 같기 때문에
kernel32.dll의 LoadLibrary(A/W), LoadLibraryEx(A/W), LdrLoadDll 등 DLL을 로드하는 API들은 모조리 후킹한 뒤,
함수가 호출되면 함수 내부에서 Return Address 가 가리키는 코드 바이트를 읽어서 ClientLoadLibrary 패턴인지 봐서 맞으면
함수 호출을 차단하는 식으로 구현하였었는데, 이렇게 구현하시면 될 겁니다.
===
http://cafe.daum.net/codeinside
말씀해주신 부분
말씀해주신 부분 감사합니다.
ClientLoadLibrary 패턴은 대략 어떤 패턴을 말씀하시는 건가요?
그러니까 이런 겁니다. 이 컴퓨터에서
그러니까 이런 겁니다.
이 컴퓨터에서 SetWindowsHookEx에 의해 LoadLibraryExW 함수가 호출되었을 때
그 return address를 debugger로 추적한 화면입니다.
화면에서 제가 드래그해 놓은 파란 부분이 바로 return address가 가리키는 코드 부분입니다.
ClientLoadLibrary 함수의 다른 부분은 변할지 몰라도, 저 부분은 잘 변하지 않습니다.
고로, LoadLibrary 계열의 함수들(LoadLibrary(A/W), LoadLibraryEx(A/W), LdrLoadDll)을 모조리 후킹한 다음,
식으로 caller의 code bytes를 미리 찾아낸 패턴과 비교해서 감지하는 것이죠.
이 방식으로 SetWindowsHookEx 뿐 아니라 알려진 모든 DLL Injection 방식을 다 차단할 수 있습니다.
(CreateRemoteThread를 이용한 방식, Code Injection 후 injected code 내에서 LoadLibrary를 호출하는 방식 등)
Code Injection은 caller가 MEM_PRIVATE 페이지 내에 위치해 있는지를 체크해서 감지합니다.
===
http://cafe.daum.net/codeinside
설명 감사합니다. OS 별 패턴이 동일한지도 살펴볼
설명 감사합니다.
OS 별 패턴이 동일한지도 살펴볼 필요와
향후 OS에서 패턴이 변경되었을때의 리스크를 생각해봐야겠네요 ^^
몰랐던 부분을 알려주셔서 감사합니다.
브라운님과 익명님께 다시한번 감사의 말씀 전하네요.
Windows NT4, 2000, XP, 2003,
Windows NT4, 2000, XP, 2003, Vista는 제가 고등학생 때 라이브러리 개발하면서 이미 호환성을 모두 테스트해 봤으니
걱정할 필요는 없습니다.
위에 스크린샷으로 올렸던 PC는 Windows 7 32비트(x86)인데, 혹시나 XP와 코드가 다른가 해서 XP user32.dll을 다운로드 받아서
디버거로 LoadLibraryExW 호출 부분을 살펴보니
로 같네요. Windows 8/8.1/10 의 호환성은 추후 확인해 보셔야 하겠지만,
NT4 ~ 7 까지의 호환성은 걱정하지 않으셔도 될 듯합니다.
조금 정확하게 비교하려면 8B F8 3B FB 74
조금 정확하게 비교하려면 8B F8 3B FB 74 까지 비교하시면 됩니다 (false positive 방지).
JE SHORT의 operand 부분은 위 XP user32.dll 의 코드에서도 알 수 있듯이
if 문 안 쪽이 길어지면 얼마든지 쉽게 달라질 수 있는 부분이라 비교하면 안되구요. (Win7: 5A / WinXP: 1B)
댓글 달기