Redirect 라고 하는 것은 어떤 프로세스가 A에 접근하려고 할 때 이것을 B로 방향을 틀어 버리는 것입니다. 결과적으로 그 프로세스는 자기가 A에 접근하고 있다고 생각하지만 실제로는 B에 접근하는 결과가 되지요. 

필요에 따라서 File I/O 를 리다이렉트할 수도 있고, 레지스트리를 리다이렉트할 수도 있고, 네트워크 패킷이나 접속하려는 URL을 리다이렉트할 수도 있습니다. 이러한 리다이렉트는 주로 가상화 제품에서 많이 사용되는 기술들입니다.


URL Redirect를 제외한 다른 리다이렉션들은 보통 api를 후킹해서 구현하는 경우가 많지만, 정규적인 방법으로도 리다이렉트할 수 있는 것들이 많이 있습니다. 그 중 대표적인 것이 파일 I/O 인데요... 정규적인 방법으로 가능하다면 정규적인 방법을 이용하는 것이 정석이겠죠? 


오늘은 simrep라는 DDK 샘플코드를 통해 파일 I/O를 리다이렉트하는 방법을 살펴보고자 합니다.

사실 simrep는 파일 I/O 를 리다이렉트하는 기본적인 방법을 설명하고 있지만, 중요한 한가지가 빠져 있기 때문에 그 코드를 제품에서 사용하기에는 부족함이 있습니다. 살펴보는 김에 이 부족한 점이 무엇인지, 그리고 어떻게 해야 이 문제를 극복할 수 있는지도 알아보겠습니다.



I. File I/O를 리다이렉트하는 방법

File I/O를 리다이렉트하는 건 원래 파일시스템에서 제공하는 기능이기 때문에 구현도 상당히 간단합니다. 

  1. Redirect 대상 I/O인지 판단. (PreCreate 혹은 PostCreate에서만 가능)

  2. Redirect 대상인 경우 FileObject의 FileName을 Redirect시킬 대상 경로로 교체

      Vista 이후인 경우 IoReplaceFileObjectName 사용하시면 되구요.... 

      XP인 경우 SimRep에 있는 SimRepReplaceFileObjectName 함수 사용

  3. PFLT_CALLBACK_DATA 의 처리상태에 다음과 같이 설정한 후 종료 (Complete)

          (이렇게 하면 FileObject가 Reparse되어 PreCreate Callback부터 다시 시작됩니다. ^^)

Cbd->IoStatus.Status = STATUS_REPARSE;  

Cbd->IoStatus.Information = IO_REPARSE; 


파일 I/O를 Redirect하는 샘플코드가 바로 simrep 인데요... DDK 7600 버젼 이후에 포함되어 있구요.. 다음의 위치에서 다운로드받을 수도 있습니다.

http://code.msdn.microsoft.com/windowshardware/SimRep-File-System-9a4e2206


simrep 샘플 코드의 PreCreate Callback 코드를 간단히 요약하면 다음과 같습니다. 
원래 주석이 잘 달려 있지만... 제가 우리말로 다시 달아놨으니 대충 읽어보면 이해가 가실 겁니다. ㅋㅋㅋ



IIsimrep 실행해 보기

File I/O를 Redirect한다는 것이 어떤건지 이해하려면... 일단 무턱대고 simrep를 빌드해서 돌려보시면 됩니다


일단 simrep 를 빌드해서 inf를 이용해 설치하면.. 레지스트리에 아래와 같이 NewMapping / OleMapping 이라는 값이 생성됩니다.

바로 \x\y 라는 경로가 들어있으면 \a\b로 Replace하라는 설정이 되겠습니다. 즉, \x\y가 \a\b로 Redirect되는 것이죠.


그렇다면.. C:\a\b 와 C:\x\y 라는 두 폴더를 생성한 후 simrep를 start시킵니다... 



simrep를 시작시킨 후 탐색기를 새로고침하면 아래와 같이 두 폴더에 똑같은 내용이 나타나는 것을 볼 수 있습니다. (정확히 말하면... C:\a\b 의 내용이 C:\x\y에서 똑같이 보이는 거구요... C:\x\y에 원래 있던 파일이나 폴더 들은 가려져서 안보이게 됩니다. ^^)


이 상태에서 \a\b에 엑셀파일을 새로 하나 만들어 볼까요?


아항... \x\y 에도 똑같은 파일이 만들어졌습니다.


그러면 이번엔 \x\y 에 있는 .pptx 문서를 하나 열어보겠습니다.


네... 마치 문서가 진짜로 거기 있는 것 처럼 잘 열립니다.


어렵쇼...? 그런데... 문서를 저장하려고 하니... 문제가 생깁니다.


Redirect된 폴더에서 문서를 여는것은 잘 되는데... 수정해서 저장은 안되는군요.


이모저모로 테스트를 좀 해 보면... 아래와 같이 Rename과 관련된 문제라는 것을 알수 있습니다.


