Driver Basic

 

 

 

n  Kernel Stack Size는 최대 12KB

n  LookAsideList
동일 LookAsideList 2회이상 initialize하면 DeadLock 발생

 

n  SpinLock
동일 Thread에서 spinlock 2회 이상 획득하면 DeadLock발생
SpinLock획득시 IRQL DISPATCH Level로 상승됨

프로세스 풀패스구하기

 

http://www.osronline.com/article.cfm?id=472


What's in a (Process) Name? Obtaining A Useful Name for the Executable Image in a Process
The NT Insider, Vol 13, Issue 4, July - August 2006 | Published: 19-Sep-06| Modified: 19-Sep-06

Over the years developers have needed or wanted to know the name of the image executing in a given process. Traditionally, this was often done using PsGetProcessImageFile Name, which returns the contents of a field in the EPROCESS structure used by the Windows OS to maintain per-process state information.

As we can see from the information in the local debugger session (See Figure 1) the process image file is little more than a field within the EPROCESS structure. Notice that the EPROCESS address is in EBP+8, making it the first - and only - parameter to this function.

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Symbol search path is: srv*c:\symbols\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
*******************************************************************************
WARNING: Local kernel debugging requires booting with /debug to work optimally.
*******************************************************************************
Windows XP Kernel Version 2600 (Service Pack 2) MP (2 procs) Free x86 compatible
Product: winNt, suite: Terminalserver SingleUserTS
Built by: 2600.xpsp_sp2_gdr.050301-1519
Kernel base = 0x804d7000 PsLoadedModuleList = 0x805624a0
Debug session time: Mon Aug 7 13:29:53.486 2006 (GMT-4)
System Uptime: 2 days 11:08:38.140
lkd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..............................................................................
Loading User Symbols
..............................................................................
Loading unloaded module list
.......................*** ERROR: Symbol file could not be found. Defaulted to export symbols for C
lkd> u nt!PsGetProcessImageFileName
Nt!PsGetProcessImageFileName:
8050a14a 8bff mov edi,edi
8050a14c 55 push ebp
8050a14d 8bec mov ebp,esp
8050a14f 8b4508 mov eax,dword ptr [ebp+8]
8050a152 0574010000 add eax,174h
8050a157 5d pop ebp
8050a158 c20400 ret 4
8050a15b 8bce mov ecx,esi

Figure 1 - Local Debug Session of PsGetProcessImageFileName

Unfortunately, there are some issues with this approach:

·        Though well-known, this function is undocumented.

·        More seriously, the information contained in this field is severely limited. It contains only the first 16 (ASCII) characters of the image file name.

It is actually the second issue that often creates problems for programmers because the name of the image file means essentially nothing. For example, we've seen kernel-mode drivers in the past that validate the name of their service by checking this field. The most egregious case we've seen is when the service was called svchost.exe, which is a common name that is often spoofed.

The Proposal
We suggest a different model for acquiring this information, as shown in Figure 2.

typedef NTSTATUS (*QUERY_INFO_PROCESS) (
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);

QUERY_INFO_PROCESS ZwQueryInformationProcess;

NTSTATUS GetProcessImageName(PUNICODE_STRING ProcessImageName)
{
NTSTATUS status;
ULONG returnedLength;
ULONG bufferLength;
PVOID buffer;
PUNICODE_STRING imageName;

PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process

if (NULL == ZwQueryInformationProcess) {

UNICODE_STRING routineName;

RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");

ZwQueryInformationProcess =
(QUERY_INFO_PROCESS) MmGetSystemRoutineAddress(&routineName);

if (NULL == ZwQueryInformationProcess) {
DbgPrint("Cannot resolve ZwQueryInformationProcess\n");
}
}
//
// Step one - get the size we need
//
status = ZwQueryInformationProcess( NtCurrentProcess(),
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength);

if (STATUS_INFO_LENGTH_MISMATCH != status) {

return status;

}

//
// Is the passed-in buffer going to be big enough for us?
// This function returns a single contguous buffer model...
//
bufferLength = returnedLength - sizeof(UNICODE_STRING);

if (ProcessImageName->MaximumLength < bufferLength) {

ProcessImageName->Length = (USHORT) bufferLength;

return STATUS_BUFFER_OVERFLOW;

}

//
// If we get here, the buffer IS going to be big enough for us, so
// let's allocate some storage.
//
buffer = ExAllocatePoolWithTag(PagedPool, returnedLength, 'ipgD');

if (NULL == buffer) {

return STATUS_INSUFFICIENT_RESOURCES;

}

//
// Now lets go get the data
//
status = ZwQueryInformationProcess( NtCurrentProcess(),
ProcessImageFileName,
buffer,
returnedLength,
&returnedLength);

if (NT_SUCCESS(status)) {
//
// Ah, we got what we needed
//
imageName = (PUNICODE_STRING) buffer;

RtlCopyUnicodeString(ProcessImageName, imageName);

}

//
// free our buffer
//
ExFreePool(buffer);

//
// And tell the caller what happened.
//
return status;

}

