Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 131. windbg/Visual Studio - HeapFree x86의 동작 분석 [링크 복사], [링크+제목 복사]
조회: 11354
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

windbg/Visual Studio - HeapFree x86의 동작 분석

들어가기에 앞서 우선 (개인적으로 ^^;) 헷갈리는 JUMP 기계어 코드를 정리합니다.

http://faydoc.tripod.com/cpu/jge.htm

JNE == Jump short if not equal (ZF = 0), Visual Studio의 경우 ZR = 0
JGE == Jump short if greater or equal (SF = OF), Visual Studio의 경우 PL = OV
JAE == Jump short if above or equal (CF = 0), Visual Studio의 경우 CY = 0

JL == Jump short if less (SF <> OF), Visual Studio의 경우 PL != OV

또한 Visual Studio의 경우 Flags 레지스터의 값을 디버깅 중에 확인하려면 "Debug" / "Windows" / "Registers(Ctrl + Alt + G)"를 선택해 "EFL" 값을 봐도 되지만, 기왕이면 각각의 플래그 값의 확인을 더욱 용이하게 할 수 있도록 다음과 같이 마우스 우 클릭해 나오는 메뉴에서 "Flags" 값을 check하면,

vs_heap_free_1.png

개별 필드 값을 아래의 단축 용어로 나눠서 확인할 수 있습니다.

OV (overflow)
UP (Direction)
EI (Enable Interrupt)
PL (Positive)
ZR (Zero)
AC (Auxilary Carry)
PE (Parity Even)
CY (Carry)

그 외에 레지스터 명칭 중 낯선 것 2개 소개합니다. ^^

SIL == ESI 레지스터의 하위 8비트
DIL == EDI 레지스터의 하위 8비트




아래는 이번 글의 실습 코드입니다.

// Windows 10 1909 - Visual C++ x86

#include <iostream>
#include <windows.h>

int main()
{
    int size = 20;

    HANDLE hHandle = HeapCreate(0, 0, 8192);
    printf("hHandle == 0x%x\n", hHandle);

    LPVOID pVoid1 = HeapAlloc(hHandle, 0, size);
    printf("pVoid1 == 0x%x\n", pVoid1);
    memset(pVoid1, 0xff, size);

    LPVOID pVoid2 = HeapAlloc(hHandle, 0, size + 1);
    printf("pVoid2 == 0x%x\n", pVoid2);
    memset(pVoid2, 0xee, size + 1);

    LPVOID pVoid3 = HeapAlloc(hHandle, 0, size + 2);
    printf("pVoid3 == 0x%x\n", pVoid3);
    memset(pVoid3, 0xcc, size + 2);

    LPVOID pVoid4 = HeapAlloc(hHandle, 0, 1);
    printf("pVoid4 == 0x%x\n", pVoid4);
    memset(pVoid4, 0xaa, 1);

    HeapFree(hHandle, 0, pVoid1);
    HeapFree(hHandle, 0, pVoid2);
    HeapFree(hHandle, 0, pVoid3);
    HeapFree(hHandle, 0, pVoid4);

    HeapDestroy(hHandle);

    printf("Exited!");

    return 0;
}

첫 번째 HeapFree(hHandle, 0, pVoid1)에서 BP를 잡고 실행했을 때 (제 경우) 각각의 포인터 값이 다음과 같이 나왔습니다.

hHandle == 0x19a0000
pVoid1 == 0x19a04b0
pVoid2 == 0x19a04d0
pVoid3 == 0x19a04f0
pVoid4 == 0x19a0510

여기서부터 Disassembly(Alt + G) 창을 띄워 아래의 "call esi" 명령까지 F11 키를 눌러 이동합니다.

    65:     HeapFree(hHandle, 0, pVoid1);
00E3110D 8B 35 04 20 E3 00    mov         esi,dword ptr [__imp__HeapFree@12 (0E32004h)]  
00E31113 83 C4 08             add         esp,8  
00E31116 C6 07 AA             mov         byte ptr [edi],0AAh  
00E31119 FF 75 F8             push        dword ptr [pVoid1]    // 세 번째 인자
00E3111C 6A 00                push        0                     // 두 번째 인자: dwFlags == 0
00E3111E FF 75 FC             push        dword ptr [hHandle]   // 첫 번째 인자: hHandle
00E31121 FF D6                call        esi  

