반응형

n  API Hooking을 이야기 하다가 갑자기 "PE File Format" ? 다음에 포스트 할 Import Adress Table Hooking을 이해하기 위해서는 꼭 필요한 내용이기 때문이다.

 

n  Windows OS에서는 실행코드와 데이터를 저장하기 위해 PE라는 포맷을 쓰고 있다. PE 파일은 예전 유닉스 시스템에서의 coff(common object file format)파일 포맷에 뿌리를 둔 포맷으로, Widows NT 개발 당시 여러 CPU플랫폼에서 동일한 파일 포맷으로 실행이 가능하도록 하기 위한 용도로 만들어졌다 하여 PE(Portable Executable) 파일이라는 이름이 붙었다고 한다.

 

n  하지만 여기서는 PE파일에 대한 자세한 내용을 기술하지 않는다. 정리하기 귀찮기도 하고 잘 정리된 사이트들이 많은 마당에, 더 상세하고 이해하기 쉽게 쓸 자신도 없다. 아래의 URL을 참고하면 PE File Format에 대해 상세한 정보를 얻을 수 있다. (개인적인 의견으로는 PE 파일에 대해 공부하며 검색했던 사이트들 중에 가장 상세하고 이해하기 쉽게 정리되어 있는 포스트이다..)

 참고 URL: http://www.reversecore.com/18

반응형

'Windows Programming' 카테고리의 다른 글

Url parsing  (0) 2010.02.05
Visual Studio 소스편집창 분할하기  (0) 2009.08.05
API Hooking - 1. Dll Injection  (0) 2009.07.22
[Win32] Windows Message – Control 제어  (0) 2009.06.02
Windows Mobile 전원 상태 감시  (0) 2008.08.12
반응형

Dll Injection
API Hooking기법에 따라 차이는 있겠으나, API Hooking을 위해서는 대상프로세스의 주소 공간안에서의 메모리 조작이 필요하다.
이를 위해 사용되는 방법이 바로 Dll Injection이다. 즉 타겟 프로세스로 하여금 우리가 작성한 Dll을 로드하도록 하고, 해당 프로세스에 로드된 Dll에서 APIHooking하는 방식이다
.
많이 사용되는 Dll Injection방법으로는 아래의 세가지 방법이 있다
.
( Dll Injection
API Hooking뿐만 아니라, 소스코드가 없는 프로그램에 대한 패치나 구조분석 등에도 사용될 수 있다. )

 

(1) AppInit_DLLs 레지스트리 이용

1) 개요

User32.dll(User32.dll에서 Export API를 사용하는 모든 프로그램은) DLL_PROCESS_ATTACH 처리 과정에서 LoadLibrary()함수를 이용하여, 아래 키 값의 AppInit DLLs엔트리에 등록된 모든 Dll을 로드한다. 이러한 동작을 이용하여 우리의 Dll을 다른 프로세스에 Injection 시킨다.

n HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows
( AppInit DLLs
에는 스페이스(‘ ‘)를 구분자로 하여 여러 개의 Dll을 등록할 수 있다. )

2) 주의 사항

하지만 이 방법의 경우, 등록된 dll이 프로세스 생성과정의 전반부에서 로딩되므로, dll 로드과정에서는 kernel32.dll에서 export한 함수만을 안전하게 사용할 수 있다.(하단 URL참조)

모든 프로세스에 Injection하기 위해서는 시스템 리부팅이 필요하다. 간혹 이 기능이 동작하기 위해서는 무조건 리부팅이 필요하다고 기술한 문서들이 있는데, 리부팅하지 않더라도 해당 레지스트리에 Dll을 등록한 이후에 생성되는 프로세스는 영향을 받는다.

Dll Inject 시점을 임의로 조절할 수 없으며, Injection대상 프로세스를 지정할 수도 없다.

Dll Unload시킬 수 있는 방법이 없다.

가급적 이 방법 사용하지 않는 것이 좋을 것 같다.

3) 참고 URL
http://support.microsoft.com/kb/197571

 

(2) SetWindowsHook()함수 사용

1) 개요
SetWindowsHook() API를 이용하여 특정 HookType에 대해 Hooking을 시도하게 되면, Dll내의 Hook Procedure뿐만 아니라, Dll코드 전체가 대상 프로세스의 메모리공간에 로드 된다. 이러한 특성을 이용하여 다른 프로세스에 Injection한다.

2) 함수 원형

HHOOK SetWindowsHookEx(

int idHook, // type of hook to install

HOOKPROC lpfn, // address of hook procedure

HINSTANCE hMod, // handle to application instance

DWORD dwThreadId // identity of thread to install hook for

);

BOOL UnhookWindowsHookEx(

HHOOK hhk // handle to hook procedure to remove

);

