본문 바로가기

My Study/Reversing

DLL Injection

이번에 봐볼 내용은 DLL Injection에 대한 내용입니다. 뭐.. 
이미 DLL Injection이라는 것은 이름만 봐도 뭔지 알수 있습니다.
말 그대로 DLL을 프로세스에 Injection한다는 것입니다.
하나의 프로세스에는 여러 DLL들이 붙어있을 것입니다.

ProcessExplorer라는 툴로 한번 봐보겠습니다.
처음에 툴을 키면 해당 프로세스의 DLL목록이 안보이실 것입니다.

빨간색 네모 쳐진 부분을 클릭하면 아래쪽에 DLL view가 열릴 것입니다.

여러 프로세스가 있는데 네이트온 프로세스를 봐보겠습니다.

너무 많아서 일부만 캡쳐했습니다. 위와 같이 많은 DLL들이 붙어있는 것을 볼수 있는데 여기에 제가 만든 DLL을 Injection시킬 수 있다면?? 해당 프로세스에 만약에 제가 원하는 동작을하는 DLL을 넣는다면 해당 프로세스가 켜져있는 한 계속 제가 원하는 동작을 수행할 것입니다. ( 주로 악성코드에서 많이 등장하죠;; )

이제 어느정도 개념은 잡았으니 이제 실제로 해보겠습니다. 
시나리오..
OS키면 항상 켜져있는 프로세스들 중에 공격을 하도록 하겠습니다. ctfmon.exe 프로세스를 대상으로 하겠습니다.

ctfmon.exe에 계산기가 켜지면 cmd창을 띄우는 동작을 하는 DLL을 Injection하겠습니다.
그러면 ctfmon.exe가 켜져있는 한 계산기를 키면 계속해서 cmd창이 뜰 것입니다.

먼저 Injection할 DLL을 만들도록 하겠습니다.

소스코드는 간단합니다. 
그냥 DLL이 해당 프로세스 주소에 맵핑이 되면 calc.exe를 찾아서 존재하면 cmd창을 띄우는 소스입니다. 

제가 여기에 DLL만드는 방법에 대해서 올린적이 있는데요. 그때는 DLLMain이라는 함수를 사용하지 않았습니다.
왜냐하면 DLL은 함수들의 집합이기 때문에 함수에 대한 정의만 있으면 되기 때문이죠.
하지만 원칙적으로는 DLL도 반드시 엔트리 포인트를 가져야 합니다. 하지만 보통 Visual Studio에서 C++을 사용하면 런타임이 엔트리 포인트를 대신 제공하므로 없어도 되는 것이었습니다. DLLMain 함수는 DLL전체의 초기화와 종료 처리를 담당합니다.
운영체제는 DLL이 처음 메모리에 올라올 때뿐만 아니라 메모리에서 제거될 때도 이 함수를 부릅니다.

위에서 DLLMain함수의 두번째 인자가 중요합니다. fdwReason은 이 함수가 호출된 이유를 지정합니다.
DLL_PROCESS_ATTACK : DLL이 프로세스의 주소 공간에 맵핑될 때 호출된다.
DLL_PROCESS_DETACH : DLL이 프로세스의 주소 공간에서 분리될 때 호출된다.
보통 이 두개의 경우를 가지고 많이 사용하게 됩니다. 위에서는 첫번째 것으로 했습니다.

FindProcess() 함수는 보시면 아실것입니다. 컴파일 시키고 해당 DLL은 가지고 있겠습니다.

dll명은 Open_cmd.dll입니다.
이제 DLL은 만들었으니 ctfmon.exe프로세스에 해당 DLL을 Injection 시키는 소스코드를 만들도록 하겠습니다.

main함수 부분
GET_PID함수 소스코드는 생략하겠습니다. ctfmon.exe 프로세스가 발견되면 해당 프로세스 PID값 가져오는 함수입니다.
이제 해당 PID값과 아까 만든 DLL경로를 인자로 전달해서 호출하고 있는 DLL_Injection함수 부분을 보시겠습니다.

대충 주석으로 설명은 달아봤습니다. 다시 말해보면

1. OpenProcess로 해당 PID로부터 핸들 값을 얻어옴
2. VirtualAllocEx 함수로 대상 프로세스의 가상 주소 공간에 dwBufSize만큼 공간 할당을 함.
    이때, 메모리를 Reserve하는 것이 아니라 Commit 해야 합니다. 예약하는게 아니라 직접 쓰기 위해서죠.
    그리고 아래서 CreateRemoteThread함수로 대상 스레드를 실행시키기 위해서는 
    해당 메모리가 Read/Write 권한을 가지고 있어야 하므로 PAGE_READWRITE로 해주었습니다.
3. kernel32.dll로부터 LoadLibraryA함수의 주소를 얻어오고 있습니다. LoadLibraryA로 해당 dll을 호출하기 위함이죠.
4. CreateRemoteThread함수로 LoadLibraryA함수를 호출하고 인자로는 할당했던 가상주소를 넘겨줍니다.
   ( Open_cmd.dll 이 들어있기 때문이죠. )

윈 XP에서 OpenProcess 함수를 정상적으로 사용하기 위해서는 첫번째 인자값을 MAXIMUM_ALLOWED 로 해주세요.
만약 첫번째 인자를 PROCESS_ALL_ACCESS 로 하고
Visual Studio상에서 Crtl + F5로 실행시키면 정상 작동하지만
만들어진 exe파일을 가지고 실행해보면 OpenProcess 부분에서 막힐 것입니다. PID로 핸들 값을 못 얻어오고 있는 것을 볼 수 있습니다.

위 과정이 전부 입니다.
DLL Injection해서 동작하는 부분은 어떻게 캡쳐로 보여드리기가 힘드므로
직접 동영상을 봐보시면 어떻게 동작하는지 이해하실 것입니다.
( 동영상이 작아서 보기 답답하신 분들은 다운 받아서 봐주세요. )

실제로 해보고 싶으신 분은 해보세요 ^^

Open_cmd.dll파일은 C:\에 두셔야 합니다.


위에서 한 가지 잘못한 점이 있다면 DLL을 만들 때 제 소스코드를 보시면 아시겠지만 DLLMain에서 리턴을 하지 못하고 계속해서 동작하고 있는 것을 보실 수 있습니다. 저렇게 DLLMain에서 리턴을 하지 못하고 계속해서 돌고 있는다면 해당 DLL은 정상적으로 작동이 되겠지만 Injection된 프로세스는 작동이 멈춰버리는 것을 보실 수 있습니다.
그러므로 DLLMain 자체에서는 별다른 동작을 않하는게 좋습니다. 결국! 스레드로 구현을 해서 원하는 코드를 스레드에 넣고 동작하도록 만들어야 할 것입니다. 소스코드를 봐보면

switch( fdwReason )
{
     case DLL_PROCESS_ATTACH:
          hThread = (HANDLE)_beginthreadex(NULL,0,ThreadProc,NULL,0,NULL);
          CloseHandle(hThread);
          break;
}


위와 같이 switch에 스레드로 구현을 하셔야 되겠습니다.

DLLMain에서 절대로 하지 말아야할 12가지 작업
http://www.jiniya.net/tt/788