그럼 IAT 테이블로 진입하게 되고,

_HeapFreeStub@12:
76BB1A70 8B FF                mov         edi,edi  
76BB1A72 55                   push        ebp  
76BB1A73 8B EC                mov         ebp,esp  
76BB1A75 5D                   pop         ebp  
76BB1A76 FF 25 E0 10 C2 76    jmp         dword ptr [__imp__HeapFree@12 (76C210E0h)] 

이제부터 본격적인 HeapFree 코드로 넘어갑니다.

771DDBD0 8B FF                mov         edi,edi  
771DDBD2 55                   push        ebp  
771DDBD3 8B EC                mov         ebp,esp  
771DDBD5 83 E4 F8             and         esp,0FFFFFFF8h  
771DDBD8 56                   push        esi  
771DDBD9 57                   push        edi  
771DDBDA 8B 7D 10             mov         edi,dword ptr [ebp+10h]  // edi == ebp + 10h == 세 번째 인자 pVoid1 
771DDBDD 85 FF                test        edi,edi  
771DDBDF 74 3D                je          RtlFreeHeap+4Eh (771DDC1Eh)  
771DDBE1 8B 75 08             mov         esi,dword ptr [ebp+8]  // esi == ebp + 8h == 첫 번째 인자 hHandle
771DDBE4 85 F6                test        esi,esi  
771DDBE6 0F 84 26 34 05 00    je          _RtlCaptureStackContext@12+7EF2h (77231012h)  
771DDBEC 81 7E 08 EE DD EE DD cmp         dword ptr [esi+8],0DDEEDDEEh  
771DDBF3 8B D7                mov         edx,edi  // _RtlpFreeHeapInternal로 첫 번째 인자 전달 (pVoid1)
771DDBF5 8B CE                mov         ecx,esi  // _RtlpFreeHeapInternal로 두 번째 인자 전달 (hHandle)
771DDBF7 0F 84 2B 34 05 00    je          _RtlCaptureStackContext@12+7F08h (77231028h)  
771DDBFD F6 05 EC E8 2B 77 02 test        byte ptr [_RtlpHpHeapFeatures (772BE8ECh)],2  
771DDC04 0F 85 2B 34 05 00    jne         _RtlCaptureStackContext@12+7F15h (77231035h)  
771DDC0A 6A 00                push        0  // _RtlpFreeHeapInternal로 세 번째 인자 전달
771DDC0C 6A 00                push        0  // _RtlpFreeHeapInternal로 네 번째 인자 전달
771DDC0E FF 75 0C             push        dword ptr [ebp+0Ch]  // _RtlpFreeHeapInternal로 다섯 번째를 HeapFree의 두 번째 인자로 전달
771DDC11 E8 42 89 04 00       call        _RtlpFreeHeapInternal@20 (77226558h) // ==> call
771DDC16 5F                   pop         edi  
771DDC17 5E                   pop         esi  
771DDC18 8B E5                mov         esp,ebp  
771DDC1A 5D                   pop         ebp  
771DDC1B C2 0C 00             ret         0Ch  
771DDC1E 5F                   pop         edi  
771DDC1F B8 01 00 00 00       mov         eax,1  
771DDC24 5E                   pop         esi  
771DDC25 8B E5                mov         esp,ebp  
771DDC27 5D                   pop         ebp  
771DDC28 C2 0C 00             ret         0Ch 

위의 코드는 별로 중요한 것이 없고 그냥 _RtlpFreeHeapInternal를 아래와 같은 식으로 호출하는 역할만 한다고 봐도 무방합니다.

_RtlpFreeHeapInternal(hHandle, pVoid1, 0, 0, 0);

이후 _RtlpFreeHeapInternal 코드로 넘어가서,