3) 구현

Hook Handle공유

n SetWindowsHook()호출을 통해 리턴되는 HHOOK 타입의 핸들은, Hooking 대상 메시지(?) Hook체인으로 전달하기 위해, 모든 Dll 인스턴스들이 공유해야 한다. 이를 위해 shared속성을 갖는 섹션에 선언한다.

#pragma data_seg(".hkshared")

HHOOK   g_hHook = NULL;

#pragma data_seg()

#pragma comment(linker, "/SECTION:.hkshared,RWS) //Read|Write|Shared

 

(3) CreateRemoteThread()

1) 개요
CreateRemoteThread()를 이용하여 이미 존재하는 프로세스내에 원격스레드를 생성하고, 이 원격스레드의 시작 함수로 Kernel32.dll LoadLibrary()의 주소를, 파라미터로 우리가 로드하려하는 hookDll의 파일명(혹은 FullPath)를 전달한다.
이 방법은 CreateRemoteThread() 4번째 인자인lpStartAddress LoadLibrary()의 함수원형이 같다는 점에서 유효하다.( 엄밀히 말하자면 같지는 않지만, 4byte의 인자와 4byte의 리턴값을 취한다는 점에서 동일하게 취급할 수 있다.)

2) 함수원형

HANDLE CreateRemoteThread(

HANDLE hProcess, // handle to process to create thread in

LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes

DWORD dwStackSize, // initial thread stack size, in bytes

LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function

LPVOID lpParameter, // argument for new thread

DWORD dwCreationFlags, // creation flags

LPDWORD lpThreadId // pointer to returned thread identifier

);

3) 구현

전술한 내용을 이용하여 CreateRemoteThread()를 호출하려면 4번재 파라미터로 , 타겟프로세스 내에서의 LoadLibrary() 함수의 주소를 구하여 넘겨주어야 한다. 하지만 아시다시피 각 프로세스는 별도의 메모리 공간을 가지고 있다.

n Kernel32.dll, ntdll.dll, user32.dll등의 시스템 dll들은 중복되지 않는 고유의 Address를 갖으므로, Dll Injection을 시도하는 프로세스에서 구한 Address는 타겟 프로세스에서도 유효하다.( Windows System Dll들은 로딩시 Relocation을 피하기 위해 고유의 BaseAddress를 가지고 있다 )

CreateRemoteThread()의 다섯번째 인자인 lpParameter에는 우리가 로드할 Dll의 이름을 넘겨주어야 한다. 하지만 파라미터로 전달해야하는 hookDll의 파일명을 Dll Injection을 시도하는 프로세스의 주소로 넘겨주어서는 의미가 없다. 각각의 프로세스는 고유의 메모공간을 가지므로, Dll Injection을 시도하는 프로세스의 주소는 타겟 프로세스에서 유효하지 않다.

n 이를 위해 타겟 프로세스에 메모리 공간을 할당하고, 그 메모리에 우리의 hookDll의 이름을 적어주어야 하는데 다음의 함수들을 사용해서 구현할 수 있다.

HANDLE OpenProcess(

DWORD dwDesiredAccess,

BOOL bInheritHandle,

DWORD dwProcessId

);

OpenProcess()는 프로세스ID를 인자로 받아 해당 프로세스의 핸들을 리턴한다.

이 핸들값은 VirtualAllocEx(), WriteProcessMemory()의 첫번째 인자로 사용된다.

LPVOID VirtualAllocEx(

HANDLE hProcess,

LPVOID lpAddress,

SIZE_T dwSize,

DWORD flAllocationType,

DWORD flProtect

);

VertualAllocEx()는 첫번째 인자로 주어진 프로세스의 가상메모리내에 메모리를 할당한다.

BOOL WriteProcessMemory(

HANDLE hProcess,

LPVOID lpBaseAddress,

LPCVOID lpBuffer,

SIZE_T nSize,

SIZE_T* lpNumberOfBytesWritten

);

VertualAllocEx()는 첫번째 인자로 주어진 프로세스의 가상메모리내에 Write를 지원한다.

4) 기타

CreateRemoteThread()를 이용해 Injection시킨 Dll은 아래와 같은 절차로 Unload시킬 수 도 있다.

n Injection GetExitCodeThread()를 통해 LoadLibray()가 리턴한 값(모듈핸들)을 보관

n LoadLibrary()를 리모트 프로세스에서 실행시킨 것과 마찬가지 방법으로 FreeLibrary()를 실행(FreeLibrary역시 4바이트의 인자를 가지며, 4바이트의 값을 리턴한다)