Figure 2 - A New Proposal

The function itself is fairly straight-forward. It does rely on use of a single undocumented function (ZwQueryInformationProcess), but note that its counterpart (NtQueryInformationProcess) is documented. We need to use the Zw variant in order to use a kernel memory buffer.

The key element is that Windows has always stored the full path name to the executable image in order to provide this information in the auditing subsystem. This API exploits that existing stored path name.

Other Process Names
This function has been implemented to extract the process name for the
current process. However, you can use one of the two methods listed below to obtain the process name for a different process:

1. If you have a handle for the process, you can use that value instead of the NtCurrentProcess() macro. (Note that in our experience, we usually have a process object and not a process handle - the two are not interchangeable).

2. If you have an EPROCESS address, you can use KeStackAttachProcess/KeUnstackDetachProcess to attach to the process. This technique is rather heavy-weight, so it may be a good idea to cache the information if you need to perform this operation regularly.

When using the second technique, it is important to note that the name that is returned is a cached name. This cache is not updated if the name of the original file changes after the name is first cached. In other words, if the executable image is renamed, which is typically allowed for a running executable, the name returned will be the name of the original file.

This issue is not unique. We have also observed that some file systems return the original name even after a rename (e.g., the CIFS client implementation does this on Windows XP). Thus, it may require additional processing such as through a file system filter driver to protect against similar events. For example, you may encounter a security product that relies on knowing the specific name of the image.

Alternatives?
There are other options that a driver could also pursue such as registering for process creation/teardown events (PsSetCreateProcessNotifyRoutine) or image loading (PsSetLoadImageNotifyRoutine).

PsSetCreateProcessNotifyRoutine has limitations on the number of drivers that can register using this API. Since there is a fixed size table in the Windows OS, it is possible for this call to fail. When this occurs, a driver needs to ensure it can handle such a failure. PsSetLoadImageNotifyRoutine has the same limitation (fixed size table), but is called for all image loads, not just the original process image. Therefore, it includes drivers, DLLs, executables, etc.

Summary
The bottom line is that all of these approaches provide a useful name because they include the full path name. This is vastly superior to using the short ASCII eye-catching name that is stored in the
EPROCESS structure. A word of caution - if you decide to use the debug level name, use it for nothing more than debugging since it is not reliable and cannot be relied on for any sort of security check.

We chose to use the proposed technique because it works in all circumstances and does not rely upon a registration that might potentially fail. In your own driver you might implement both this mechanism and a cache-based mechanism tied to the process creation logic.

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Windows 2000 Support"
Unfortunately, this technique works only for XP and more recent. Windows 2000 doesn't maintain this information and there's no simple way to duplicate this (basically, you have to watch with a file system filter driver for when a file object is created with EXECUTE access and the name used at that point.)

 

 

IoCompleteRequest in ReactOS

 

 

VOID NTAPI IoCompleteRequest(IN PIRP Irp, IN CCHAR PriorityBoost)

{

    /* Call the fastcall */

    IofCompleteRequest(Irp, PriorityBoost);

}

 

 

VOID FASTCALL IofCompleteRequest(IN PIRP Irp, IN CCHAR PriorityBoost)

