Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 3개 있습니다.)
디버깅 기술: 213. Windbg - swapgs 명령어와 (Ring 0 커널 모드의) FS, GS Segment 레지스터
; https://www.sysnet.pe.kr/2/0/13853

디버깅 기술: 214. Windbg - syscall 단계까지의 Win32 API 호출 (예: Sleep)
; https://www.sysnet.pe.kr/2/0/13856

디버깅 기술: 215. Windbg - syscall 이후 실행되는 KiSystemCall64 함수 및 SSDT 디버깅
; https://www.sysnet.pe.kr/2/0/13859




Windbg - syscall 이후 실행되는 KiSystemCall64 함수 및 SSDT 디버깅

지난 글에서, ntdll.dll에서 제공하는 각종 함수들은 syscall로 인해 커널에서 제공하는 함수인 KiSystemCall64로 넘어오는 것을 설명했습니다.

Windbg - syscall 단계까지의 Win32 API 호출 (예: Sleep)
; https://www.sysnet.pe.kr/2/0/13856

즉, KiSystemCall64까지 진입한 경우라면 이미 Ring 3에서 Ring 0으로 특권 계층이 바뀐 상태가 된 것입니다. 이 부분에서 재미있는 점이 하나 있는데요, WinDbg를 이용해 KiSystemCall64 호출로 넘어가는 단계는 trace/bp 등으로 추적이 불가능하다는 점입니다.

가령, syscall 단계에서 F11 키를 눌러 trace into를 시도하면, 그 순간 Debugee 측의 운영체제는 (블루 스크린이 뜨거나) CPU 100% 현상을 보이며 호스트 측의 WinDbg는 더 이상 반응하지 않게 됩니다. (결국 Debugee 측을 재부팅해야 합니다.)

이에 대해 검색해 보면 다음의 동영상을 찾을 수 있는데요,

How To Step Into a SYSCALL With a Debugger (via Kernel Binary Patch) | Reverse Engineering Win10 x64
; https://youtu.be/uw0kIsHiG0k?t=926

Online x86 / x64 Assembler and Disassembler
; https://defuse.ca/online-x86-assembler.htm#disassembly

제목에도 나오지만, 디버깅 시 trampoline 방식으로 KiSystemCall64 함수의 내부에 직접 기계어 코드를 패치해 (BP가 아닌) BA를 걸어 디버깅하는 방법을 소개하고 있습니다. 왜 그렇게 해야 하는가에 대해서는 동영상에서 자세하게 설명하고 있는데요, 대충 정리를 해보면 이렇습니다.

우선 KiSystemCall64 함수를 찾아,

3: kd> rdmsr 0xc0000082
msr[c0000082] = fffff802`3d62a000

// 또는, 심벌로 확인
3: kd> x nt!KiSystemCall64
fffff802`3d62a000 nt!KiSystemCall64 (KiSystemCall64)

그 위치의 기계어 코드를 역어셈블하면 도입부가 이런 식으로 나옵니다.

// syscall 호출 시 rcx 레지스터에 호출 IP가 들어가 있음

3: kd> u nt!KiSystemCall64 L6
nt!KiSystemCall64:
fffff802`3d62a000 0f01f8          swapgs // gs 레지스터 교환
fffff802`3d62a003 654889242510000000 mov   qword ptr gs:[10h],rsp // 스택 레지스터 교환을 위해 현재의 rsp 값을 보관

fffff802`3d62a00c 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h] // 커널 스택으로 변경 (DPL == 0인 selector로 변경)

fffff802`3d62a015 6a2b            push    2Bh
fffff802`3d62a017 65ff342510000000 push    qword ptr gs:[10h]
fffff802`3d62a01f 4153            push    r11

syscall을 호출하면 저 함수로 진입한 이후, 초반에 swapgs를 통해 커널 모드를 위한 gs 레지스터까지 변경하는 절차를 거치는데요, 이로 인해 스레드별 문맥 정보에 해당하는 KPCR 구조체를 접근할 수 있게 됩니다.

이후 _KPCR.UserRsp (0x10) 위치에 현재 사용자 모드의 rsp 값을 백업한 다음, 0x1A8 위치에 보관해 두었던 커널 스택의 주소를,

