성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>윈도우 - 스레드 스택의 "red zone"</h1> <p> "Raymond Chen"의 글을 제 마음대로 정리한 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Why do we even need to define a red zone? Can’t I just use my stack for anything? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20190111-00/?p=100685'>https://devblogs.microsoft.com/oldnewthing/20190111-00/?p=100685</a> </pre> <br /> "red zone"이 뭔지 궁금해서 찾아본 건데, 정작 x86/x64에서는 실습을 할 수 없어 다소 제목 값을 못하게 되었습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 보통 스택은 이렇게 구성됩니다.<br /> <br /> <img alt='stack_red_zone_1.png' src='/SysWebRes/bbs/stack_red_zone_1.png' /><br /> <br /> 그런데 일부 플랫폼에서는 SP가 가리키는 (스택은 상위 주소에서 하위 주소로 자라므로) 아래의 영역에 대해 "red zone"이라는 구역을 정의하기도 합니다. <br /> <br /> <img alt='stack_red_zone_2.png' src='/SysWebRes/bbs/stack_red_zone_2.png' /><br /> <br /> 그런 경우에는 "red zone"도 (비록 SP 포인터 범위에 포함되지는 않지만) 유효한 영역, 다르게 표현하면 응용 프로그램의 동작을 위해 필요한 공간으로 취급합니다. 윈도우 운영체제의 경우 CPU 종류 별로 다음과 같이 red zone 크기를 정한다고 하는데,<br /> <br /> <img alt='stack_red_zone_3.png' src='/SysWebRes/bbs/stack_red_zone_3.png' /><br /> <br /> 따라서 우리가 일반적으로 사용하는 INTEL/AMD CPU인 x86, x64 환경에서는 red zone 영역이 없습니다.<br /> <br /> 그런데, 개인적으로 한 번도 궁금해 본 적이 없는 질문을 "Raymond Chen"이 합니다. 우리는 분명히 SP 포인터가 스택의 top을 가리키고 그 영역 내의 데이터만 건드리지 않는다면 프로그램 수행에는 아무런 지장이 없을 것임을 알 수 있습니다. 그렇다면 그 top을 넘어서는 하위 주소의 범위를 임의 목적으로 사용하는 것도 가능하지 않을까요?<br /> <br /> 하지만 그에 대한 반례로, 아래의 코드를 예시로 듭니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // x86 기준의 코드 MOV [esp-4], eax ; save eax below the stack pointer MOV ecx, [esp-4] ; read it into ecx CMP ecx, eax ; are they the same? JNZ panic ; N: something crazy happened </pre> <br /> (SP 범위를 넘어서는) [esp-4] 주소에 eax의 값을 보관하고, 다시 그 값을 ecx에 보관 후 ecx와 eax의 값을 비교하면... 당연히 값은 같을 것입니다. 그런데, 경우에 따라 panic 라벨로 점프하는 코드가 실행될 수 있다는 것입니다.<br /> <br /> 위의 코드는 x86이므로 일단 red zone으로 여겨지는 안전 영역이 0바이트로 아예 존재하지 않습니다. 이것은, 시스템이 정확히 SP 포인터가 가리키는 위치 내의 데이터만 안 건드린다면 안정성이 보장된다는 "가정"을 하게 됩니다.<br /> <br /> 따라서, 위의 어셈블리 코드에서 "[esp-4]" 영역은 다른 특별한 상황에서 (응용 프로그램 개발자가 아닌) 시스템 코드에 의해 임의로 사용될 수도 있다는 것입니다.<br /> <br /> 일례로, windbg의 .call 명령어 사용은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .call (Call Function) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/-call--call-function-'>https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/-call--call-function-</a> </pre> <br /> 현재의 스레드 스택을 사용해야 할 것이고, SP 영역 밖의 내용이 안전하다는 가정으로 필요에 따라 esp-4 영역을 사용합니다.<br /> <br /> 이 외에도, 절묘하게 "MOV [esp-4], eax" 코드가 수행된 후 스레드가 선점된 상태에서 메모리 관리자가 해당 코드를 담은 메모리 페이지를 working set에서 내렸다고 가정해 보겠습니다. 이후 스레드가 실행을 재개하면 메모리 관리자는 디스크로 내린 코드 페이지를 working set에 다시 올리려 할 것입니다. 그런데, 하필 그 과정에서 I/O 에러가 발생하는 상황도 있을 것입니다. 그럼 운영체제는 예외 처리 절차에 따라 exception frame을 현재 스택에 구성하게 되는데요, 이때도 역시 응용 프로그램의 유효한 스택 사용을 SP 포인터를 기준으로 하므로 그 이후의 영역, 즉 [esp-4] 위치의 메모리가 사용될 것입니다.<br /> <br /> 물론, 위와 같은 우연이 겹칠 확률은 거의 없을 것입니다. 하지만 분명한 건, SP 포인터 이후의 내용이 100% 안전하지는 않다는 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 위의 시나리오에서 실제로 windbg의 .call 명령어를 실습해 볼까요? ^^<br /> <br /> 간단하게 C++로 다음의 코드를 작성하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #include <iostream> #include <stdio.h> <span style='color: blue; font-weight: bold'>int Add(int i, int j) { return i + j; }</span> int main() { std::cout << "Press any key to exit...!\n"; getchar(); } </pre> <br /> 실행 후 windbg로 연결(Attach)합니다. 이후, Add 함수와 스택 포인터 위치의 데이터를 확인하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Stupid debugger tricks: Calling functions and methods // ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20070427-00/?p=27083'>https://devblogs.microsoft.com/oldnewthing/20070427-00/?p=27083</a> 0:004> <span style='color: blue; font-weight: bold'>x ConsoleApplication1!Add</span> 00007ff7`6ffb2120 ConsoleApplication1!Add (int, int) 0:004> <span style='color: blue; font-weight: bold'>dd rsp-8</span> 00000065`b39ff740 <span style='color: blue; font-weight: bold'>00000000 00000000</span> 761c84ee 00007fff 00000065`b39ff750 00000000 00000000 00000000 00000000 00000065`b39ff760 00000000 00000000 00000000 00000000 00000065`b39ff770 00000000 00000000 7400244d 00007fff 00000065`b39ff780 00000000 00000000 00000000 00000000 00000065`b39ff790 00000000 00000000 00000000 00000000 00000065`b39ff7a0 00000000 00000000 7614dfb8 00007fff 00000065`b39ff7b0 00000000 00000000 00000000 00000000 </pre> <br /> .call을 실행합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>.call ConsoleApplication1!Add(1,2)</span> Thread is set up for call, 'g' will execute. WARNING: This can have serious side-effects, including deadlocks and corruption of the debuggee. 0:004> <span style='color: blue; font-weight: bold'>g</span> .call returns: int 0n3 ntdll!DbgBreakPoint: 00007fff`76192af0 cc int 3 </pre> <br /> 이제 다음과 같이 이전 rsp-8 위치의 데이터를 확인하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>dd 00000065`b39ff740</span> 00000065`b39ff740 <span style='color: blue; font-weight: bold'>ccfdebcc cccccccc</span> 761c84ee 00007fff 00000065`b39ff750 00000000 00000000 00000000 00000000 00000065`b39ff760 00000000 00000000 00000000 00000000 00000065`b39ff770 00000000 00000000 7400244d 00007fff 00000065`b39ff780 00000000 00000000 00000000 00000000 00000065`b39ff790 00000000 00000000 00000000 00000000 00000065`b39ff7a0 00000000 00000000 7614dfb8 00007fff 00000065`b39ff7b0 00000000 00000000 00000000 00000000 </pre> <br /> 보다시피 windbg의 .call 호출 수행으로 스택이 변조되었습니다. 아울러, 함수에 전달했던 값은 (64비트 ABI 규약으로) rcx, rdx에 전달된 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:004> <span style='color: blue; font-weight: bold'>r</span> rax=00000065b347a000 rbx=0000000000000000 rcx=<span style='color: blue; font-weight: bold'>0000000000000001</span> rdx=<span style='color: blue; font-weight: bold'>0000000000000002</span> rsi=0000000000000000 rdi=0000000000000000 rip=00007ff76ffb2120 rsp=00000065b39ff718 rbp=0000000000000000 r8=0000000000000000 r9=0000000000000000 r10=00007fff7408cff0 r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl zr na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ConsoleApplication1!Add: 00007ff7`6ffb2120 89542410 mov dword ptr [rsp+10h],edx ss:00000065`b39ff728=00000000 </pre> <br /> 참고로, ".call" 명령은 windbg가 제공하는 것이지만, <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20151016-00/?p=91341'>그게 없어도 개발자가 직접 stack frame을 구성해 호출</a>하는 것이 가능합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그건 그렇고 ARM 개발 환경 도구가 국내에도 정발되었다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows Dev Kit 2023 ; <a target='tab' href='https://www.microsoft.com/en-us/d/windows-dev-kit-2023/94k0p67w7581'>https://www.microsoft.com/en-us/d/windows-dev-kit-2023/94k0p67w7581</a> </pre> <br /> ARM64에서 16바이트 공간의 red zone 여부를 테스트할 수 있었을 텐데... 아쉽군요. ^^ 아마도 위와 같이 windbg로 .call 명령어를 수행하면 rsp-16까지의 데이터 변조는 발생하지 않을 것입니다. (혹시 확인해 주실 분 있을까요? ^^)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 정리해 보면, Raymond Chen의 글도 제목이 좀 이상합니다. 제목과는 달리 왜 "red zone" 영역이 필요한지에 대해서는 언급이 없고, 그냥 플랫폼 별로 존재한다고만 언급한 것이 전부입니다. 이에 대해 좀 더 찾아보면 아래의 문서에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://www.agner.org/optimize/calling_conventions.pdf'>https://www.agner.org/optimize/calling_conventions.pdf</a> 64 bit Linux, BSD and Mac. This system has six integer registers and eight XMM registers for parameter transfer. This means that a maximum of 14 parameters can be transferred in registers in 64 bit Linux, BSD and Mac, while 64 bit Windows allows only 4. There is no shadow space on the stack. Instead there is a "red zone" below the stack pointer that can be used for temporary storage. The red zone is the space from [rsp-128] to [rsp-8]. A function can rely on this space being untouched by interrupt and exception handlers (except in kernel code). <span style='color: blue; font-weight: bold'>It is therefore safe to use this space for temporary storage as long as you don't do any push or call instructions.</span> Everything stored in the red zone is destroyed by function calls. The red zone is not available in Windows. </pre> <br /> 응용 프로그램 개발자가 명시적인 push/call 명령어 없이 임시 용도로 활용할 수 있는 SP 이후의 공간이라고 합니다. 왜 저런 공간이 필요한 것인지... 그래도 모르겠군요. ^^; 저런 의미에서 볼 때 Raymond도 "... a red zone, which is a region of the stack below the stack pointer that is still valid for applications to use."라는 설명으로 퉁친 것 같습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1806
(왼쪽의 숫자를 입력해야 합니다.)