{

    PIO_STACK_LOCATION StackPtr, LastStackPtr;

    PDEVICE_OBJECT DeviceObject;

    PFILE_OBJECT FileObject;

    PETHREAD Thread;

    NTSTATUS Status;

    PMDL Mdl, NextMdl;

    ULONG MasterCount;

    PIRP MasterIrp;

    ULONG Flags;

    NTSTATUS ErrorCode = STATUS_SUCCESS;

   

           IOTRACE(IO_IRP_DEBUG, "%s - Completing IRP %p\n", __FUNCTION__, Irp);

 

    /* Make sure this IRP isn't getting completed twice or is invalid */

    if ((Irp->CurrentLocation) > (Irp->StackCount + 1))

    {

        /* Bugcheck */

        KeBugCheckEx(MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR)Irp, 0, 0, 0);

    }

 

    /* Some sanity checks */

    ASSERT(Irp->Type == IO_TYPE_IRP);

    ASSERT(!Irp->CancelRoutine);

    ASSERT(Irp->IoStatus.Status != STATUS_PENDING);

    ASSERT(Irp->IoStatus.Status != (NTSTATUS)0xFFFFFFFF);

 

    /* Get the last stack */

    LastStackPtr = (PIO_STACK_LOCATION)(Irp + 1);

    if (LastStackPtr->Control & SL_ERROR_RETURNED)

    {

        /* Get the error code */

        ErrorCode = (NTSTATUS)LastStackPtr->Parameters.Others.Argument4;

    }

 

    /* Get the Current Stack and skip it */

    StackPtr = IoGetCurrentIrpStackLocation(Irp);

    IoSkipCurrentIrpStackLocation(Irp);

 

    /* Loop the Stacks and complete the IRPs */

    do

    {

        /* Set Pending Returned */

        Irp->PendingReturned = StackPtr->Control & SL_PENDING_RETURNED;

 

        /* Check if we failed */

        if (!NT_SUCCESS(Irp->IoStatus.Status))

        {

            /* Check if it was changed by a completion routine */

            if (Irp->IoStatus.Status != ErrorCode)

            {

                /* Update the error for the current stack */

                ErrorCode = Irp->IoStatus.Status;

                StackPtr->Control |= SL_ERROR_RETURNED;

                LastStackPtr->Parameters.Others.Argument4 = (PVOID)ErrorCode;

                LastStackPtr->Control |= SL_ERROR_RETURNED;

            }

        }

 

        /* Check if there is a Completion Routine to Call */

        if ((NT_SUCCESS(Irp->IoStatus.Status) && (StackPtr->Control & SL_INVOKE_ON_SUCCESS)) ||

            (!NT_SUCCESS(Irp->IoStatus.Status) && (StackPtr->Control & SL_INVOKE_ON_ERROR)) ||

            (Irp->Cancel && (StackPtr->Control & SL_INVOKE_ON_CANCEL)))

        {

            /* Clear the stack location */

            IopClearStackLocation(StackPtr);

 

            /* Check for highest-level device completion routines */

            if (Irp->CurrentLocation == (Irp->StackCount + 1))

            {

                /* Clear the DO, since the current stack location is invalid */

                DeviceObject = NULL;

            }

            else

            {

                /* Otherwise, return the real one */

                DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;

            }

 

            /* Call the completion routine */

            Status = StackPtr->CompletionRoutine(DeviceObject, Irp, StackPtr->Context);

 

            /* Don't touch the Packet in this case, since it might be gone! */

            if (Status == STATUS_MORE_PROCESSING_REQUIRED) return;

        }

        else

        {

            /* Otherwise, check if this is a completed IRP */

            if ((Irp->CurrentLocation <= Irp->StackCount) && (Irp->PendingReturned))

            {

                /* Mark it as pending */

                IoMarkIrpPending(Irp);

            }

 

            /* Clear the stack location */

            IopClearStackLocation(StackPtr);

        }

 

        /* Move to next stack location and pointer */

        IoSkipCurrentIrpStackLocation(Irp);

        StackPtr++;

    } while (Irp->CurrentLocation <= (Irp->StackCount + 1));

 

    /* Check if the IRP is an associated IRP */

    if (Irp->Flags & IRP_ASSOCIATED_IRP)

    {

        /* Get the master IRP and count */

        MasterIrp = Irp->AssociatedIrp.MasterIrp;

        MasterCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);

 

        /* Free the MDLs */

        for (Mdl = Irp->MdlAddress; Mdl; Mdl = NextMdl)

        {

            /* Go to the next one */

            NextMdl = Mdl->Next;

            IoFreeMdl(Mdl);

        }

 

        /* Free the IRP itself */

        IoFreeIrp(Irp);

 

        /* Complete the Master IRP */

        if (!MasterCount) IofCompleteRequest(MasterIrp, PriorityBoost);

        return;

    }

 

    /* We don't support this yet */

    ASSERT(Irp->IoStatus.Status != STATUS_REPARSE);

 

    /* Check if we have an auxiliary buffer */

    if (Irp->Tail.Overlay.AuxiliaryBuffer)

    {

        /* Free it */

        ExFreePool(Irp->Tail.Overlay.AuxiliaryBuffer);

        Irp->Tail.Overlay.AuxiliaryBuffer = NULL;

    }

 

    /* Check if this is a Paging I/O or Close Operation */

    if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION))

    {

        /* Handle a Close Operation or Sync Paging I/O */

        if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))

        {

            /* Set the I/O Status and Signal the Event */

            Flags = Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO);

            *Irp->UserIosb = Irp->IoStatus;

            KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);

 

            /* Free the IRP for a Paging I/O Only, Close is handled by us */

            if (Flags) IoFreeIrp(Irp);

        }

        else

        {

#if 0

            /* Page 166 */

            KeInitializeApc(&Irp->Tail.Apc

                            &Irp->Tail.Overlay.Thread->Tcb,

                            Irp->ApcEnvironment,

                            IopCompletePageWrite,

                            NULL,

                            NULL,

                            KernelMode,

                            NULL);

            KeInsertQueueApc(&Irp->Tail.Apc,

                             NULL,

                             NULL,

                             PriorityBoost);

#else

            /* Not implemented yet. */

            DPRINT1("Not supported!\n");

            while (TRUE);

#endif

        }

 

        /* Get out of here */

        return;

    }

 

    /* Unlock MDL Pages, page 167. */

    Mdl = Irp->MdlAddress;

    while (Mdl)

    {

                     MmUnlockPages(Mdl);

        Mdl = Mdl->Next;

    }

 

    /* Check if we should exit because of a Deferred I/O (page 168) */

    if ((Irp->Flags & IRP_DEFER_IO_COMPLETION) && !(Irp->PendingReturned))

    {

        /*

         * Return without queuing the completion APC, since the caller will

         * take care of doing its own optimized completion at PASSIVE_LEVEL.

         */

        return;

    }

 

    /* Get the thread and file object */

    Thread = Irp->Tail.Overlay.Thread;

    FileObject = Irp->Tail.Overlay.OriginalFileObject;

 

    /* Make sure the IRP isn't canceled */

    if (!Irp->Cancel)

    {

        /* Initialize the APC */

        KeInitializeApc(&Irp->Tail.Apc,

                        &Thread->Tcb,

                        Irp->ApcEnvironment,

                        IopCompleteRequest,

                        NULL,

                        NULL,

                        KernelMode,

                        NULL);

 

        /* Queue it */

        KeInsertQueueApc(&Irp->Tail.Apc,

                         FileObject,

                         NULL, /* This is used for REPARSE stuff */

                         PriorityBoost);

    }

    else

    {

        /* The IRP just got canceled... does a thread still own it? */

        Thread = Irp->Tail.Overlay.Thread;

        if (Thread)

        {

            /* Yes! There is still hope! Initialize the APC */

            KeInitializeApc(&Irp->Tail.Apc,

                            &Thread->Tcb,

                            Irp->ApcEnvironment,

                            IopCompleteRequest,

                            NULL,

                            NULL,

                            KernelMode,

                            NULL);

 

            /* Queue it */

            KeInsertQueueApc(&Irp->Tail.Apc,

                             FileObject,

                             NULL, /* This is used for REPARSE stuff */

                             PriorityBoost);

        }

        else

        {

            /* Nothing left for us to do, kill it */

            ASSERT(Irp->Cancel);

            IopCleanupIrp(Irp, FileObject);

        }

    }

}

 

                                                 http://msdn.microsoft.com/ko-kr/library/8s9b9yaz(VS.80).aspx