5: kd> dt _KPCR UserRsp
nt!_KPCR
   +0x010 UserRsp : Uint8B

3: kd> dt _KPCR Prcb..
nt!_KPCR
   +0x180 Prcb   : 
      +0x000 MxCsr  : Uint4B
      +0x004 LegacyNumber : UChar
      +0x005 ReservedMustBeZero : UChar
      +0x006 InterruptRequest : UChar
      +0x007 IdleHalt : UChar
      +0x008 CurrentThread : 
      +0x010 NextThread : 
      +0x018 IdleThread : 
      +0x020 NestingLevel : UChar
      +0x021 ClockOwner : UChar
      +0x022 PendingTickFlags : UChar
      +0x022 PendingTick : UChar
      +0x022 PendingBackupTick : UChar
      +0x023 IdleState : UChar
      +0x024 Number : Uint4B
      +0x028 RspBase : Uint8B // _KPCR의 [0x1a8] 위치에 해당
    ...[생략]...

rsp에 덮어씁니다. 그러니까, 적어도 저 단계까지는 진행을 해야 사용자 모드에서 커널 모드로의 전환이 완료된 것이고, BP를 걸어도 WinDbg가 정상적으로 대응을 하게 됩니다.

따라서 그 이후의 코드, 위의 경우 fffff802`3d62a015부터 BP를 걸 수 있다는 건데요, 그런데 BP 자체도 여기서는 문제가 됩니다. 사실 BP의 의미가 해당 위치에 int 3(0xcc) 패치를 하는 것이므로 그렇게 되면 WinDbg 자체도 소프트웨어로서의 동작에 영향을 받게 돼 버립니다.

이로 인해 BP는 걸 수 없고, 대신 CPU 하드웨어에서 제공하는 breakpoint인 BA를 걸어야 합니다. 가령 위의 nt!KiSystemCall64 출력 결과에서는 "push 2Bh" 위치 이후부터 ba를 걸면 됩니다.

3: kd> ba e 1 fffff802`51429e15

하지만, 저렇게까지 했어도 여전히 현실적으로 문제가 있습니다. 즉, KiSystemCall64 함수 호출은 윈도우 전반적으로 사용되는 공통 길목이기 때문에 (이번 예제에서처럼) Sleep에서 이어지는 호출로 인해 ba가 걸렸다는 것을 보증할 수 없습니다. 운이 좋다면 몇 번의 'g' 키 입력으로 SleepEx 함수와 연결된 상황의 디버깅을 할 수도 있겠지만, ^^; 쉽지 않을 것입니다.

바로 이런 문제를 해결하기 위해 동영상에서 소개하는 것이 바로 trampoline 패치입니다. 자세한 사항은 동영상을 참고하시고 이 글에서는 그 부분은 생략하고 넘어가겠습니다. (어려워서가 아니라, 지루한 작업입니다. ^^;)




KiSystemCall64 함수의 초기 작업이 끝나고 나면, 이제 남은 중요한 작업은 eax 레지스터에 담긴 서비스 함수 번호(SSN: System Service Number)에 해당하는 커널 측 함수를 실행하는 역할이 있습니다.

그 시작으로 SSDT(System Service Descriptor Table)가 나오는데,

System Service Descriptor Table - SSDT
; https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel

A Gentle Introduction to Syscalls in Windows
; https://captain-woof.medium.com/a-gentle-introduction-to-syscalls-in-windows-7800e05acc8a

// x64 Windows 기준
// https://github.com/Faran-17/Windows-Internals/blob/main/System%20Architecture%20and%20Components/System%20Service%20Descriptor%20Table.md

