FileSystemFilterDriver 파일숨기기
2009/06/14 13:19

ZwQueryDirectoryFile

Window XP이후 버전 에서 폴더내 파일 또는 폴더의 정보를 얻기 위한 과정은 ZwCreateFile 또는 ZwOpenFile을 통해 얻어진 handle을 통해 ZwQueryDirectoryFile 에서 디렉토리에 대한 파일 또는 폴더 정보를 query하여 얻는다.
즉 윈도우에서 탐색기 혹은 cmd 창에서 dir 명령어도 Win32 API 에서 파일 및 폴더 정보를 얻어오는 과정과 사용된 API가 다르겠지만 결국 Native API 인 ZwQueryDirectoryFile 을 통해서 디렉토리내 파일 또는 폴더명을 얻는다는 이야기다.
따라서 파일 또는 폴더를 숨기기 위해서는 ZwQueryDirectoryFile 함수를 Hooking 하여 원하는 파일 또는 폴더를 숨길수 있다는 이야기다.

IRP_MJ_DIRECTORY_CONTROL

Hooking을 하지 않고 파일필터 드라이버에서 파일 및 폴더를 숨기는 기능을 수행하기 위해서는 아래의 IRP funtion code를 참고하면 된다.

IRP stack의 Major Function 이 IRP_MJ_DIRECTORY_CONTROL
IRP stack의 Minor Function 이 IRP_MN_QUERY_DIRECTORY

위의 두가지 Funtion code외에 FileInformationClass 를 조사하여야 한다.

IRP stack의 Parameters->FileInformationClassFileBothDirectoryInformation

대부분의 win32에서의 폴더내의 파일 및 파일 열거에 대한 호출은 FileBothDirectoryInformation(VISTA에서는 FileIdBothDirectoryInformation 로 변경)이다. 따라서 대부분 FileBothDirectoryInformation로 원하는 처리(파일 또는 풀더 숨김)가 가능하지만 보다 강력한(?) 숨기기 기능을 원하다면 FilFullDirectoryInformation, FileNameInformation, FileDirectoryInfomation class도 대한 처리도 필요하지 않을까 싶다.

전처리

원하는 조건은 다 충족되었다.
이제 파일 또는 폴더 숨기는 작업을 수행하여야 하는데 그전에 알아두어야 할 것이있다.

조건이 만족된 지금 현재 상태는 File System Driver로 IRP를 넘겨 주기 전 이기 때문에 파일 및 폴더에 어떤 정보도 알지 못한다. 따라서 지금 처리할수 있는 작업은 폴더에 대한 접근 거부(STATUS_ACCESS_DENIED) 또는 폴더내 어떤 파일도 보여주지 않는 작업(STATUS_NO_SUCH_FILE)만이 가능하다.

후처리

조건이 만족한 상태라면 파일 숨기는 기능을 처리해야 하지만 위의 전처리에서 설명했다시피 폴더 및 파일에 대한 정보를 아직 얻지 못한 상태이기때문에 어떠한 가공(?)도 할수 없다.
따라서 IoSetCompletionRoutine 을 통해 IRP가 완료한 시점에 다시 호출받도록 설정하여 File System Driver가 모든 작업을 완료하고 나서 다시 호출될수 있도록 해야 한다.
그래야만 File System Driver에서 얻어지 파일 또는 폴더에 대한 정보를 가공(?) 할수 있다.

완료루틴

IoSetCompletionRoutine 에서 설정한 대로 File System Driver에서 작업을 완료하고 완료루틴이 호출되면 원하는 파일 또는 폴더에 대한 작업을 수행한다.
파일 또는 폴더에 대한 정보는 Irp->UserBuffer 에 넘겨져 온다.
이 정보를 제대로 파악하기 위해서는 아래의 parameter를 참고해야한다.

IrpSp->Parameters.QueryDirectory.Length
버퍼의 길이 - METHOD_NEITHER를 사용하기 때문에 Irp->UserBuffer 의 크기
IrpSp->Parameters.QueryDirectory.FileName
파일 열거를 위한 매칭패턴을 위해 사용됨. 첫 호출에만 제공됨. 그 이후 NULL이면 *, *.* 패턴과 동일시 됨
IrpSp->Parameters.QueryDirectory.FileInformationClass
결과에 대한 데이타 구조체 형식, MSDN 참고
IrpSp->Parameters.QueryDirectory.FileIndex
열거가 발생할 시작 index

