심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  


   심플스 배너



CreateIoCompletionPort 함수는 내부적으로 NtCreateIoCompletion 함수를 호출하게 되는데
다음과 같이 생겼습니다. ( 디컴파일 결과만 보여줍니다. )

NTSTATUS __stdcall NtCreateIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads)
{
  void *v4; // ecx@2
  PHANDLE v5; // edi@2
  char v7; // al@1
  int v8; // eax@6
  int v9; // esi@7
  HANDLE *v10; // eax@7
  int PreviousMode; // [sp+1Ch] [bp-1Ch]@1
  CPPEH_RECORD ms_exc; // [sp+20h] [bp-18h]@4
  int v13; // [sp+10h] [bp-28h]@6
  void *v14; // [sp+18h] [bp-20h]@7

  v7 = *(_BYTE *)(*MK_FP(__FS__, 292) + 314);
  LOBYTE(PreviousMode) = *(_BYTE *)(*MK_FP(__FS__, 292) + 314);
  if ( v7 )
  {
    v5 = IoCompletionHandle;
    v4 = IoCompletionHandle;
    if ( (unsigned int)IoCompletionHandle >= (unsigned int)MmUserProbeAddress )
      v4 = MmUserProbeAddress;
    *(_DWORD *)v4 = *(_DWORD *)v4;
    ms_exc.disabled = -2;
  }
  else
  {
    v5 = IoCompletionHandle;
  }
  v8 = ObCreateObject(
         PreviousMode,
         (int)IoCompletionObjectType,
         (int)ObjectAttributes,
         PreviousMode,
         0,
         48,
         0,
         0,
         (int)&v13);
  IoCompletionHandle = (PHANDLE)v8;
  if ( v8 >= 0 )
  {
    v9 = v13;
    KeInitializeQueue(v13, NumberOfConcurrentThreads);
    *(_DWORD *)(v9 + 40) = 0;
    *(_BYTE *)(v9 + 44) = 0;
    v10 = (HANDLE *)ObInsertObject(v9, 0, DesiredAccess, 0, 0, &v14);
    IoCompletionHandle = v10;
    if ( (signed int)v10 >= 0 )
    {
      if ( (_BYTE)PreviousMode )
        *v5 = v14;
      else
        *v5 = v14;
    }
  }
  return (NTSTATUS)IoCompletionHandle;
}

함수를 열자마자 바로 실체가 나옵니다.
KeInitializeQueue 를 호출하는 것을 볼 수 있습니다.
즉, IOCP를 초기화하는 과정은 어떠한 특수한 함수를 호출하는것도 아니며 그냥 커널에 있는 함수인
KeInitializeQueue 함수를 호출하며 결국엔 커널에 큐잉모델을 단지 이용하는 것일뿐입니다.

그렇다면 PostQueuedCompletionStatus 함수는 구지 안봐도 어떠한 함수를 호출할지 뻔하지만
그래도 최종적으로 호출되는 코드는 다음과 같습니다.

NTSTATUS __stdcall NtSetIoCompletion(HANDLE IoCompletionHandle, ULONG CompletionKey, ULONG CompletionValue, NTSTATUS Status, ULONG Information)
{
  signed int v5; // esi@1
  NTSTATUS v7; // eax@1
  int AccessMode; // [sp+4h] [bp-8h]@1
  PVOID Object; // [sp+8h] [bp-4h]@1

  LOBYTE(AccessMode) = *(_BYTE *)(*MK_FP(__FS__, 292) + 314);
  v7 = ObReferenceObjectByHandle(IoCompletionHandle, 2u, IoCompletionObjectType, AccessMode, &Object, 0);
  v5 = v7;
  if ( v7 >= 0 )
  {
    v5 = IoSetIoCompletion((int)Object, CompletionKey, CompletionValue, Status, Information, 1);
    ObfDereferenceObject();
  }
  return v5;
}

signed int __stdcall IoSetIoCompletion(int a1, int a2, int a3, int a4, int a5, int a6)
{
  return IoSetIoCompletionEx(a1, a2, a3, a4, a5, a6, 0);
}