typedef struct tagSERVICE_DESCRIPTOR_TABLE {
    SYSTEM_SERVICE_TABLE nt; //effectively a pointer to Service Dispatch Table (SSDT) itself
    SYSTEM_SERVICE_TABLE win32k;
    SYSTEM_SERVICE_TABLE sst3; //pointer to a memory address that contains how many routines are defined in the table
    SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE;

그런데 위의 경우 4개의 필드(nt, win32k, sst3, sst4)를 모두 SYSTEM_SERVICE_TABLE 타입으로 명시하고 있지만 실제로는 C/C++ struct 구문으로 대충 이렇게 표현하는 것이 맞습니다.

// https://github.com/Iolop/SSDT-Hook/blob/master/SSDT-Hook(Driver).c

typedef struct _SYSTEM_SERVICE_TABLE
{
    PULONG nt;
    PULONG win32k;
    ULONG nEntries;
    PULONG argumentTable;
} SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;

위의 구조를 감안해 WinDbg에서 심벌 풀이를 해보면 이런 값이 나오고,

4: kd> dps nt!KeServiceDescriptorTable L4
fffff802`3e0018c0  fffff802`3d2cf090 nt!KiServiceTable
fffff802`3e0018c8  00000000`00000000
fffff802`3e0018d0  00000000`000001e6 // nt!KiServiceTable에 486(0x1e6)개의 서비스 함수가 있다는 것을 의미
fffff802`3e0018d8  fffff802`3d2cf82c nt!KiArgumentTable

출력 결과에도 나오지만, KiServiceTable은 그것 자체로 심벌이 등록돼 있으므로 직접 구하는 것도 가능합니다.

5: kd> ? KiServiceTable
Evaluate expression: -8786476732272 = fffff802`3d2cf090

바로 이 KiServiceTable에서 서비스 번호에 대응하는 함수의 주소를 구할 수 있는데요, 가령 Sleep의 ntdll!NtDelayExecution에서 eax에 설정한 값이 (Windows 11의 경우) 0x34이고, KiServiceTable 내의 개별 항목 크기가 4바이트이므로 다음과 같이 그 위치를 찾아갈 수 있습니다.

4: kd> dd /c1 KiServiceTable + 4*0x34 L1
fffff802`3d2cf160  05c76a00

// https://captain-woof.medium.com/a-gentle-introduction-to-syscalls-in-windows-7800e05acc8a
// SSNs are of 2 bytes each (WORD).
// 현재 2바이트로 할당돼 있어, 최대 65,536개의 서비스 함수를 가질 수 있습니다.

이때 KiServiceTable에 담긴 항목의 값(위의 경우 05c76a00)이 의미하는 것은, 해당 주소로부터 서비스 함수가 떨어진 위치 값인데 하위 4비트를 (부호 유지하며 우측 shift 연산으로) 잘라야 합니다. 따라서 다음과 같이 구하면,

// fffff802`3d2cf090 == nt!KiServiceTable 위치

4: kd> ? fffff802`3d2cf090 + (05c76a00 >>> 4)
Evaluate expression: -8786470672592 = fffff802`3d896730

// 엄밀히는 movsxd  r11,dword ptr [r10+rax*4] 연산이기 때문에,
// 4바이트 DWORD 값을 8바이트 레지스터에 부호 확장해 변환하는 작업이므로,
// 05c76a00 값이 아니라 00000000`05c76a00 값으로 계산해야 합니다.
/*
? fffff802`3d2cf090 + (00000000`05c76a00 >>> 4)
*/

fffff802`3d896730 위치가 nt!NtDelayExecution 함수의 시작 부분이고, 간단하게 역어셈블로 확인할 수 있습니다.

4: kd> u KiServiceTable + (05c76a00 >>> 4) L1
nt!NtDelayExecution:
fffff802`3d896730 4883ec28        sub     rsp,28h

// Nt... 함수는 주로 매개변수 확인 작업을 하며, 실제 처리는 이후 Zw... 함수에 전달해 수행

이참에 WinDbg의 수식 표현을 좀 공부하면 ^^ 위의 하드 코드 부분을 아래와 같이 수식으로 바꿀 수 있습니다.

// MASM Evaluator를 사용하는 경우 (dwo는 movsxd와 같은 부호 확장은 하지 않으므로 오프셋 값이 음수라면 다른 처리가 필요합니다.)
5: kd> u KiServiceTable + (dwo(KiServiceTable + 4*0x34) >>> 4) L1
nt!NtDelayExecution:
fffff802`3d896730 4883ec28        sub     rsp,28h

// 또는 C++ Evaluator를 곁들여!
5: kd> ? @@c++(*(long *)(@@(nt!KiServiceTable) + 4*0x34)) >>> 4
int 0n6059680

5: kd> u KiServiceTable + (@@c++(*(long *)(@@(nt!KiServiceTable) + 4*0x34)) >>> 4) L1
nt!NtDelayExecution:
fffff802`3d896730 4883ec28        sub     rsp,28h

참고로, 잘라낸 0~3비트는 해당 서비스 함수의 인자 개수를 나타낸다는 글이 있는데,

The Quest for the SSDTs
; https://www.codeproject.com/Articles/1191465/The-Quest-for-the-SSDTs

Bits 4-31 of 0x030b4fc7 correspond to the relative address to the base of the nt!KiServiceTable. Bits 0-3 are related to the number of arguments and will not be used here.


그렇다면 05c76a00의 경우 nt!NtDelayExecution의 인자는 원래 2개인데 0개라고 계산되고, 위의 글에서도 예를 든 NtCreateFile은 11개의 인자를 갖지만 030b4fc7라고 나오므로 7개의 인자를 갖는 것으로 계산되니 뭔가 맞지 않습니다.

한데, 아마도 처음 4개의 인자는 레지스터(rcx, rdx, r8, r9)를 통해 전달하므로 그것을 초과한 매개변수 개수로 인한 스택의 영향을 나타내는 것이 아닐까 싶습니다. 일례로, (Windows 11에서) 0x100번째 서비스 함수인 nt!NtGetNextProcess는 5개의 인자를 갖는데,

7: kd> dd /c1 KiServiceTable + 4*0x100 L1
fffff802`3d2cf490  0620b401

7: kd> u KiServiceTable + (06f8de01 >> 4) L1
nt!NtGetNextProcess:
fffff802`3d9c7e70 48895c2410      mov     qword ptr [rsp+10h],rbx

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

따라서, 4개를 초과한 1개의 인자가 스택으로 전달된다는 것을 06f8de01 값에 의해 알 수 있습니다. (그렇게 계산하면, Windows의 서비스 함수는 최대 4 + 0xf == 19개의 매개변수로 제한이 됩니다.)

참고로, KiServiceTable의 모든 항목(KeServiceDescriptorTable의 offset 0x10에 위치한 0x1e6, 즉 486개)을 심벌과 함께 출력하려면 이런 명령어를 사용하면 됩니다.

// Finding Address of All SSDT Routines
// https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel#finding-address-of-all-ssdt-routines

.foreach /ps 1 /pS 1 ( offset {dd /c 1 nt!KiServiceTable L poi(nt!KeServiceDescriptorTable+10)}){ r $t0 = ( offset >>> 4) + nt!KiServiceTable; .printf "%p - %y\n", $t0, $t0 }

서비스 함수 관련해서 재미있는 github 링크를 하나 발견했는데요,

Windows System Call Tables
; https://github.com/j00ru/windows-syscalls


이를 이용하면 운영체제별 서비스 함수에 어떤 번호가 할당돼 있는지 쉽게 검색할 수 있습니다.




이 정도면 대충 살펴본 것 같죠? ^^

부가적으로, (Win32 API가 아닌) device driver와 통신하는 경우에는 CreteFile, WriteFile, DeviceIoControl 등의 함수를 사용하게 되는데요, 이럴 때도 위의 원칙을 벗어나진 않습니다. 결국 ntdll!Nt...에서 syscall을 통해 nt!Nt...로 넘어가는 것은 동일하고, 단지 이후로는 Win32 API라면 마이크로소프트가 제공한 ntoskrnl.exe의 함수를 호출하고 말겠지만 device driver라면 그 중간 과정에서 I/O Manager가 관여한다는 점만 다를 뿐입니다.

그나저나, 보안(PatchGuard)의 이유로 KiSystemCall64 함수는 별도로 nt!KiSystemCall64Shadow 함수를 갖게 되는데요,

3: kd> u nt!KiSystemCall64Shadow LA
nt!KiSystemCall64Shadow:
fffff802`3dcf41c0 0f01f8          swapgs
fffff802`3dcf41c3 654889242510a00000 mov   qword ptr gs:[0A010h],rsp
fffff802`3dcf41cc 65488b242500a00000 mov   rsp,qword ptr gs:[0A000h]
fffff802`3dcf41d5 650fba242518a0000001 bt  dword ptr gs:[0A018h],1
fffff802`3dcf41df 7203            jb      nt!KiSystemCall64Shadow+0x24 (fffff802`3dcf41e4)
fffff802`3dcf41e1 0f22dc          mov     cr3,rsp
fffff802`3dcf41e4 65488b242508a00000 mov   rsp,qword ptr gs:[0A008h]
fffff802`3dcf41ed 6a2b            push    2Bh
fffff802`3dcf41ef 65ff342510a00000 push    qword ptr gs:[0A010h]
fffff802`3dcf41f7 4153            push    r11

"How To Step Into a SYSCALL With a Debugger (via Kernel Binary Patch) | Reverse Engineering Win10 x64" 동영상에서는 오히려 KiSystemCall64Shadow를 호출하는 것으로 나오지만 일단 제가 테스트한 Windows 11에서는 언제나 KiSystemCall64로 호출이 되었습니다. 이게 랜덤으로 바뀌는 것인지, 아니면 특정 버전에서만 그런 것인지는 모르겠습니다.

어쨌든 저런 경우에도 ba를 걸어 디버깅을 할 수 있고 규칙은 동일하게 커널 스택이 바뀐 이후부터, 위의 경우라면 fffff802`3dcf41ed 주소부터 ba를 걸 수 있습니다.