서명 도구(SignTool.exe)

서명 도구는 파일에 디지털 서명을 하고, 파일의 서명을 확인하고, 파일에 타임스탬프를 기록하는 명령줄 도구입니다.

Note참고

Microsoft Windows NT, Windows Me, Windows 98 또는 Windows 95에서는 서명 도구가 지원되지 않습니다.

signtool [command] [options] [file_name | ...]

매개 변수

인수 설명

command

파일에서 수행할 작업을 지정하는 명령 플래그 중 하나입니다.

options

명령 플래그를 수정하는 옵션 플래그 중 하나입니다.

file_name

서명할 파일 경로입니다.

다음은 서명 도구에서 지원되는 명령입니다.

명령 설명

catdb

카탈로그 데이터베이스에서 카탈로그 파일을 추가하거나 제거합니다.

sign

파일에 디지털 서명을 수행합니다.

signwizard

서명 마법사를 시작합니다. 파일 이름 명령줄 인수에는 단일 파일만 지정할 수 있습니다.

timestamp

파일에 타임스탬프를 기록합니다.

verify

파일의 디지털 서명을 확인합니다.

다음은 catdb 명령에 적용되는 옵션입니다.

Catdb 옵션 설명

/d

기본 카탈로그 데이터베이스가 업데이트되도록 지정합니다. /d/g 옵션을 모두 사용하지 않을 경우 서명 도구에서 시스템 구성 요소와 드라이버 데이터베이스를 업데이트합니다.

