inf 파일로 드라이버 설치시 경고창 없이 설치하기
2011/07/26 09:12
64bit 와 32bit에서 설치하도록 2011년 7월 다시 수정
64bit OS에서 변경된 보안기능으로 서명되지 않은 장치 드라이버는 사용할수 없기 때문에 드라이버 파일에 모든 인증을 받아 사용해왔다.
XP 64bit에서는 NDIS 5.x 를 지원하는 Passthru를 기반으로 작성했고 64bit Win7에서는 NDIS 6.x 이상을 지원하는 filter를 기반으로 작성하였기 때문에 각각 inf 파일을 이용하여 설치해야 한다.
따라서 bindview 예제를 가지고 설치 테스트를 했는데 이 설치 과정에서 문제가 발생했다.분명 singtool로 드라이버 인증을 하였음에도 불구하고 서명되지 않은 어쩌구 저쩌구 설치하겠느냐? 설치하지 않겠느냐? 라는 대화창이 뜬다.
사용자에게 선택권을 주면 사용자가 설치하지 않는 경우가 있기 때문에 보안 프로그램 특성상 경고창 없이 설치를 해야만 하기때문에 이 과정을 강제로 skip을 해야한다.
사용자 삽입 이미지
서명 어쩌구 저쩌구 애매한 문구 때문에 다소 삽질(?)을 했으나 결국 inf파일로 설치하는 드라이버는 Windows Logo 관련 WHQL 인증을 받지 않으면 위와 같은 경고창이 뜨는걸 확인.
WHQL인증을 받아볼까 알아봤다가 .... 포기하고 ...결국 bindview 코드를 분석해서 수정하고 설치하는 몇차례 삽질을 거쳐 win7 64bit에서는 아래의 과정으로 경고창 없이 설치가 가능하다.

1. bindview 예제에 있는 SetupCopyOEMInf 를 생략하고 INetCfgClassSetup class의 Install 을 통해서 설치하도록 설치 프로그램 작성
2. SetupCopyOEMInf 를 삭제했기 때문에 수동으로 inf, sys 파일을 windows\inf 폴더에 복사2. 설치프로그램을 통해 inf 파일로 드라이버 설치
3. windows\system32\drivers 폴더에 드라이버가 복사가 되지 않으니 수동으로 sys 파일을 복사이렇게 하면 Win7에서 경고창없이 설치가 되고 동작도 문제가 없다.(vista에서는 테스트 해보지 않았지만 문제없이 될거라고 생각한다.)그런데 문제는 XP 64bit의 Passthru를 설치하려고 했더니 설치가 되지 않는다.
우선 inf 파일이 2개 인데다가 Win7에서 설치한 방법으로는 드라이버 파일을 찾는 대화창이 표시된다.또 삽집을 통해 방법이 다소 다르지만 XP 64bit에서도 경고창 없이 설치가 가능하다.

1. bindview 예제에 있는 SetupCopyOEMInf 를 생략하고 INetCfgClassSetup class의 Install 을 통해서 설치하도록 설치 프로그램 작성(중요한건 2개의 inf 파일을 모두 같이 install 해야한다. 테스트해보니 미니포트용 inf (netsf_m.inf)를 설치하지 않아도 됨)
2. SetupCopyOEMInf 를 삭제했기 때문에 역시 마찬가지로 수동으로 2개의 inf, sys 파일을 windows\inf 폴더에 복사
3. inf 파일의 SourceDisksNames 섹션과 SourceDisksFiles 섹션의 항목을 주석처리하고 C:\WINDOWS\ServicePackFiles 에 sys 파일을 복사(sys 파일을 찾는 경로명을 생략하면 ServicePackFiles에서 드라이버를 찾기때문에)4. 설치프로그램을 통해 inf 파일로 드라이버 설치5. windows\system32\drivers 폴더에 드라이버가 복사가 되지 않으니 수동으로 sys 파일을 복사 Unintalll은 설치과정의 반대로만 하면된다.

32bit에서 경고창없이 설치

win7에서는 64bit와 같은 과정을 통하면 경고창 없이 설치가능하였음.
혹시 그래도 경고창이 뜬다면 아래 링크참고.
테스트는 해보지 않았으니 참고할것.(disable하여 설치후 다시 enable하면 크게 문제 없을듯)
XP에서는 64bit에서 처럼 설치하였더니 실패.
레지스트리에 HKEY_LOCAL_MACHINE\Software\Microsoft\Driver Signing 를 0으로 설정하여야 함.
하지만 쉽게 변경이 되지 않으니 아래 링크를 참고
Osrloader.exe 로 서비스 생성(모든 옵션은 그냥 디폴트로 설정)후 테스트 해볼것.
2011/07/26 09:12 2011/07/26 09:12
Trackback Address :: http://hongyver.pe.kr/ttblog/trackback/775