win32k 서비스 함수의 경우에는, 아마도 _SYSTEM_SERVICE_TABLE의 필드에 "win32k"가 있는 걸로 봐서 과거에 함께 기록된 것으로 보이는데,

typedef struct _SYSTEM_SERVICE_TABLE
{
    PULONG nt;
    PULONG win32k;
    ULONG nEntries;
    PULONG argumentTable;
} SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;

현재는 저 값이 0입니다. 이에 대해 검색해 보면 win32k 시스템 함수 테이블은 아예 프로세스 문맥 수준에서만 제공되는 것 같은데요,

// !process 0 0 notepad.exe
// .process /i ...[PROCESS 주소]...
// g; .reload

2: kd> dd /c1 win32k!W32pServiceTable L4
ffffe8d1`72e59000  ff8b8340
ffffe8d1`72e59004  ff8e1842
ffffe8d1`72e59008  ff8b75c0
ffffe8d1`72e5900c  ff901f40

// 만약 위의 명령어를 프로세스 문맥 수준에서 실행하지 않으면 다음과 같은 결과가 나옵니다.
/*
2: kd> dd /c1 win32k!W32pServiceTable L4
ffffe8d1`72e59000  ????????
ffffe8d1`72e59004  ????????
ffffe8d1`72e59008  ????????
ffffe8d1`72e5900c  ????????
*/