/g GUID

고유한 전역 식별자(GUID)로 식별되는 카탈로그 데이터베이스가 업데이트되도록 지정합니다.

/r

지정된 카탈로그를 카탈로그 데이터베이스에서 제거합니다. 이 옵션을 지정하지 않으면 서명 도구는 지정된 카탈로그를 카탈로그 데이터베이스에 추가합니다.

/u

추가된 카탈로그 파일에 대해 고유한 이름이 자동으로 생성되도록 지정합니다. 필요한 경우 기존 카탈로그 파일과의 이름 충돌을 방지하기 위해 카탈로그 파일의 이름을 바꿉니다. 이 옵션을 지정하지 않으면 서명 도구는 추가하려는 카탈로그와 같은 이름의 기존 카탈로그를 덮어씁니다.

Note참고

카탈로그 데이터베이스는 카탈로그 파일 자동 조회에 사용됩니다.

다음 옵션은 sign 명령에 적용됩니다.

Sign 옵션 설명

/a

가장 적합한 서명 인증서를 자동으로 선택합니다. 이 옵션이 없으면 서명 도구는 유효한 서명 인증서를 하나만 찾게 됩니다.

/c CertTemplateName

서명 인증서의 인증서 템플릿 이름(Microsoft 확장)을 지정합니다.

/csp CSPName

개인 키 컨테이너를 포함하는 CSP(암호화 서비스 공급자)를 지정합니다.

/d Desc

서명된 콘텐츠에 대한 설명을 지정합니다.

/du URL

서명된 콘텐츠에 대한 확장된 설명의 URL(Uniform Resource Locator)을 지정합니다.

/f SignCertFile

파일에 있는 서명 인증서를 지정합니다. 파일이 PFX(개인 정보 교환) 형식이면서 암호로 보호되는 경우, /p 옵션을 사용하여 암호를 지정합니다. 파일에 개인 키가 없으면 /csp/k 옵션을 사용하여 CSP 및 개인 키 컨테이너 이름을 각각 지정합니다.

/i IssuerName

서명 인증서의 발급자 이름을 지정합니다. 이 값은 발급자의 전체 이름에서 부분 문자열이 될 수 있습니다.

/k PrivKeyContainerName

개인 키 컨테이너 이름을 지정합니다.

/n SubjectName

서명 인증서의 주체 이름을 지정합니다. 이 값은 주체의 전체 이름에서 부분 문자열이 될 수 있습니다.

/p Password

PFX 파일을 열 때 사용할 암호를 지정합니다. PFX 파일은 /f 옵션을 사용하여 지정할 수 있습니다.

/r RootSubjectName

서명 인증서와 연결해야 하는 루트 인증서의 주체 이름을 지정합니다. 이 값은 루트 인증서 주체의 전체 이름에서 부분 문자열이 될 수 있습니다.

/s StoreName

인증서를 검색할 때 열 저장소를 지정합니다. 이 옵션을 지정하지 않으면 내 저장소가 열립니다.

