심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  

 

어떤 보안 프로그램( nProtect, 키보드보안 ) 이나 SPTD를 사용하는 가상 시디 소프트웨어 ( Daemon, Alchol 120% ) 에 경우에는 드라이버가 설치되어 있지만 윈도우즈 탐색기에 \\system32\\drivers\\에 가보면 해당 드라이버 파일들이 존재하지 않는다는 것을 알 수 있습니다.

데몬을 예로 들자면 다음과 같이 설치가 완료되면 장치 관리자에 저장소 컨트롤러가 추가되게 됩니다.


b0072382_4971b1f3078e5.png 


그러나 그림에서 보는 경로에 파일은 존재하지 않으며 gmer과 같은 루트킷 체크프로그램으로 파일 시스템을 직접적으로 읽어도 파일이 존재하지 않습니다.

이러한 방식은 드라이버 파일을 숨기는 방식이 아니며 실제로 하드 디스크에 존재하지 않는 경우입니다.
드라이버를 로드하면 로드 후 바로 파일을 하드 디스크상에 삭제함으로 메모리상에만 존재하게 되는 것입니다.

이렇게 드라이버 파일을 바로 삭제하는 이유는 리버스 엔지니어링을 방지하기 위함인데
이러한 경우 어떻게 해당 드라이버 파일을 가져올 수 있는지에 대한 방법에 대해 소개할까합니다.

사실 방법은 매우 간단하며 다음과 같은 시스템에서 제공해주는 함수를 이용하면 쉽게 드라이버 파일인 .sys를 흭득 할 수 있습니다.


PsSetLoadImageNotifyRoutine

NTSTATUS
  PsSetLoadImageNotifyRoutine(
    IN PLOAD_IMAGE_NOTIFY_ROUTINE  NotifyRoutine
    );


훅킹쪽에 관심이 있으신 분이라면 거의 알만한 함수죠..
이 함수는 하나의 콜백함수를 인자로 받으며 이 콜백함수는 "시스템에서 어떠한 모듈이 로드 될 때 ( Dll, Sys )" 호출되게 됩니다.

이쯤이면 거의 답이 나왔다시피 합니다.

한 번 실제 구현코드를 보겠습니다.


