내가 만든 DLL Injector로 실제 API 후킹 해보기

#REVERSING#C-Family

ws2_32.dll의 WSASend 함수를 후킹하여 패킷 로그 남기기


이전글에서 GUI 기반 DLL Injector를 구현해 봤다. 이번엔 후킹에 사용될 DLL을 수정하여 실제로 API 후킹을 해 보았다.

시행착오

내가 후킹할 APIws2_32.dllsend함수였다.

애플리케이션 단에서 리버싱 기술을 토대로 패킷을 모니터링하는 기본은 ws2_32.dllsend/recv 함수를 함수하는 기법이다. 이를 통해 내 네트워크에서 들어오고 나가는 패킷을 모니터링할 수 있다.

몇달 전에 C# WPF를 이용하여 만들어 둔 TCP/IP 채팅 프로그램이 있었다.

그래서 이 채팅 클라이언트 프로그램에 ws2_32.dllsend함수를 후킹하는 DLL을 주입하는 실습을 하고 싶었다.

후킹에 사용될 DLL은 send함수를 후킹하여 send함수 호출 이전에 OutputDebugString를 우선 호출하여 DebugView로그를 남기도록 만들었다.

어제 거의 하루종일 시도했지만 실패했다. DLL 주입은 성공적으로 되었지만 채팅을 보낼 때 OutputDebugString을 호출을 안한다...

근데 또 이상한게 클라이언트 프로그램 말고 서버 프로그램에 DLL 인젝션을 하면 API 후킹이 정상적으로 되어 OutputDebugString을 호출을 정상적으로 한다.


해결법

결국 정답은 스택오버플로우에서 찾았다. 서버 프로그램에서 채팅을 클라이언트로 보낼때는 GetStream().Write 함수를 사용했다. 이 함수는 ws2_32.dllsend함수를 사용하는 것이 맞다. 그래서 서버 프로세스에 DLL 인젝션을 하면 후킹이 잘 됐었다.

그런데! 클라이언트 프로그램에서 서버로 채팅을 보낼때는 GetStream().WriteAsync를 사용했는데 이 함수는 비동기 작업으로 ws2_32.dllWSASend 함수를 사용한다는 것이다!


naked는 64비트에서 불가

처음에 시도한 후킹법은 코드 후킹으로 GetProcAddress()를 이용하여 WSASend함수 주소를 찾은 다음

디버거로 적당한 jmp문을 정하고 해당 점프문을 내가 원하는 함수로 점프하는 점프문으로 덮어 씌우려고 했다.

이때 내가 원하는 함수는 _declspec(naked)asm으로 어셈블리로 직접 작성하려고 했다.

그런데 naked가 x86 및 ARM에서만 유효하고 x64에서는 사용 못하는 것을 MSDN을 통해 이번에 처음 알았다.


MinHook?

그래서 새롭게 찾은 후킹법은 라이브러리를 이용하는 것이였다. MinHookDetours 두 라이브러리가 가장 인기가 많은데 MinHook의 평이 더 좋은 거 같아 MinHook을 사용했다. 실제로 써보니 함수들이 직관적이라서 좋았다.

MinHook은 오픈소스로 깃허브에서 다운받아 사용할 수 있다.

MinHook을 이용해서 후킹에 사용될 DLL을 만들었다. 전체 코드는 여기


성공

먼저 타켓 EXE의 이름과 DLL 코드를 빌드한 후 생성한 DLL을 DLL 인젝터에 넣고 DLL을 주입한다. dll_injector

프로세스 탐색기에서 DLL이 성공적으로 주입된 것을 확인할 수 있다. process_explorer

다음으로 채팅 클라이언트에서 채팅을 보내면 내부적으로 WSASend 함수를 사용하기 전에 OutputDebugString를 호출할 것이다. chat

DebugView에서 OutputDebugString를 호출한 흔적을 확인할 수 있었다. 이로써 DLL 인젝션을 이용한 API 후킹에 성공했다! debugView