/sha1 Hash

서명 인증서의 SHA1 해시를 지정합니다.

/sm

사용자 저장소 대신 컴퓨터 저장소가 사용되도록 지정합니다.

/t URL

타임스탬프 서버의 URL을 지정합니다. 이 옵션이 없으면 서명 파일에 타임스탬프가 기록되지 않습니다. 타임스탬프 기록에 실패하면 경고가 생성됩니다.

/u Usage

서명 인증서에 있어야 하는 EKU(향상된 키 용도)를 지정합니다. 용도 값은 OID 또는 문자열로 지정될 수 있습니다. 기본 용도는 "코드 서명"(1.3.6.1.5.5.7.3.3)입니다.

다음 옵션은 timestamp 명령에 적용됩니다.

Timestamp 옵션 설명

/t URL

필수적 요소입니다. 타임스탬프 서버의 URL을 지정합니다. 타임스탬프가 기록되는 파일은 이전에 서명되었어야 합니다.

다음은 verify 명령에 적용되는 옵션입니다.

Verify 옵션 설명

/a

모든 메서드를 사용하여 파일 확인을 할 수 있도록 지정합니다. 먼저 카탈로그 데이터베이스를 검색하여 카탈로그에서 파일이 서명되었는지 여부를 확인합니다. 어떤 카탈로그에서도 파일이 서명되지 않은 경우에는 서명 도구에서 파일에 포함된 서명을 확인하려고 합니다. 이 옵션은 카탈로그에서 서명되거나 서명되지 않은 파일을 확인할 때 권장됩니다. 서명되거나 서명되지 않은 파일의 예로 Windows 파일 또는 드라이버가 있습니다.

/ad

기본 카탈로그 데이터베이스를 사용하여 카탈로그를 찾습니다.

/as

시스템 구성 요소(드라이버) 카탈로그 데이터베이스를 사용하여 해당 카탈로그를 찾습니다.

/ag CatDBGUID

카탈로그 데이터베이스에서 GUID로 식별되는 카탈로그를 찾습니다.

/c CatFile

이름별로 카탈로그 파일을 지정합니다.

/o Version

운영 체제 버전별로 파일을 확인합니다. 버전 매개 변수의 형식은 PlatformID:VerMajor.VerMinor.BuildNumber입니다.

/pa

기본 인증 확인 정책이 사용되도록 지정합니다. /pa 옵션을 지정하지 않으면 서명 도구는 Windows 드라이버 확인 정책을 사용합니다. 이 옵션은 catdb 옵션과 함께 사용할 수 없습니다.

/pg PolicyGUID

GUID를 기준으로 확인 정책을 지정합니다. GUID는 확인 정책의 ActionID와 일치합니다. 이 옵션은 catdb 옵션과 함께 사용할 수 없습니다.

/r RootSubjectName

서명 인증서와 연결해야 하는 루트 인증서의 주체 이름을 지정합니다. 이 값은 루트 인증서 주체의 전체 이름에서 부분 문자열이 될 수 있습니다.

/tw

서명에 타임스탬프가 기록되지 않으면 경고가 생성되도록 지정합니다.

다음 옵션은 모든 서명 도구 명령에 적용됩니다.

전역 옵션 설명

/q

성공한 실행에 대해서는 출력하지 않고 실패한 실행에 대해서만 최소 출력합니다.

/v

성공한 실행, 실패한 실행 및 경고 메시지에 대해 자세히 출력합니다.

서명 도구를 사용하려면 로컬 컴퓨터에 CAPICOM 2.0 재배포 가능 파일이 설치되어 있어야 합니다. CAPICOM 2.0 재배포 가능 파일은 http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdkredist.htm에서 제공합니다.

서명 도구의 verify 명령은 신뢰할 수 있는 기관에서 발행한 서명 인증서인지, 해당 서명 인증서가 취소되었는지 그리고 서명 인증서가 특정 정책에 대해 유효한지를 선택적으로 확인합니다.

서명 도구는 종료 코드로 성공한 실행에 대해서는 0, 실패한 실행에 대해서는 1 그리고 경고로 끝난 실행에 대해서는 2를 반환합니다.

이 명령은 가장 적합한 인증서를 사용하여 파일에 자동으로 서명하는 방법을 보여 줍니다.

