본문 바로가기
개발/Windows

Windows 드라이버 커널모드에서 파일 해시 구하기

by lucidmaj7 2020. 7. 23.
728x90
반응형

일반적으로 보안 프로그램에 쓰이는 디바이스드라이버들은 파일이벤트, 레지스트리, 프로세스이벤트 등을 탐지하여 자신이 가지고 있는 정책에 따라 차단을 하거나 로깅을 합니다. 특히 특정 파일이나 프로그램을 찾기위해서 파일 해시를 구하는 경우가 많습니다.
파일 해시를 구하기 위해서는 암호화 라이브라리 등을 사용하여 유저영역에서 구하는 경우가 일반적입니다.
하지만 이벤트를 유저모드 애플리케이션에 통지하고 해시를 구하고, 다시 차단하는 로직을 수행하면서 컨텍스트 스위칭이 잦아지는 문제점이 있습니다.

파일 해시를 커널모드에서 구해서 필터링이 가능할까?

이러한 의문점에 검색을 해보니 많은 내용이 검색되지는 않았지만 단서가 될만한 내용을 다음 사이트에서 찾을 수 있었습니다.

http://www.rohitab.com/discuss/topic/41394-calculate-md5-hash-of-file-in-kernel-mode/

위 페이지에서는 md5를 커널모드에서 구할 수 있냐 에 대한 문제에 대한 주제로 논의하고 있는 글입니다.

md5는 rfc1321(https://tools.ietf.org/html/rfc1321)에 나와있는 내용으로 비교적 구현이 간단하여 복잡한 암호화 라이브러리를 사용하지 않고 많은 사람들이 직접 구현하여 쓰기도 하였습니다. 마찬가지로 위 페이지에서도 C로 짜여진 MD5 함수를 커널모드 코드로 포팅하여 예제코드를 올려둔 것을 볼 수 있었습니다.

예제 코드

다음과 같이 단순히 커널모드 예제코드를 작성해 보았습니다. 

아래 예제코드는 c:\test2.txt라는 파일을 읽어 md5해시를 구해줍니다.

#include <ntifs.h>
#include <wdm.h>

#include "md5.h"

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    md5_state_t state;          /* MD5 state info */
    md5_byte_t  sum[16];        /* Sum data */
    char mmd5[33];
    NTSTATUS status;
    UNICODE_STRING fileNameU;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE fileHandle;
    RtlInitUnicodeString(&fileNameU, L"\\??\\C:\\test2.txt");
    IO_STATUS_BLOCK ioStatusBlock;
    unsigned char readBuffer[4096];
    UNREFERENCED_PARAMETER(readBuffer);
    UNREFERENCED_PARAMETER(state);

    DriverObject->DriverUnload = MyUnload;
    InitializeObjectAttributes(&objectAttributes, &fileNameU, OBJ_CASE_INSENSITIVE, NULL, NULL);

    status = ZwCreateFile(&fileHandle,
        FILE_READ_ATTRIBUTES | GENERIC_READ | SYNCHRONIZE, 
        &objectAttributes, 
        &ioStatusBlock, 
        NULL, 
        FILE_ATTRIBUTE_NORMAL, 
        FILE_SHARE_READ, 
        FILE_OPEN,
        
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, 
        NULL, 
        0);

    if (!NT_SUCCESS(status))
    {
        DbgPrint("MD5 ZwCreateFile error\n");
        return STATUS_SUCCESS;
    }
    DbgPrint("MD5 Init\n");
    md5_init(&state);
    DbgPrint("MD5 Append\n");

    memset(readBuffer, 0, 4096);

    while (1) {
        status = ZwReadFile(fileHandle, NULL, NULL, NULL, (PVOID)&ioStatusBlock, (PVOID)&readBuffer, 1024, NULL, NULL);
        if (NT_SUCCESS(status)) {
            md5_append(&state, (unsigned char*)readBuffer, ioStatusBlock.Information);
            memset(readBuffer, 0, 4096);
           
        }
        else 
        {
            break;
        }
    }
    
    DbgPrint("MD5 Finish\n");
    md5_finish(&state, sum);
    DbgPrint("Attempting to print Checksum\n");
    ZwClose(fileHandle);
    httpMD5String(sum, mmd5);

    KdPrint(("%s\n", mmd5));

    return STATUS_SUCCESS;
}

md5를 구하는 함수들을 따로 c와 h파일로 묶어 첨부합니다.

md5.zip
0.00MB

다음은 md5.h의 내용입니다.

#pragma once
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */

/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
    md5_word_t count[2];    /* message length in bits, lsw first */
    md5_word_t abcd[4];     /* digest buffer */
    md5_byte_t buf[64];     /* accumulate block */
} md5_state_t;


static void
md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/);

void
md5_init(md5_state_t* pms);
void
md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes);

void
md5_finish(md5_state_t* pms, md5_byte_t digest[16]);



char*                  /* O - MD5 sum in hex */
httpMD5String(const md5_byte_t* sum,    /* I - MD5 sum data */
    char             md5[33])   /* O - MD5 sum in hex */;

 

실행 결과

드라이버를 시작하면 DriverEntry에서 주어진 파일의 해시를 구합니다. 실행결과 정상적으로 해시가 구해지는 것을 볼 수 있었습니다.

 

 

728x90
반응형

댓글