이름변경을 위한 IRP_MJ_SET_INFORMATION IRP 생성
2011/06/21 09:58


기본적으로 ZwSetInformationFile 함수를 사용하나 rootkit등의 우회나 이름 변경시 추가 작업을 위한 hook등이 필요할때 드라이버에서 IRP_MJ_SET_INFORMATION IRP를 생성하여 파일의 이름을 변경한다.

파일명은 //device//harddiskvolume[n]//path를 포함한 file명 또는 //??//[drive]://path를 포함한 file명, //DosDevice//[drive]://path를 포함한 file명 와 같이 설정하는 점과 직접 생성한 IRP 이므로 완료루틴에서 pending 처리하지 않는다는 점을 고려한 대략의 코드는 아래와 같다.

IRP_MJ_SET_INFORMATION의 dispatch 루틴에서 특정 원본 파일의 이름을 강제로 a.ppt로 변경하는 코드이므로 원하는 파일명을 rename 하기 위해서는 추가적으로 FileObject를 구하여 한다.

XP에서 간단한 테스트는 문제없었으나 실제 사용을 위해서는 OSR의 Cracking Rename Operations 참고하여 수정하여 사용할것.(책임회피)
http://www.osronline.com/article.cfm?article=85
{
	...

	PIO_STACK_LOCATION pIrpSp;
	KEVENT event;
	NTSTATUS status;
	PIRP pNewIrp = NULL;
	PFILE_RENAME_INFORMATION pNewRename = NULL;
	ULONG NewRenameLen =  wcslen(L"\\??\\c:\\a.ppt") * 2;
	ULONG NewRenameSize = sizeof(FILE_RENAME_INFORMATION) + NewRenameLen;

	KeInitializeEvent(&event, SynchronizationEvent, FALSE);

	pNewRename = ExAllocatePoolWithTag(PagedPool, NewRenameSize, 'hong');
	if(pNewRename == NULL)
	   /// return 리소스부족;

	memcpy(pNewRename, pRenameInfo, sizeof(FILE_RENAME_INFORMATION));
	pNewRename->FileNameLength = NewRenameLen;
	wcsncpy(pNewRename->FileName, L"\\??\\c:\\a.ppt", NewRenameLen/2);

	pNewIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
	if(pNewIrp == NULL)
	   /// return 리소스부족;

	pNewIrp->Flags |= IRP_BUFFERED_IO;
	pNewIrp->RequestorMode = KernelMode;
	pNewIrp->UserIosb = &pIrp->IoStatus;
	pNewIrp->UserEvent = &event;
	pNewIrp->Tail.Overlay.OriginalFileObject = currentIrpStack->FileObject; // 원본 File Object
	pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread();
	pNewIrp->AssociatedIrp.SystemBuffer = (PVOID)&pNewRename;

	pIrpSp = IoGetNextIrpStackLocation(pNewIrp);
	pIrpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
	pIrpSp->FileObject = currentIrpStack->FileObject; 
	pIrpSp->DeviceObject = DeviceObject;
	pIrpSp->Parameters.SetFile.Length = NewRenameSize;
	pIrpSp->Parameters.SetFile.FileInformationClass = FileRenameInformation;
	pIrpSp->Parameters.SetFile.FileObject = currentIrpStack->FileObject;
	pIrpSp->Parameters.SetFile.ReplaceIfExists = pNewRename->ReplaceIfExists;

	IoSetCompletionRoutine(pNewIrp, CompleteRoutine, 0, TRUE, TRUE, TRUE);
	IoCallDriver( devExt->NLExtHeader.AttachedToDeviceObject, pNewIrp );
	KeWaitForSingleObject( &event, Executive, KernelMode, TRUE, 0);
	ExFreePoolWithTag(pNewRename, 'hong');

        ...
}
/// 직접만든 IRP는 pending을 하지 않음
NTSTATUS CompleteRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp,PVOID Context)
{
	*Irp->UserIosb = Irp->IoStatus;
	KeSetEvent(Irp->UserEvent, 0, FALSE);
	IoFreeIrp(Irp);

	return STATUS_MORE_PROCESSING_REQUIRED;
}
2011/06/21 09:58 2011/06/21 09:58
Trackback Address :: http://hongyver.pe.kr/ttblog/trackback/776


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에서 얻어지 파일 또는 폴더에 대한 정보를 가공(?) 할수 있다.

위의 그림에서 2번을 참고.

완료루틴

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 얻기

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;
...
}

2009/06/14 13:19 2009/06/14 13:19
Trackback Address :: http://hongyver.pe.kr/ttblog/trackback/772