signtool sign /a MyFile.exe

IoCallDriver in ReactOS

 

 

NTSTATUS NTAPI IoCallDriver(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

           /* Call fastcall */

           return IofCallDriver(DeviceObject, Irp);

}

 

NTSTATUS FASTCALL IofCallDriver(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

           PDRIVER_OBJECT DriverObject;

           PIO_STACK_LOCATION StackPtr;

 

           /* Get the Driver Object */

           DriverObject = DeviceObject->DriverObject;

 

           /* Decrease the current location and check if */

           Irp->CurrentLocation--;

           if ( Irp->CurrentLocation <= 0 )

           {

                     /* This IRP ran out of stack, bugcheck */

                     KeBugCheckEx(NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR)Irp, 0, 0, 0);

           }

 

           /* Now update the stack location */

           StackPtr = IoGetNextIrpStackLocation(Irp);

           Irp->Tail.Overlay.CurrentStackLocation = StackPtr;

 

           /* Get the Device Object */

           StackPtr->DeviceObject = DeviceObject;

 

           /* Call it */

           return DriverObject->MajorFunction[StackPtr->MajorFunction](DeviceObject,   Irp);

}

 

FileMap을 이용한 파일엑세스 모니터링

FileSystem 필터 드라이버를 작성해서, 파일의 Read/Write를 감시 차단하는 기능을 구현했다.
그런데 이상하게도 notepad text파일을 여는 경우에 대해서는 감시가 되지 않는다.

내용을 확인해 본 즉슨,
notepad
는 파일을 R/W할 때 ReadFile/WriteFile API를 사용하지 않고, 파일을 Create한 후,  CreateFileMapping()를 이용하여 파일의 내용을 메모리에 올린 후 작업하는 것으로 분석이 된다.

FileSystem 필터 드라이버에서 감지되는 내용은 notepad에서 해당 파일을 메모리에 mapping하기 위해 텍스트 파일을 Create(Open)하는 과정 뿐이다.

기존의 드라이버의 경우, NativeAPI NtCreateSection()을 후킹하여 처리되었던 부분인데,
64bit OS
까지 한 벌의 소스로 작성해야 하는지라 NtCreateSection을 후킹 하기에는 문제가 있어 보인다.

정말 파일 시스템 필터 드라이버만으로 불가능 한가???

웹 검색해봐도 딱히 나오는 정보도 없고, 사실 거의 포기할 뻔 했다.
Read
대상 파일이 Memory map되고 난 이후에는 Read동작을 모니터링 할 수 없으므로,
map
되는 시점을 로깅 시점으로 잡아야 한다.( 이건 안될거 같고 )
그냥 Read를 차단하면 파일 Create 자체를 막는 방식으로 돌려서 처리를 하던 중...


filemon
소스에서 아래의 코드를 발견하긴 했지만.
DriverObject->FastIoDispatch->AcquireFileForNtCreateSection
FastIoDispatch->AcquireFileForNtCreateSection
XP이상에서는 사용되지 않는 것으로 MSDN에 명시되어 있으며, windows 2000에서 테스트 해본 결과로는 filemap을 생성하더라도 호출되지 않는다.

하지만 다행히도 연관검색어로 찾아낸 함수가 하나 있었으니 FsRtlRegisterFileSystemFilterCallbacks() 함수를 사용하면 된다.


PreAcquireForSectionSynchronization
에 대한 Callback을 등록하여, Callback함수 호출 시 차단하거나 로깅 하면 FileMapping을 이용하는 I/O동작 또한 처리가 가능하다.

다만 FsRtlRegisterFileSystemFilterCallbacks() 함수는 XP 이상에서만 사용할 수 있다.

결국 windows 2000에서는 Read차단시 file create를 차단하는 방법으로 구현할 수 밖에 없었지만,
XP
이상에서는 정확히 처리 할 수 있다.

언제나 찾기전에는 도저히 검색되지 않던 정보들이, 답을 알고 나면 보이는 법....
WDK
FileSpy 샘플을 보면 콜백등록 소스가 포함되어 있다..
진즉 볼것을....

 

 

  1. 박진용 2014.08.13 11:48

    저에게 큰 도움이 되었습니다. 감사합니다. ^^

+ Recent posts