Vista이상의 경우 다른 세션에 존재하는 프로세스를 대상으로 CreateRemoteThread()를 호출할 경우, 실패한다( lasterror: NOT_ENOUGH_MEMORY) 세션별로 Injection 프로세스를 띄워주면 되겠지만 용이치 않을 경우, ntdll!NTCreateThreadEx()를 사용하면 된다고 한다.(Vista이상의 OS만 유효함)

n (http://blog.naver.com/rkawk01?Redirect=Log&logNo=70046078078)

n http://code.google.com/p/easyhook-continuing-detours/source/browse/trunk/EasyHook_Specific/RemoteHook/RemoteHooking.cpp?spec=svn9&r=9

 

 

반응형
반응형

ASM Code


* 디버깅할때 자주 보게되는 한눈에 들어오지 않는 asm 코드들을 정리한다.
 

DisAsm

xor eax, eax

  • xor( Exclusive OR) - 두 오퍼랜드의 비트들에 대해 논리 XOR를 수행하여 그 결과를 목적지 오퍼랜드에 저장한다.
  • XOR 연산은 두 값 중 한 값만 참인 경우 참으로 간주하므로 eax값을 0으로 만드는 코드이다.
  • 같은 값을 xor하면 모든 비트가 같으므로 1이 나올수 없다.
  • 결국 mov eax, 0 과 같은 결과가 나온다는 얘기인데, 위와 같은 코드를 사용하는 이유는 xor연산이 cost & opcode size가 작기 때문인듯 하다.
    0041CD4E B8 00 00 00 00   mov         eax,0
    0041CD53 33 C0              xor         eax,eax

mov edi, edi

  • 2byte NOP코드와 같다.
  • XP-SP2이후부터, 함수 prolog의 첫번째 명령으로 추가되었다.
  • 이는 마이크로소프트가 재부팅 없이 패치를 수행(hot-fix)하기 위해 추가한 것으로
    XP-SP2
    이전에는 prolog 2byte(why2?)+이어지는 명령의 2byte를 교체해야 했으나,
    XP-SP2
    이후에는 위의 두바이트 명령이 추가됨으로써 함수의 prolog가 정확히 5바이트로 변경되었으며, 이로 인해 인라인 후킹이 더 쉬워졌다.(다른 코드로의 점프에 필요한 바이트수가 5byte)

jmp WHook!ILT+360(?MyMessageBoxWYGHPAUHWND__PB_W1IZ) (00b3716d)

  • ILT( Incremental Link Table )
    /INCREMENTAL
    옵션을 주어 링크 했을때, 링커는 함수 주소들의 배열(ILT)을 생성한다. 그리고 함수를 호출하기 위해 해당 슬롯을 통해 함수를 직접 호출한다.
    ILT+360은 해당배열의 360번째 슬롯의 주소를 의미한다.

add byte ptr [eax],al

  • 이 명령의 기계어 코드를 보면 0000이다. 이 명령이 반복적으로 계속되는 경우, 유효한 기계어 코드라기 보다는 0으로 채워진 메모리를 디스어셈블러가 이 코드로 번역했다고 보는 게 맞을 거 같다.

pushad

  • 모든 레지스터를 저장

popad

  • 모든 레지스터를 복구

 

Asm 코드 작성

_emit [BYTE]

  • 인자로 넘겨진 바이트들을 직접 기계어 코드로서 삽입

 

 

 

반응형
반응형

프로세스 풀패스구하기

 

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.)

 

 

반응형
반응형

< 소프트웨어 인터럽트 0x2E에 의한 커널모드 진입방식 >

사용자 삽입 이미지

- 각 native 함수별 서비스 번호를 EAX 레지스터에 넣어 주고, EDX 레지스터에는 스택 프레임

  의 포인터를 저장시켜 놓은 후, 소프트웨어 인터러브 0x2E를 호출한다.


- Windbg에서 "!idt" 명령을 사용하면 모든 인터럽트 디스크립터 테이블을 보여 주는데, 인터럽

  트 0x2E의 경우 ntoskrnl 모둘에 있는 KiSystemService()라는 루틴을 호출하는 것을 알 수 있다.

 

  * KiSystemService() : Application으로부터 입력받은 EAX의 서비스 번호를 참조하여 User  

    Level에서 호출한 함수와 매핑되는 Kernel Level의 함수를 호출해 주는 역할을 한다.

반응형

'Windows Programming > Knowledge base' 카테고리의 다른 글

svchost.exe  (0) 2011.06.29
COM 응용 기술  (0) 2010.04.01
최대 소켓연결 세션 늘리기  (0) 2009.01.09
IE와 관련된 몇가지 레지스트리 위치  (1) 2008.07.04
XP Firewall 관련 서비스(ICS)  (0) 2008.06.25
반응형

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);

        }

    }

}

 

반응형

+ Recent posts