이로부터 서비스 함수를 구하는 방법은 nt!KiServiceTable과 원칙은 같습니다. 위의 테이블 출력이라면 0번째 서비스 함수가 ff8b8340으로 나오므로 4바이트 음수 값을 부호 확장해 8바이트로 ffffffff`ff8b8340 값이 됩니다. 마지막으로 저 값에서 4비트를 부호 유지하며 우측 시프트 하면 최종적으로 ffffffff`fff8b834 값이 나오고 이것을 Table 주소에 더하면,

2: kd> u win32k!W32pServiceTable + ffffffff`fff8b834 L1
win32k!NtUserGetThreadState:
ffffe8d1`72de4834 4883ec28        sub     rsp,28h

이렇게 win32k 서비스 함수를 찾을 수 있습니다. 그런데, 실제로는 NtUserGetThreadState 함수를 표에서 찾아보면 서비스 함수 번호가 0x1000으로 나옵니다. 전체적인 win32k 함수표 내용을 보면 모든 숫자가 0x1000보다 높은 것을 볼 수 있는데요, 그렇다면 아마도 base가 0x1000이라는 것을 짐작할 수 있습니다.

따라서, 대충 이렇게 정리할 수 있습니다.

// 실습 환경: Windows 10 (버전 10.0.19045.5247)
// 
// 함수표에서 NtGdiInvertRgn의 함수 번호는 Windows 10 환경에서 0x1064

// 0x1064 함수 번호에서 0x1000을 뺀 오프셋을 구하고,
2: kd> dd /c1 win32k!W32pServiceTable + 4*(0x1064 - 0x1000) L1
ffffe8d1`72e59190  ff934140

// 8바이트로 부호 확장
ff934140 ==> ffffffff`ff934140