void CallBackLoadImageNotifyRoutine(__in PUNICODE_STRING FullImageName, __in HANDLE ProcessId, __in PIMAGE_INFO ImageInfo)
{
    HANDLE hReadHandle;
    HANDLE hWriteHandle;
    CHAR pFileBuffer[1024];
    NTSTATUS ntStatus;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES ObjectAttributes;
    LARGE_INTEGER Offset;
    UNICODE_STRING uniWritFileName;
    KdPrint(("모듈이 로드되었습니다. : %ws ( Irql : %#x )\n", FullImageName->Buffer, KeGetCurrentIrql()));
    // 드라이버의 파일은 확장자가 .sys 이므로 확장자가 s로 끝나는 이름이 있는지 확인합니다.
    if ((FullImageName->Buffer[wcslen(FullImageName->Buffer) - 1] == 's')
        || (FullImageName->Buffer[wcslen(FullImageName->Buffer) - 1] == 'S'))
    {
        // 드라이버 파일이 로드되었습니다.
        InitializeObjectAttributes(&ObjectAttributes, FullImageName, OBJ_CASE_INSENSITIVE, NULL, NULL);
        ntStatus = ZwCreateFile(&hReadHandle,
                                GENERIC_READ | SYNCHRONIZE,
                                &ObjectAttributes,
                                &IoStatusBlock, NULL,
                                FILE_ATTRIBUTE_NORMAL,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                FILE_OPEN,
                                FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                                NULL,
                                0);
        if (NT_SUCCESS(ntStatus) == FALSE)
        {
            KdPrint(("파일을 생성하지 못 하였습니다.\n"));
            return ;
        }
        else
        {
            Offset.LowPart = 0;
            Offset.HighPart = 0;
            RtlInitUnicodeString(&uniWritFileName, L"\\??\\C:\\Output.Sys");
            InitializeObjectAttributes(&ObjectAttributes, &uniWritFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
            ntStatus = ZwCreateFile(&hWriteHandle,
                                    GENERIC_WRITE | SYNCHRONIZE | FILE_APPEND_DATA,
                                    &ObjectAttributes,
                                    &IoStatusBlock,
                                    NULL,
                                    FILE_ATTRIBUTE_NORMAL,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE,
                                    FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                                    NULL,
                                    0);
            if (NT_SUCCESS(ntStatus) == FALSE)
            {
                KdPrint(("쓰기 파일을 생성 할 수 없음 %#x\n", ntStatus));
                ZwClose(hReadHandle);
                return ;
            }
            while (TRUE)
            {
                ntStatus = ZwReadFile(hReadHandle, NULL, NULL, NULL, &IoStatusBlock, pFileBuffer, 1024, &Offset, NULL);
                if((NT_SUCCESS(ntStatus) == FALSE) || (IoStatusBlock.Information != 1024))
                {
                    ntStatus = ZwWriteFile(hWriteHandle, NULL, NULL, NULL, &IoStatusBlock, pFileBuffer, IoStatusBlock.Information, NULL, NULL);
                    if (NT_SUCCESS(ntStatus) == FALSE)
                    {
                        ZwClose(hWriteHandle);
                        ZwClose(hReadHandle);
                        return ;
                    }
                    KdPrint(("루프를 빠져나갑니다.\n"));
                    // 파일을 모두 작성하였으니 루프를 빠져나갑니다.
                    ZwClose(hWriteHandle);
                    ZwClose(hReadHandle);
                    break;
                }
                else
                {
                    ntStatus = ZwWriteFile(hWriteHandle, NULL, NULL, NULL, &IoStatusBlock, pFileBuffer, 1024, NULL, NULL);
                    if (NT_SUCCESS(ntStatus) == FALSE)
                    {
                        KdPrint(("쓰기 실패...\n"));
                        ZwClose(hWriteHandle);
                        ZwClose(hReadHandle);
                        return ;
                    }
                }
                Offset.LowPart += 1024;
            }
            KdPrint(("완료...\n"));
        }
    }
}

void Hook()
{
    if (NT_SUCCESS(PsSetLoadImageNotifyRoutine(CallBackLoadImageNotifyRoutine)) == TRUE)
    {
        KdPrint(("성공...\n"));
    }
    else
    {
        KdPrint(("실패...\n"));
    }
}

Hook() 이라는 함수는 DriverEntry와 같은 함수에서 호출하거나 또는 다른
PASSIVE_LEVEL에서 호출하면 됩니다.

콜백함수에서는 파일의 이름에서 끝에 이름이 'S' 또는 's'일 경우라면 "Output.sys"라는 파일로 "C:\" 루트에다가 드라이버 파일을 생성하게 됩니다.

실제로 이 드라이버 파일을 컴파일하고 로드를 미리 시켜놓은 다음에 데몬을 설치하고 설치가 완료되면 가상 장치 업데이트
창에서 드라이버를 로드하게 되는데 다음과 같이 DbgView에서 드라이버 파일이 로드됨을 볼 수 있습니다.


b0072382_4971b1f3078e5.png 

완료라는 메세지가 출력되고 C드라이브에 가보면 다음과 같이 Outpup.sys가 생성되게 됩니다.


b0072382_4971b1f3078e5.png 


국내에 보안 프로그램들도 위에서처럼 드라이버를 숨기게 됩니다.
밑에 그림은 특정 은행에 들어갔을 때 키보드 보안 프로그램을 설치하게 되는데 이 때 로드 된 드라이버입니다.


b0072382_4971b1f3078e5.png 

결론은 드라이버가 로드되기전에 PsSetLoadImageNotifyRoutine를 이용하면 로드 후 바로 삭제되는 로딩방식의 드라이버 파일을
가져올 수 있다는 것입니다.


Lyn

2010.09.04 01:52:42

헐.. 로드 했다는건 핸들 잡고있는거 아닌가요 ㅡ,.ㅡ? 왜 삭제가 되는거지 ;;

profile

lain

2010.09.04 09:33:46

EXE, DLL같은 경우와 다르게 드라이버파일에 경우에는 삭제가 됩니다.ㅎㅎ


제 개인적인 생각에는 EXE같은 타입에 파일이 삭제가 되지 않는 경우는 EXE가 가상메모리에 매핑이 될 때

윈도우즈 내부적으로 EXE 파일자체를 Memory Mapped File로 사용하기 때문에 그럴까 합니다.

관련 내용은 Windows 실행파일의 구조와 원리라는 책에 43페이지부터 잘 나와있습니다.


sys파일에 경우에는 커널모드에서 실행되고 EXE처럼 PASSIVE_LEVEL에서만 실행되는게 아니라

페이지아웃이 되면 안되는 경우도 있기 때문에 물리적인 메모리에 다 올리는 것 같습니다.

Lyn

2010.09.04 11:36:55

흠. 드라이버는 전부 NonPaged pool 에 올라가는건가요.


드라이버 많이깔면 성능에 영향미칠수도 있겠네요 `-`;

profile

lain

2010.09.04 12:01:14

페이지 아웃되는 영역도 있기 때문에 Paged Pool에도 들어갈 수 있습니다.

물론 이런 경우에도 드라이버 파일은 삭제가 됩니다.

처음에 로드 될 때 어떠한 상황이라도 완전히 물리 메모리에 올라가는지에 대해서는

저도 정확하게 확인해보질 않아서 잘은 모르겠습니다.emoticon

profile

esniper

2010.09.05 22:07:51

최근 바이러스들은 드라이버로 올려놓고 원본 파일은 삭제 되버리니깐 엄청 골치아픈데

그런것들만 따로 모아서 보여주면 편하겠네~

List of Articles
번호 제목 글쓴이 날짜 조회 수
163 Linux Tip 리눅스 기반 오픈 소스 사이트 모음 삽질신 2009-11-16 70583
162 Linux Tip VirtualBox에 우분투 설치 후에 내부 네트워크 접속 설정하기 file esniper 2010-09-10 35154
161 Windows Research IE Cache 경로 변경하기 lain 2011-06-09 30650
160 Windows Research VMWare 탐지 기법 우회 - 2 [2] lain 2011-04-04 28631
159 Windows Research 화면캡쳐방지는 어떻게 구현될까? [3] lain 2010-09-04 28412
158 PHP Tip php 파싱 라이브러리 - snoopy esniper 2009-03-26 20572
157 Linux Tip Directory index forbidden by Options directive 삽질신 2009-11-16 20108
156 Linux Tip VirtualBox에서 오른쪽 CTRL키 사용하기(VitualBox 호스트키) file [2] esniper 2010-09-10 19534
155 Windows Tip SSH 터널링 - putty의 plink 사용예 esniper 2009-03-26 19429
154 문서자료 고품질의 무료 아이콘들 [3] esniper 2010-09-08 19301
153 문서자료 json 문법 체크 사이트 Lyn 2012-02-28 18735
152 PHP Tip mysql_insert_id() - 마지막 insert 된 아이디 값 esniper 2009-03-26 18271
151 Linux Tip 우분투 설치 후 putty에서 한글 안 깨지도록 설정하기 file esniper 2010-09-10 16999
150 Linux Tip 우분투 ssh or mysql 서버 접속지연이 있는 경우 해결책 esniper 2010-09-10 16370
149 Windows Research 커널레벨에서 IAT Hook lain 2011-06-11 16297
148 Linux Tip 우분투에 IRC서버 설치후 닉네임 길이와 동접자 조절. [1] 오랑캐꽃 2010-11-26 15918
147 Windows Research 유저 레벨에서 API Hook 1부 file [1] lain 2010-09-03 15680
146 Windows Research SetEvent 함수는 언제 실패할까? lain 2010-09-04 15596
145 Windows Research Sysinternals - Filemon source code file [5] lain 2010-09-04 15334
144 Windows Research Sysinternals - Regmon source code file [1] lain 2010-09-04 14920

  • 이용약관
  • 개인정보취급방침
  • 사이트맵