igned int __stdcall IoSetIoCompletionEx(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
{
  void *v7; // eax@1
  signed int v8; // esi@1

  v7 = (void *)a7;
  v8 = 0;
  if ( a7 || (v7 = IopAllocateMiniCompletionPacket(1, a6), v7) )
  {
    *((_DWORD *)v7 + 3) = a2;
    *((_DWORD *)v7 + 4) = a3;
    *((_DWORD *)v7 + 5) = a4;
    *((_DWORD *)v7 + 6) = a5;
    KiInsertQueue(v7, 0, 0);
  }
  else
  {
    v8 = -1073741670;
  }
  return v8;
}

보시다시피 KiInsertQueue를 호출합니다. ( 참고로 KeInsertQueue 함수는 KiInsertQueue를 내부적으로 호출하므로
거의 동일하다고 보시면 됩니다. )

그렇다면 이제 PostQueuedCompletionStatus 함수에 실패여부도 고려를 해야 할 상황입니다.
가장 첫 번째로 IOCP핸들을 잘 못 넘겼을 경우인데 그 부분은 제외하겠습니다.
또한 KeInsertQueue 함수자체도 에러코드를 리턴하지 않습니다.
하지만 여기서 중요한 것은 위에서 IopAllocateMiniCompletionPacket 함수를 호출하는 부분입니다.
다음과 같이 생겼습니다.

PVOID __stdcall IopAllocateMiniCompletionPacket(int a1, char a2)
{
  SIZE_T ST04_4_0; // ST04_4@0
  ULONG ST08_4_0; // ST08_4@0
  EX_POOL_PRIORITY ST0C_4_0; // ST0C_4@0
  int ST10_4_0; // ST10_4@0
  int ST14_4_0; // ST14_4@0
  int ST18_4_0; // ST18_4@0
  int ST1C_4_0; // ST1C_4@0
  PVOID result; // eax@2
  POOL_TYPE v10; // ebx@2
  int v11; // edi@2
  int v12; // esi@2
  int v13; // esi@3
  PVOID v14; // [sp+10h] [bp-1Ch]@2

  if ( a1 == 3 )
  {
    v10 = 0;
    ST0C_4_0 = 0;
    ST08_4_0 = 544236361;
    ST04_4_0 = 40;
    goto LABEL_8;
  }
  v11 = *MK_FP(__FS__, 32);
  v12 = *(_DWORD *)(*MK_FP(__FS__, 32) + 1496);
  ++*(_DWORD *)(v12 + 12);
  result = (PVOID)RtlpInterlockedPopEntrySList(ST10_4_0, ST14_4_0);
  v14 = result;
  v10 = 0;
  if ( !result )
  {
    ++*(_DWORD *)(v12 + 16);
    v13 = *(_DWORD *)(v11 + 1500);
    ++*(_DWORD *)(v13 + 12);
    result = (PVOID)RtlpInterlockedPopEntrySList(ST18_4_0, ST1C_4_0);
    v14 = result;
    if ( !result )
    {
      ++*(_DWORD *)(v13 + 16);
      if ( a2 )
      {
        a1 = 2;
        v14 = ExAllocatePoolWithQuotaTag(0, 0x1Cu, 0x20706349u);
        result = v14;
        goto LABEL_9;
      }
LABEL_8:
      result = ExAllocatePoolWithTagPriority(v10, ST04_4_0, ST08_4_0, ST0C_4_0);
LABEL_9:
      if ( result == (PVOID)v10 )
        return result;
      goto LABEL_10;
    }
  }
LABEL_10:
  *((_DWORD *)result + 2) = a1;
  return result;
}

첫 번째로 고려해야 할 함수는 RtlpInterlockedPopEntrySList 입니다.
이 함수는 다음과 같이 생겼습니다.

.text:00439378 ; __fastcall RtlpInterlockedPopEntrySList(x)
.text:00439378                 public @RtlpInterlockedPopEntrySList@4
.text:00439378 @RtlpInterlockedPopEntrySList@4 proc near
.text:00439378                                         ; CODE XREF: MiAllocateMdlPagesByLists(x,x,x,x,x,x,x)+183
.text:00439378                                         ; KeAllocateInterrupt(x)+46
.text:00439378
.text:00439378 var_C           = dword ptr -0Ch
.text:00439378
.text:00439378                 push    ebx             ; ExInterlockedPopEntrySList
.text:00439379                 push    ebp
.text:0043937A                 mov     ebp, ecx
.text:0043937C                 sub     esp, 4
.text:0043937F                 xor     eax, eax
.text:00439381                 mov     [esp+0Ch+var_C], eax
.text:00439381 @RtlpInterlockedPopEntrySList@4 endp ; sp-analysis failed

전혀 고려대상이 아닙니다.
단순한 인라인 함수정도로 생각하면 될것입니다.
( 함수가 좀 길어서 다 올리지 않았습니다. 보시면 맨 마지막에 스택분석 실패라고 나온것을 볼 수 잇죠..
  밑에 코드가 사실은 더 있습니다. )

그 다음에 마지막으로 고려해야 할 대상은 ExAllocatePoolWithQuotaTag 함수입니다.
( ExAllocatePoolWithTagPriority 함수도 호출하는데 이 함수는 메모리가 부족하 경우 NULL을 리턴합니다. )
이 함수는 특이하게도 메모리가 부족해서 함수가 실패할 경우 NULL을 리턴하는게 아니라
예외 핸들러를 실행시킵니다.
해당 함수가 디컴파일이 되어서 안 보이겠지만 사실은 예외 핸들러가 작성되어 있습니다.

PAGE:00669EDA ; __stdcall IopAllocateMiniCompletionPacket(x, x)
PAGE:00669EDA _IopAllocateMiniCompletionPacket@8 proc near
PAGE:00669EDA                                         ; CODE XREF: IoSetIoCompletionEx(x,x,x,x,x,x,x)+14
PAGE:00669EDA                                         ; IoAllocateMiniCompletionPacket(x,x)+9
PAGE:00669EDA
PAGE:00669EDA var_1C          = dword ptr -1Ch
PAGE:00669EDA ms_exc          = CPPEH_RECORD ptr -18h
PAGE:00669EDA arg_0           = dword ptr  8
PAGE:00669EDA arg_4           = byte ptr  0Ch
PAGE:00669EDA
PAGE:00669EDA                 push    0Ch
PAGE:00669EDC                 push    offset dword_44C338
PAGE:00669EE1                 call    __SEH_prolog4
PAGE:00669EE6                 cmp     [ebp+arg_0], 3
PAGE:00669EEA                 jz      loc_669F74
PAGE:00669EF0                 mov     edi, large fs:20h
PAGE:00669EF7                 mov     esi, [edi+5D8h]
PAGE:00669EFD                 inc     dword ptr [esi+0Ch]
PAGE:00669F00                 mov     ecx, esi
PAGE:00669F02                 call    @RtlpInterlockedPopEntrySList@4 ; RtlpInterlockedPopEntrySList(x)
PAGE:00669F07                 mov     [ebp+var_1C], eax
PAGE:00669F0A                 xor     ebx, ebx
PAGE:00669F0C                 cmp     eax, ebx
PAGE:00669F0E                 jnz     short loc_669F88
PAGE:00669F10                 inc     dword ptr [esi+10h]
PAGE:00669F13                 mov     esi, [edi+5DCh]
PAGE:00669F19                 inc     dword ptr [esi+0Ch]
PAGE:00669F1C                 mov     ecx, esi
PAGE:00669F1E                 call    @RtlpInterlockedPopEntrySList@4 ; RtlpInterlockedPopEntrySList(x)
PAGE:00669F23                 mov     [ebp+var_1C], eax
PAGE:00669F26                 cmp     eax, ebx
PAGE:00669F28                 jnz     short loc_669F88
PAGE:00669F2A                 inc     dword ptr [esi+10h]
PAGE:00669F2D                 cmp     [ebp+arg_4], bl
PAGE:00669F30                 jz      short loc_669F6A
PAGE:00669F32                 mov     [ebp+arg_0], 2
PAGE:00669F39                 mov     [ebp+ms_exc.disabled], ebx
PAGE:00669F3C                 push    20706349h       ; Tag
PAGE:00669F41                 push    1Ch             ; NumberOfBytes
PAGE:00669F43                 push    ebx             ; PoolType
PAGE:00669F44                 call    _ExAllocatePoolWithQuotaTag@12 ; ExAllocatePoolWithQuotaTag(x,x,x)
PAGE:00669F49                 mov     [ebp+var_1C], eax
PAGE:00669F4C                 mov     [ebp+ms_exc.disabled], 0FFFFFFFEh
PAGE:00669F53                 jmp     short loc_669F65
PAGE:00669F55 ; ---------------------------------------------------------------------------
PAGE:00669F55
PAGE:00669F55 loc_669F55:                             ; DATA XREF: .text:0044C34C
PAGE:00669F55                 xor     eax, eax
PAGE:00669F57                 inc     eax
PAGE:00669F58                 retn
PAGE:00669F59 ; ---------------------------------------------------------------------------
PAGE:00669F59
PAGE:00669F59 loc_669F59:                             ; DATA XREF: .text:0044C350
PAGE:00669F59                 mov     esp, [ebp+ms_exc.old_esp]
PAGE:00669F5C                 mov     [ebp+ms_exc.disabled], 0FFFFFFFEh
PAGE:00669F63                 xor     ebx, ebx
PAGE:00669F65
PAGE:00669F65 loc_669F65:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+79
PAGE:00669F65                 mov     eax, [ebp+var_1C]
PAGE:00669F68                 jmp     short loc_669F84
PAGE:00669F6A ; ---------------------------------------------------------------------------
PAGE:00669F6A
PAGE:00669F6A loc_669F6A:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+56
PAGE:00669F6A                 push    ebx
PAGE:00669F6B                 push    20706349h
PAGE:00669F70                 push    1Ch
PAGE:00669F72                 jmp     short loc_669F7E
PAGE:00669F74 ; ---------------------------------------------------------------------------
PAGE:00669F74
PAGE:00669F74 loc_669F74:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+10
PAGE:00669F74                 xor     ebx, ebx
PAGE:00669F76                 push    ebx             ; Priority
PAGE:00669F77                 push    20706349h       ; Tag
PAGE:00669F7C                 push    28h             ; NumberOfBytes
PAGE:00669F7E
PAGE:00669F7E loc_669F7E:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+98
PAGE:00669F7E                 push    ebx             ; PoolType
PAGE:00669F7F                 call    _ExAllocatePoolWithTagPriority@16 ; ExAllocatePoolWithTagPriority(x,x,x,x)
PAGE:00669F84
PAGE:00669F84 loc_669F84:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+8E
PAGE:00669F84                 cmp     eax, ebx
PAGE:00669F86                 jz      short loc_669F8E
PAGE:00669F88
PAGE:00669F88 loc_669F88:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+34
PAGE:00669F88                                         ; IopAllocateMiniCompletionPacket(x,x)+4E
PAGE:00669F88                 mov     ecx, [ebp+arg_0]
PAGE:00669F8B                 mov     [eax+8], ecx
PAGE:00669F8E
PAGE:00669F8E loc_669F8E:                             ; CODE XREF: IopAllocateMiniCompletionPacket(x,x)+AC
PAGE:00669F8E                 call    __SEH_epilog4
PAGE:00669F93                 retn    8
PAGE:00669F93 _IopAllocateMiniCompletionPacket@8 endp ; sp-analysis failed



이쯤에서 PostQueuedCompletionStatus 함수가 실패할 경우는 다음과 같이 요약됩니다.
첫 번째로 핸들이 잘 못 될 경우 ( 그러나 프로그램 로직상에 문제로 발생할 수 있기 때문에 무시 )
두 번째로 시스템 메모리가 부족 할 경우..

시스템 메모리가 부족 할 경우에는 프로그래머의 잘 못으로 일어나는 현상이 아니므로
PostQueuedCompletionStatus 함수는 반드시 실패할 수 있기 때문에 큐를 대기하는 쪽에서는
이부분을 염두해 두어야 합니다. ( 물론 이렇게 코드를 작성하기가 매우 매우 힘들겠지만.. )
List of Articles
번호 제목 글쓴이 날짜 조회 수
163 문서자료 json 문법 체크 사이트 Lyn 2012-02-28 4570
162 Windows Research SCSI Miniport - IOCTL_SCSI_PASS_THROUGH_DIRECT ( Windows ... lain 2011-06-11 7893
161 Windows Research 커널레벨에서 IAT Hook lain 2011-06-11 9253
160 Windows Research DEVICE_OBJECT에 대한 보안 디스크립터 변경 lain 2011-06-11 7021
159 Windows Research IE Cache 경로 변경하기 lain 2011-06-09 25658
158 Windows Research 32Bit 윈도우즈에서 실제 물리메모리크기 얻어오기 lain 2011-06-09 7291
157 Windows Research VMWare 탐지 기법 우회 - 2 [2] lain 2011-04-04 17404
156 Linux Tip 우분투에 IRC서버 설치후 닉네임 길이와 동접자 조절. [1] 오랑캐꽃 2010-11-26 10308
155 Linux Tip VirtualBox에 우분투 설치 후에 내부 네트워크 접속 설정하기 file esniper 2010-09-10 24338
154 Linux Tip VirtualBox에서 오른쪽 CTRL키 사용하기(VitualBox 호스트키) file [2] esniper 2010-09-10 11532
153 Linux Tip 우분투 설치 후 putty에서 한글 안 깨지도록 설정하기 file esniper 2010-09-10 10999
152 Linux Tip 우분투 ssh or mysql 서버 접속지연이 있는 경우 해결책 esniper 2010-09-10 10638
151 문서자료 고품질의 무료 아이콘들 [3] esniper 2010-09-08 12887
150 Windows Research Sysinternals - Filemon source code file [5] lain 2010-09-04 9845
149 Windows Research Sysinternals - Regmon source code file [1] lain 2010-09-04 9471
» Windows Research PostQueuedCompletionStatus 함수에 실패 여부도 고려해야 할까? lain 2010-09-04 8317
147 Windows Research SetEvent 함수는 언제 실패할까? lain 2010-09-04 9833
146 Windows Research 커널모드 드라이버에서 사용하는 시간관련 매크로 lain 2010-09-04 7931
145 Windows Research 화면캡쳐방지는 어떻게 구현될까? [3] lain 2010-09-04 16688
144 Windows Research IRP Completion Hook file lain 2010-09-04 7847

  • 이용약관
  • 개인정보취급방침
  • 사이트맵