_RtlpFreeHeapInternal@20:
77226558 8B FF                mov         edi,edi  
7722655A 55                   push        ebp  
7722655B 8B EC                mov         ebp,esp  
7722655D 83 EC 40             sub         esp,40h  
77226560 53                   push        ebx  
77226561 56                   push        esi   
77226562 8B F1                mov         esi,ecx  // esi == hHandle
77226564 33 DB                xor         ebx,ebx  // ebx = 0
77226566 8B C2                mov         eax,edx  // eax == pVoid1
77226568 89 75 F8             mov         dword ptr [ebp-8],esi  // local_var1 == esi == hHandle
7722656B 57                   push        edi  
7722656C 89 45 F0             mov         dword ptr [ebp-10h],eax  // local_var2 == pVoid1
7722656F 8B FB                mov         edi,ebx  // edi == 0
77226571 81 7E 08 EE DD EE DD cmp         dword ptr [esi+8],0DDEEDDEEh  // cmp [hHandle + offset 8], 0DDEEDDEE 
                                                                        // cmp [0x019A0008] == 0xffeeffee
                                                                        // ZR = 0
77226578 0F 85 89 00 00 00    jne         _RtlpFreeHeapInternal@20+0AFh (77226607h) // ==> jump
...[생략]...
772265E1 5F                   pop         edi  
772265E2 5E                   pop         esi  
772265E3 5B                   pop         ebx  
772265E4 C9                   leave  
772265E5 C2 0C 00             ret         0Ch 

기본적인 변수 설정만 한 후 "jne _RtlpFreeHeapInternal@20+0AFh" 코드로 jump합니다. 이제부터 의미 있는 코드들이 나오는데,

77226607 F7 46 44 00 00 00 01 test        dword ptr [esi+44h],1000000h  // [hHandle + offset 44h] == 00000000
                                                                // 0x0 and 1000000h, ZR = 1
7722660E 8B 4D 08             mov         ecx,dword ptr [ebp+8]  // ecx = _RtlpFreeHeapInternal의 세 번째 인자 == 0
77226611 0F 85 B6 06 00 00    jne         _RtlpFreeHeapInternal@20+775h (77226CCDh)  
77226617 F6 46 48 01          test        byte ptr [esi+48h],1   // [hHandle + offset 48h] == 00000000
                                                        // 0x0 and 1 (ZR = 1)
7722661B 74 0B                je          _RtlpFreeHeapInternal@20+0D0h (77226628h) // ==> jump to 77226628
...[생략]...
...[생략]...
...[생략]...
77226628 A8 07                test        al,7  // eax == pVoid1(0x19a04b0), al == 0xb0
                                                // 0xb0 and 0x07 == 0x0 (ZR = 1)
7722662A 75 2A                jne         _RtlpFreeHeapInternal@20+0FEh (77226656h)  
7722662C 8D 78 F8             lea         edi,[eax-8]  // pVoid - 8 == 0x019A04A8
                                    0x019A04A8  2cdebacd // pVoid - 8 위치
                                    0x019A04AC  0c00bbf9 // pVoid - 4 위치
                                    0x019A04B0  ffffffff // pVoid 포인터가 가리키는 위치 - 데이터 구분을 위해 일부러 '0xff'로 채움
                                    0x019A04B4  ffffffff
                                    0x019A04B8  ffffffff
7722662F 80 7F 07 05          cmp         byte ptr [edi+7],5  // [edi + 7] == 0x0c
                                                              // cmp 0c, 5 == (ZF = 0)
77226633 75 09                jne         _RtlpFreeHeapInternal@20+0E6h (7722663Eh) // ==> jump to 7722663E
...[생략]...
...[생략]...
...[생략]...
7722663E F6 47 07 3F          test        byte ptr [edi+7],3Fh  // 0xc and 0x3f (ZR = 0)
77226642 75 20                jne         _RtlpFreeHeapInternal@20+10Ch (77226664h) // ==> jump to 77226664
...[생략]...
...[생략]...
...[생략]...
77226664 85 FF                test        edi,edi   // edi == 0x019A04A8 (pVoid - 8) (ZR = 0)
77226666 75 28                jne         _RtlpFreeHeapInternal@20+138h (77226690h) // ==> jump to 77226690
...[생략]...
...[생략]...
...[생략]...
77226690 8B 45 F0             mov         eax,dword ptr [ebp-10h]  // eax == [ebp - 10h] == local_var2 == pVoid1
77226693 80 78 FF 05          cmp         byte ptr [eax-1],5  // eax == 019a04b0, (byte)[eax - 1] == 0x0c
                                                              // cmp == 0x0c - 0x05 == 0x7 == (ZR = 0)