처음에 언급했던바처럼 FileBothDirectoryInformation 에 대한 class 만 참고하다면
pFileInfo = (PFILE_BOTH_DIR_INFORMATION) Irp->UserBuffer 와 같은 casting 을 통해 pFileInfo 를 통해 파일 정보를 구할수 있다.(PFILE_BOTH_DIR_INFORMATION 는 각각의 파일에 대한 정보를 담고 있으며 NextEntryOffset으로 다음 정보에 대한 offset값을 가지고 있다.)
파일정보에서 특정파일을 숨기기 위해서는 NextEntryOffset 값을 건너 뛰도록 offset 값을 조작하여 파일을 숨긴다.

주의해야 할점이라면 버퍼의 용량이 그다지 크지 않기 때문에 특정 폴더에 대한 query 시 폴더내 모든 파일(폴더)의 정보가 한번에 넘어오지 않는다. (경험상 약 30개 파일의 정보 단위로 넘어오는듯)
한 폴더에 약 100개의 파일이 존재한다면 30개씩 약 4번에 걸쳐 모든 정보가 넘어온다.

또한 Irp->Flags parameter도 참고하여야 한다.

SL_RESTART_SCAN 첫번째 부터 스캔
SL_RETURN_SINGLE_ENTRY 한개만 스캔
SL_INDEX_SPECIFIED FileIndex 에서 부터 스캔

FindFirst/FindNext 과 같은 win api에서는 flags가 SL_RETURN_SINGLE_ENTRY 가 설정되어 파일이 한개만 조사되기 때문에구조체에 한개 파일의 정보만 담겨진다.
따라서 NextEntryOffset 값을 조작하는데 유의해야한다. 이는 여러개의 파일정보가 있으나 숨기고자 하는 파일정보가 마지막에 위치하였을때도 마찬가지다.
(편법으로 파일이름을 . 으로 대체하기도 했음)

code snippet

FileInformationClass 에 따라 FileName 및 FileNameLength 얻기
[code c++]
switch(irpsp->Parameters.QueryDirectory.FileInformationClass)
{
 case FileBothDirectoryInformation:
  p = ((PFILE_BOTH_DIR_INFORMATION)buf)->FileName;
  len = &(((PFILE_BOTH_DIR_INFORMATION)buf)->FileNameLength);  
  sp = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortName;  
  slen = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortNameLength;  
  break;  
 case FileFullDirectoryInformation:   
  p = ((PFILE_FULL_DIR_INFORMATION)buf)->FileName;  
  len = &(((PFILE_FULL_DIR_INFORMATION)buf)->FileNameLength);  
  break;  
 case FileDirectoryInformation:   
  p = ((PFILE_DIRECTORY_INFORMATION)buf)->FileName;  
  len = &(((PFILE_DIRECTORY_INFORMATION)buf)->FileNameLength);  
  break;  
 case FileIdBothDirectoryInformation:  
  p = ((PFILE_ID_BOTH_DIR_INFORMATION )buf)->FileName;  
  len = &(((PFILE_ID_BOTH_DIR_INFORMATION )buf)->FileNameLength);  
  sp = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortName;  
  slen = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortNameLength;  
  break;
...
}
[/code]