이 문제가 왜 발생하는지 알아보고... 해결 방법도 살펴보도록 하겠습니다.



III. simrep 의 문제점 수정하기


이 문제를 이해하려면.. 파일시스템이 Rename Operation을 처리하는 방식을 이해해야 하는데요...

일단... 다음의 포스트를 읽어보신 다음에 계속 진행하시기 바랍니다. ^^;;

http://kuaaan.tistory.com/431


이 문제의 현상은... Rename 과정에서 SL_OPEN_TARGET_DIRECTORY 가 설정된 IRP가 발생하는데, 이 IRP의 FileObject를 FltGetFileNameInformation 하는 과정에서 FinalComponent가 제거되어 버리고, 이 상태에서 UNICODE_STRING을 Replace하기 때문에 발생하는 문제입니다.


예를 들어 살펴볼까요?


\a\b\ 를 \x\y\ 로 리다이렉트하는 경우를 PseudoCode로 표현해보면, 일반적인 경우에는 

1. FileObject는 "\x\y\test.txt" 를 가리키고 있음.


2. FileObject에서 파일경로를 문자열로 추출한다.  

    -. FltGetFileNameInformation (FileObject) ==> PathString = "\x\y\test.txt"


3. 추출된 경로 문자열을 기반으로 Redirect 대상 I/O인지 판단해서 경로 문자열을 Replace 조작한다.

    -. ReplaceUnicodeString(PathString, "\x\y", "\a\b"); ==> 결과 : "\a\b\test.txt"


4. 수정된 경로 문자열을 FileObject에 다시 Set한 후 STATUS_REPARSE를 리턴한다.

    -. IoReplaceFileObjectName(FileObject, PathString);  


※ 결과적으로  \x\y\test.txt 는 \a\b\test.txt로 Redirect됨.


이렇게 되겠지만... SL_OPEN_TARGET_DIRECTORY 가 설정된 경우엔 예상과 다르게...

1. FileObject는 "\x\y\test.txt" 를 가리키고 있음.


2. FileObject에서 파일경로를 문자열로 추출한다.  

    -. FltGetFileNameInformation (FileObject) ==> PathString = "\x\y"  // SL_OPEN_TARGET_DIRECTORY 때문에 

                                                                             // "\x\y\test.txt"이 아닌  L"\x\y"이 됨.


3. 추출된 경로 문자열을 기반으로 Redirect 대상 I/O인지 판단해서 경로 문자열을 Replace 조작한다.

    -. ReplaceUnicodeString(PathString, "\x\y", "\a\b"); ==> 결과 : "\a\b"


4. 수정된 경로 문자열을 FileObject에 다시 Set한 후 STATUS_REPARSE를 리턴한다.

    -. IoReplaceFileObjectName(FileObject, PathString);  


※ 결과적으로 "\x\y\test.txt" 는 "\a\b\test.txt"가 아닌 "\a\b"로 Redirect됨.


이렇게 되어버리는 것이 문제의 원인입니다.


그렇다면, ReplaceString 할때 FinalComponent를 복원시켜주면 되겠군요... ^^


아래와 같이 한번 수정해보겠습니다.


1. FileObject는 "\x\y\test.txt" 를 가리키고 있음.


2. FileObject에서 파일경로를 문자열로 추출한다.  

    -. FltGetFileNameInformation (FileObject) ==> PathString = "\x\y" // SL_OPEN_TARGET_DIRECTORY 때문에 

                                                                             // "\x\y\test.txt"이 아닌  L"\x\y"이 됨.


3. 추출된 경로 문자열을 기반으로 Redirect 대상 I/O인지 판단해서 경로 문자열을 Replace 조작한다.

    -. ReplaceUnicodeString(PathString, "\x\y", "\a\b"); ==> 결과 : "\a\b"


4. 최초 FileObject 경로에 들어있단 Final Component ("test.txt")를 append 해서 보정해준다.

    -. AppendUnicodeString(PathString, "test.txt"); ==> 결과 : "\a\b\test.txt"


5. 수정된 경로 문자열을 FileObject에 다시 Set한 후 STATUS_REPARSE를 리턴한다.

    -. IoReplaceFileObjectName(FileObject, PathString);  


※ 보정 결과 의도했던 대로 \a\b\test.txt로 Redirect됨.


위의 simrep 샘플코드에 제 나름대로 수정한 코드입니다. 주석에 "[수정]"이라고 되어 있는 부분 위주로 보시면 됩니다.




(알고보면 저렇게 간단한 것을.... 처음에는 원인을 몰라서 거의 1주일 가까이를 어셈만 들여다보면서 살았답니다... 정말 ntfs.sys의 맨 밑바닥까지 기어들어갔다 나왔어요... ㅜ.ㅜ)


여러분은 앞으로... File I/O를 Redirect할 때... Rename이 실패하더라도 

당황하지 않고.... FinalComponent를 복원해 주면...


(이미지 출처)



Posted by kuaaan

댓글을 달아 주세요



사랑합니다. 편안히 잠드소서