77226697 0F 85 45 01 00 00    jne         _RtlpFreeHeapInternal@20+28Ah (772267E2h) // ==> jump to 772267E2
...[생략]...
...[생략]...
...[생략]...
772267E2 38 5F 07             cmp         byte ptr [edi+7],bl   // [edi + 7] == 0x0c, BL == 0
                                                                // cmp == (ZR = 0, OV = 0, PL = 0)
772267E5 0F 8D DF 04 00 00    jge         _RtlpFreeHeapInternal@20+772h (77226CCAh) // ==> jump to 77226CCA
...[생략]...
...[생략]...
...[생략]...
77226CCA 8B 4D 08             mov         ecx,dword ptr [ebp+8]    // ecx = _RtlpFreeHeapInternal의 세 번째 인자 == 0
77226CCD 8B D1                mov         edx,ecx  // edx = 0
77226CCF 8B CE                mov         ecx,esi  // ecx = hHandle
77226CD1 50                   push        eax  
77226CD2 57                   push        edi  
77226CD3 83 CA 02             or          edx,2  
77226CD6 E8 95 6F FB FF       call        @RtlpFreeHeap@16 (771DDC70h)  
...[생략]...

보는 바와 같이 "pVoid"가 가리키고 있는 포인터보다 8바이트 앞선 위치를 edi 레지스터에 저장 후, 실질적으로는 dword('pVoid - 4') 위치의 상위 첫 바이트를 대상으로 다양한 체크를 하고 있습니다. 별다른 이상이 없다면 RtlpFreeHeap 함수를 다음과 같은 형식으로 호출하게 됩니다.

RtlpFreeHeap(hHandle, 0, pVoid, pVoid - 8, 0 | 2);

나중에 잠깐 다루겠지만, dword('pVoid - 4') 위치의 상위 첫 바이트가 이상이 없다고 해도 RtlpFreeHeap 내부에서 한 번 더 dword(pVoid - 8), dword(pVoid - 4) 위치의 값을 테스트하게 되고 그 과정에서 변조가 판단되면 비정상 종료가 발생하게 됩니다.




일부러 비정상 종료를 한번 유도해 볼까요? ^^ HeapFree를 하기 전 다음과 같은 코드를 넣어두고,

*(((BYTE*)pVoid1) - 4) = 0x01;

실행하면, dword(pVoid - 4)의 최하위 바이트가 바뀌게 됩니다.

0x00E004A8  05010004 
0x00E004AC  0c000101 
0x00E004B0  ffffffff 
0x00E004B4  ffffffff 

ASLR이나, Heap의 다양한 내부 상태 값으로 인해 저 하나의 값이 어떻게 영향을 미칠지는 알 수 없습니다. 우선, 이런 식의 변조를 했을 때 RtlReportFatalFailure로 끝나는 사례를 먼저 살펴보겠습니다. (참고로, 운에 따라 비정상 종료하지 않는 경우도 있습니다.)

    ntdll.dll!_RtlReportFatalFailure@4()    Unknown
    ntdll.dll!_RtlReportCriticalFailure@12()    Unknown
    ntdll.dll!_RtlpReportHeapFailure@4()    Unknown
    ntdll.dll!_RtlpHpHeapHandleError@12()   Unknown
    ntdll.dll!_RtlpLogHeapFailure@24()  Unknown
    ntdll.dll!_RtlpAnalyzeHeapFailure@12()  Unknown
    ntdll.dll!@RtlpFreeHeap@16()    Unknown
    ntdll.dll!_RtlpFreeHeapInternal@20()    Unknown
    ntdll.dll!RtlFreeHeap() Unknown
>    ConsoleApplication1.exe!main() Line 60  C++