// 4비트 signed 우측 시프트
ffffffff`ff934140 ==> ffffffff`fff93414

2: kd> u win32k!W32pServiceTable + ffffffff`fff93414 L1
win32k!NtGdiInvertRgn:
ffffe8d1`72dec414 4883ec28        sub     rsp,28h

좀 복잡해 보이는데, 약간만 손보면 저걸 한 줄의 WinDbg 수식으로 바꿀 수 있습니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 1/11/2025]

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)
13881정성태2/7/2025406닷넷: 2316. C# - Port I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13880정성태2/5/2025439오류 유형: 947. sshd - Failed to start OpenSSH server daemon.
13879정성태2/5/2025503오류 유형: 946. Ubuntu - N: Updating from such a repository can't be done securely, and is therefore disabled by default.
13878정성태2/3/2025700오류 유형: 945. Windows - 최대 절전 모드 시 DRIVER_POWER_STATE_FAILURE 발생 (pacer.sys)
13877정성태1/25/20251023닷넷: 2315. C# - PCI 장치 열거 (레지스트리, SetupAPI)파일 다운로드1
13876정성태1/25/20251169닷넷: 2314. C# - ProcessStartInfo 타입의 Arguments와 ArgumentList파일 다운로드1
13875정성태1/24/20251161스크립트: 69. 파이썬 - multiprocessing 패키지의 spawn 모드로 동작하는 uvicorn의 workers
13874정성태1/24/20251132스크립트: 68. 파이썬 - multiprocessing Pool의 기본 프로세스 시작 모드(spawn, fork)
13873정성태1/23/20251059디버깅 기술: 217. WinDbg - PCI 장치 열거
13872정성태1/23/20251045오류 유형: 944. WinDbg - 원격 커널 디버깅이 연결은 되지만 Break (Ctrl + Break) 키를 눌러도 멈추지 않는 현상
13871정성태1/22/20251159Windows: 278. Windows - 윈도우를 다른 모니터 화면으로 이동시키는 단축키 (Window + Shift + 화살표)
13870정성태1/18/20251236개발 환경 구성: 741. WinDbg - 네트워크 커널 디버깅이 가능한 NIC 카드 지원 확대
13869정성태1/18/20251256개발 환경 구성: 740. WinDbg - _NT_SYMBOL_PATH 환경 변수에 설정한 경로로 심벌 파일을 다운로드하지 않는 경우
13868정성태1/17/20251195Windows: 277. Hyper-V - Windows 11 VM의 Enhanced Session 모드로 로그인을 할 수 없는 문제
13867정성태1/17/20251302오류 유형: 943. Hyper-V에 Windows 11 설치 시 "This PC doesn't currently meet Windows 11 system requirements" 오류
13866정성태1/16/20251309개발 환경 구성: 739. Windows 10부터 바뀐 device driver 서명 방법
13865정성태1/15/20251394오류 유형: 942. C# - .NET Framework 4.5.2 이하의 버전에서 HttpWebRequest로 https 호출 시 "System.Net.WebException" 예외 발생
13864정성태1/15/20251345Linux: 114. eBPF를 위해 필요한 SELinux 보안 정책
13863정성태1/14/20251285Linux: 113. Linux - 프로세스를 위한 전용 SELinux 보안 문맥 지정
13862정성태1/13/20251229Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정
13861정성태1/11/20251346Windows: 276. 명령행에서 원격 서비스를 동기/비동기로 시작/중지
13860정성태1/10/20251269디버깅 기술: 216. WinDbg - 2가지 유형의 식 평가 방법(MASM, C++)
13859정성태1/9/20251336디버깅 기술: 215. Windbg - syscall 이후 실행되는 KiSystemCall64 함수 및 SSDT 디버깅
13858정성태1/8/20251410개발 환경 구성: 738. PowerShell - 원격 호출 시 "powershell.exe"가 아닌 "pwsh.exe" 환경으로 명령어를 실행하는 방법
13857정성태1/7/20251820C/C++: 187. Golang - 콘솔 응용 프로그램을 Linux 데몬 서비스를 지원하도록 변경파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...