2009/06/14 13:19 2009/06/14 13:19
Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • TWEETY 2009/06/29 10:29  댓글주소  수정/삭제  댓글쓰기
    그림이 안보여~~
  • 오곡 2013/07/04 10:57  댓글주소  수정/삭제  댓글쓰기
    잘배우고 갑니다 ^^

  • STATUS_MEDIA_WRITE_PROTECTED
    2008/12/05 09:29
    파일 필터 드라이버 기능중에 이동저장장치(외장하드, USB저장장치등)에 쓰기금지기능이 있다.
    이동저장장치에서 자유롭게 읽기는 가능해도 쓰기는 안되게 하는 기능인데...
    이 기능이 다른 프로그램에서는 제대로 동작하는 한글2007에서는 원본파일을 강제로 2KB로 만들어 버린다.

    코드를 살펴보니 IRP_MJ_WRITE시 STATUS_ACCESS_DENIED를 리턴하여 쓰기금지 기능을 수행하고 있다.
    대체로 양호하게 동작을 수행하는데 유독 한글2007에서만 파일이 깨진다.

    Filemon, Filespy로 IRP를 들여다 봐도 별반 차이가 없고 왜 그럴까 하던차에 SD 메모리카드에 lock기능이 있길래 lock으로 설정을 하고 쓰기기능을 수행한 결과.

    눈에 띄는게 IRP_MJ_SET_INFORMATION의 FileEndOfFileInformation 일때STATUS_MEDIA_WRITE_PROTECTED 값을 리턴한다.

    MSDN에 이런 내용이 있다.

    FileEndOfFileInformation
    This is called when someone is trying to change the logical size of the stream. Note that when the cache manager’s lazy writer thread attempts to set a new end of file no oplock check is made. This is because the check will have been made previously when the real write request came in.

    결국 이동저장장치에 쓰기금지를 하려면
    일반적으로 IRP_MJ_WRITE에서 막으면 되겠지만 프로그램에 따라(특히 한글2007 같이 cache를 사용한다면)...
    IRP_MJ_SET_INFORMATION(FileEndOfFileInformation)에서도 알맞는 처리를 해줘야 한다.

    NTSTATUS.H를 참조해서 STATUS_ACCESS_DENIED를 하던지...STATUS_MEDIA_WRITE_PROTECTED를 하던지 취향에 맞게 하면 되겠다.
    2008/12/05 09:29 2008/12/05 09:29
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • PROpecia 2011/09/14 04:19  댓글주소  수정/삭제  댓글쓰기
    좋은 정보 감사! ㅎㅎ
    즐거운 하루 되세요~

  • 비주얼스튜디오 단축키 - 정의바로가기를 되돌리기 CTRL+*
    2008/09/25 08:39
    코드를 분석하다가 보면...
    특정 함수나 변수명의 선언이나 정의를 보고 싶을때...
    무심코 F12(CTRL+F12) 또는 마우스 오른쪽을 눌러 "선언으로 이동" 또는 "정의로 이동"를 사용한다.
    그런데 문제는 대충 함수(변수)의 정의나 선언을 살펴보고 바로 이전으로 되돌아 가고 싶은데 북마크나 보고있던 부분의 line의 줄수를 특별히 기억해 두고 있지 안았다면 낭패
    늘 어디더라 하면서 찾기가 일쑤였는데...

    그럴땐 CTRL+*(숫자패드의 *)를 눌러보자.
    그럼 이전 코드부분으로 되돌려진다.
    아마도 정의 또는 선언으로 이동할때 위치를 스택에 저장하는듯 하다.

    나도 어디선가 주워들은 내용인데 MSDN이며 구글링을 해도 찾지를 못하겠다.
    회사 동료가 보다가 신기해 하길래 포스팅.

    더불어 가급적 마우스를 안쓰고 키보드로 해결하려고 노력중인 단축키들...

    블럭 주석달기
    CTRL+K, CTRL+C 주석 블럭 설정
    CTRL+K, CTRL+U 주석 블럭 해제

    마우스로 상단 파일이동이 아닌 키보드로 파일간 이동
    CTRL+TAB, CTRL+SHIFT+TAB

    중간중간 분석한 부분을 잊어버리지 않기 위해 북마크(가끔은 브레이크 포인터도 쓴다는...)
    F2, CTRL+F2

    창이동
    ALT+0 소스코드창으로
    ALT+2 출력창으로
    가장 필요한 찾기창은 CTRL+TAB으로 대신....(이거 단축키는 없나?)

    { } 짝 찾기
    CTRL+]

    ps.
    디버깅시 실시간으로 GetLastError 값알아내기(이것도 회사동료가 신기해 하던...Debugging Applications 책 참조)
    조사식(watch)창의 이름(Name)에 @ERR 값 입력
    2008/09/25 08:39 2008/09/25 08:39
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • 구루마루 2008/11/26 11:40  댓글주소  수정/삭제  댓글쓰기
    처음 들르네요 ^^;
    Ctrl + *는 처음 보는군요. (함 눌러봐야지~)
    전 주로 이전 위치 이동에 Ctrl + - 를 씁니다. Ctrl + Shift + - 하면 다시 현재 위치로 돌아오구요.
    Ctrl + ] 는 #if ~ #endif 구문에도 적용되더군요.
    • hongyver 2008/11/26 16:43  댓글주소  수정/삭제
      지금 해봤더니 CTRL - 도 꽤 유용하군요.
      그런데 VS2008에서만 동작하는군요. VS6에서는 안돼요 ㅜㅜ
    • 구루마루 2008/12/02 08:42  댓글주소  수정/삭제
      VS6에서는 안되는가요? 제가 2003 부터 쓰기 시작해서 ^^;;
      VS .net 2003, VS 2005, VS 2008에서는 공통으로 먹히는 키랍니다.
  • 지나가다가 2009/01/30 04:00  댓글주소  수정/삭제  댓글쓰기
    Ctrl+* 최고네요! 찾기 창은 뭘 말하는지 정확히 모르겠으나
    Ctrl+d나 Ctrl+h, Ctrl+f 등을 많이 씁니다. Ctrl+d를 한 뒤에는 f3, shift+f3
  • DoubleJ 2009/04/19 13:21  댓글주소  수정/삭제  댓글쓰기
    이렇게 편한 단축키가 있었네요..^^
    저도 정의로 이동 한다음 한참 찾았었는데 잘 읽고 갑니다.^^