이렇게 되면 Visual Studio 디버깅 중에는 다음과 같은 식의 예외 메시지 창이 출력됩니다.

Unhandled exception at 0x7727F94D (ntdll.dll) in ConsoleApplication1.exe: 0xC0000374: A heap has been corrupted (parameters: 0x772BB960). occurred


정확히 "heap" 영역이 "corrupted"되었다고 알려주고 있으며 이 상황을 유발한 몇몇 문맥 정보를 ("parameters: 0x......")로 알려주고 있습니다. 실제로 이 주소의 메모리 값을 확인해 보면,

0x772BB960  00000002
0x772BB964  000003d0
0x772BB968  00000004
0x772BB96C  00e00000 // hHandle
0x772BB970  00e001e8
0x772BB974  00000000
0x772BB978  00000000
0x772BB97C  00000000
0x772BB980  00e00000 // hHandle
0x772BB984  00e004a8 // (pVoid - 8) 주소
0x772BB988  00000000
0x772BB98C  00000000

저렇게 Heap handle 값과 Free를 시도하려고 했던 주소의 (헤더 위치로 보정된) pVoid 주솟값을 알 수 있습니다. 따라서, 원래 HeapFree로 전달한 값이 00e004a8 + 0x8 == e004b0 임을 알 수 있습니다.




그런데, 또 다른 식으로 비정상 종료하는 케이스가 있습니다.

0x013B04A8  05010004 
0x013B04AC  0c001401 
0x013B04B0  ffffffff 
0x013B04B4  ffffffff 

역시 마찬가지로 하위 1바이트만 0x01 값으로 채운 경우인데 다음과 같이 오류 메시지 및 callstack이 다릅니다.

Unhandled exception at 771DE00D (ntdll.dll) in ConsoleApplication1.exe: 0xC0000005: Access violation reading location 0x00C9FDF2.


    ntdll.dll!@RtlpFreeHeap@16()    Unknown
    ntdll.dll!_RtlpFreeHeapInternal@20()    Unknown
    ntdll.dll!RtlFreeHeap() Unknown
>    ConsoleApplication1.exe!main() Line 60  C++

실제로 0x773CE00D 위치의 코드를 보면,

771DE002 8B 4F 4C             mov         ecx,dword ptr [edi+4Ch]  
771DE005 8B C1                mov         eax,ecx  
771DE007 C1 E8 14             shr         eax,14h  
771DE00A 22 47 52             and         al,byte ptr [edi+52h] // edi ==  0x013b0000
771DE00D 32 43 02             xor         al,byte ptr [ebx+2] // ebx == 0x00C9FDF0 == (invalid memory pointer)

xor의 두 번째 오퍼랜드의 주소가 (디버거에서 알려준) 0x00C9FDF2이고 이 주솟값의 영역은 한 번도 할당한 적이 없으므로 Access Violation 예외가 발생한 것입니다. 이전에 살펴 본 _RtlReportFatalFailure의 경우 문맥 정보를 통해 해제하려는 주솟값을 알아낼 수 있었지만, AV 예외가 발생한 이번에는 그 주소를 알기 쉽지 않습니다. 우선, 대충 각 callstack의 frame 별로 레지스터를 조사해 보면 다음과 같이 주솟값이 (운이 좋게) 나오는 것을 볼 수 있습니다. (또는, 각각의 함수 내부 코드를 통해 stack에 보관된 값을 찾아내는 식으로 조사할 수 있습니다.)

[ntdll.dll!@RtlpFreeHeap@16()]
esi == 013B04A8 == pVoid - 8

[ntdll.dll!_RtlpFreeHeapInternal@20()]
edi == 013B04A8 == pVoid - 8

[ntdll.dll!RtlFreeHeap()]
edi == 013B04B0 == pVoid

물론, 위의 상황은 운영체제의 버전/패치에 따라 달라질 수 있으므로 적당하게 취할 수 있는 센스를 발휘해야 합니다. ^^




이 정도면, Heap corruption 관련 예외가 발생했을 때 어떤 메모리를 해제하려고 했는지는 쉽게(?) 찾을 수 있을 것입니다. 다음번 글에서는 이번에 다뤘던 정보를 바탕으로 지난 글의 덤프 분석 사례를,

windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12058

좀 더 깊이 있게 들어가 보겠습니다. ^^




마지막으로, pVoid + 8, pVoid + 4 위치의 값을 체크하는 코드를 아래에 (필요한 정도로만) 정리합니다.

772267E2 38 5F 07             cmp         byte ptr [edi+7],bl  // from 77226697
                                                            // [edi + 7] == 8c, BL == 0
                                                            // cmp == (ZF = 0, OV = 0, PL = 1)
772267E5 0F 8D DF 04 00 00    jge         _RtlpFreeHeapInternal@20+772h (77226CCAh)                  
772267EB 8B CF                mov         ecx,edi   // ecx == 0x01433A70
772267ED C1 E9 03             shr         ecx,3     // ecx == 0x0028674E
772267F0 33 0F                xor         ecx,dword ptr [edi]  
                                                    // ecx == 0x0028674E xor 0x2e3b9086 == 0x2e13f7c8
772267F2 33 0D 14 E9 2B 77    xor         ecx,dword ptr [_RtlpLFHKey (772BE914h)]  // [772BE914h] == 2f1df7c8
                                                    // 0x2e13f7c8 xor 2f1df7c8 == 10E0000
772267F8 33 CE                xor         ecx,esi   // 10E0000 xor g_hHeap == 01420000 == 4C0000
772267FA 66 85 C9             test        cx,cx     // cx == 0000 (ZR == 1)
772267FD 0F 85 B4 04 00 00    jne         _RtlpFreeHeapInternal@20+75Fh (77226CB7h)   // 하위 4바이트가 0이 아니라면 _RtlpLogHeapFailure
77226803 C1 E9 0D             shr         ecx,0Dh   // ecx == 0x260
77226806 8B C7                mov         eax,edi   // 0x01433A70 (pVoid - 8)
77226808 2B C1                sub         eax,ecx   // 0x01433A70 - 0x260 = 0x1433810
7722680A 8B 08                mov         ecx,dword ptr [eax]  // ecx == [0x1433810] 위치의 값
7722680C 89 4D F4             mov         dword ptr [ebp-0Ch],ecx  // [ebp - 0Ch] == ecx == [0x1433810] 위치의 값
7722680F 85 C9                test        ecx,ecx  // cx == 00fbcbc8 (ZR == 0)
77226811 0F 84 A0 04 00 00    je          _RtlpFreeHeapInternal@20+75Fh (77226CB7h)  // ecx == 0이면 _RtlpLogHeapFailure
                                                                 // 두 번째 쿠키 값 검증
77226817 8B 47 04             mov         eax,dword ptr [edi+4]  // eax == 8c001265
7722681A 8B 71 04             mov         esi,dword ptr [ecx+4]  // esi == [[0x1433810] 위치의 값 + offset 4]
7722681D C1 E8 08             shr         eax,8  
77226820 0F B7 D0             movzx       edx,ax  
77226823 8B 01                mov         eax,dword ptr [ecx]  
77226825 89 75 EC             mov         dword ptr [ebp-14h],esi  
77226828 89 5D E4             mov         dword ptr [ebp-1Ch],ebx  
7722682B 89 55 E0             mov         dword ptr [ebp-20h],edx  
7722682E 8B 00                mov         eax,dword ptr [eax]  
77226830 8B 48 0C             mov         ecx,dword ptr [eax+0Ch]  
77226833 8B 46 10             mov         eax,dword ptr [esi+10h]  
77226836 33 C1                xor         eax,ecx  
77226838 89 4D D8             mov         dword ptr [ebp-28h],ecx  
7722683B 33 C6                xor         eax,esi  
7722683D 33 05 14 E9 2B 77    xor         eax,dword ptr [_RtlpLFHKey (772BE914h)]  
77226843 8B F0                mov         esi,eax  
77226845 0F B7 C0             movzx       eax,ax  
77226848 C1 EE 10             shr         esi,10h  
7722684B 0F AF F2             imul        esi,edx  
7722684E 03 75 EC             add         esi,dword ptr [ebp-14h]  
77226851 03 C6                add         eax,esi  
77226853 3B C7                cmp         eax,edi  
77226855 74 1A                je          _RtlpFreeHeapInternal@20+319h (77226871h) 
77226857 8B 51 0C             mov         edx,dword ptr [ecx+0Ch]  
7722685A 53                   push        ebx  
7722685B 53                   push        ebx  
7722685C 53                   push        ebx  
7722685D 57                   push        edi  
7722685E 6A 03                push        3  
77226860 59                   pop         ecx  
77226861 E8 A9 A4 06 00       call        _RtlpLogHeapFailure@24 (77290D0Fh) // ==> failure




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/7/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...
NoWriterDateCnt.TitleFile(s)
13294정성태3/22/20234121.NET Framework: 2105. LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리 - 두 번째
13293정성태3/22/20234191오류 유형: 853. dumpbin - warning LNK4048: Invalid format file; ignored
13292정성태3/21/20234305Windows: 232. C/C++ - 일반 창에도 사용 가능한 IsDialogMessage파일 다운로드1
13291정성태3/20/20234712.NET Framework: 2104. C# Windows Forms - WndProc 재정의와 IMessageFilter 사용 시의 차이점
13290정성태3/19/20234219.NET Framework: 2103. C# - 윈도우에서 기본 제공하는 FindText 대화창 사용법파일 다운로드1
13289정성태3/18/20233416Windows: 231. Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 자식 윈도우를 생성하는 방법파일 다운로드1
13288정성태3/17/20233515Windows: 230. Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법파일 다운로드1
13287정성태3/16/20233683Windows: 229. Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 윈도우를 직접 띄우는 방법파일 다운로드1
13286정성태3/15/20234146Windows: 228. Win32 - 리소스에 포함된 대화창 Template의 2진 코드 해석 방법
13285정성태3/14/20233736Windows: 227. Win32 C/C++ - Dialog Procedure를 재정의하는 방법파일 다운로드1
13284정성태3/13/20233936Windows: 226. Win32 C/C++ - Dialog에서 값을 반환하는 방법파일 다운로드1
13283정성태3/12/20233477오류 유형: 852. 파이썬 - TypeError: coercing to Unicode: need string or buffer, NoneType found
13282정성태3/12/20233807Linux: 58. WSL - nohup 옵션이 필요한 경우
13281정성태3/12/20233716Windows: 225. 윈도우 바탕화면의 아이콘들이 넓게 퍼지는 경우 [2]
13280정성태3/9/20234452개발 환경 구성: 670. WSL 2에서 호스팅 중인 TCP 서버를 외부에서 접근하는 방법
13279정성태3/9/20233996오류 유형: 851. 파이썬 ModuleNotFoundError: No module named '_cffi_backend'
13278정성태3/8/20233940개발 환경 구성: 669. WSL 2의 (init이 아닌) systemd 지원 [1]
13277정성태3/6/20234575개발 환경 구성: 668. 코드 사인용 인증서 신청 및 적용 방법(예: Digicert)
13276정성태3/5/20234306.NET Framework: 2102. C# 11 - ref struct/ref field를 위해 새롭게 도입된 scoped 예약어
13275정성태3/3/20234660.NET Framework: 2101. C# 11의 ref 필드 설명
13274정성태3/2/20234252.NET Framework: 2100. C# - ref 필드로 ref struct 타입을 허용하지 않는 이유
13273정성태2/28/20233953.NET Framework: 2099. C# - 관리 포인터로서의 ref 예약어 의미
13272정성태2/27/20234199오류 유형: 850. SSMS - mdf 파일을 Attach 시킬 때 Operating system error 5: "5(Access is denied.)" 에러
13271정성태2/25/20234142오류 유형: 849. Sql Server Configuration Manager가 시작 메뉴에 없는 경우
13270정성태2/24/20233754.NET Framework: 2098. dotnet build에 /p 옵션을 적용 시 유의점
13269정성태2/23/20234289스크립트: 46. 파이썬 - uvicorn의 콘솔 출력을 UDP로 전송
1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...