성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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'>닷넷에서 ESP/RSP 레지스터 값을 구하는 방법</h1> <p> 사실 이에 대한 소스는 다음의 글에서 이미 모두 다뤘습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Visual C++ / x64 환경에서 inline-assembly를 매크로 어셈블리로 대체하는 방법 - 두 번째 이야기 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11691'>https://www.sysnet.pe.kr/2/0/11691</a> C++의 inline asm 사용을 .NET으로 포팅하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1267'>https://www.sysnet.pe.kr/2/0/1267</a> 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1762'>https://www.sysnet.pe.kr/2/0/1762</a> </pre> <br /> 따라서 남은 작업은 RSP 레지스터의 값을 읽어들이는 기계어 코드를 알아내고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // x64 mov rax, rsp 00007FF6FFF01A40 <span style='color: blue; font-weight: bold'>48 8B C4</span> mov rax,rsp ret 00007FF6FFF01A43 C3 ret // x86 mov eax, esp 00841780 <span style='color: blue; font-weight: bold'>8B C4</span> mov eax,esp ret 00841782 C3 ret </pre> <br /> 이것을 코드 페이지로 할당해 호출하기만 하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Runtime.InteropServices; namespace ConsoleApp1 { internal class Program { private readonly static byte[] x86GetSP = { 0x8b, 0xc4, // mov eax,esp 0xc3, // ret }; private readonly static byte[] x64GetSP = { 0x48, 0x8b, 0xc4, // mov rax, rsp 0xc3, // ret }; [Flags()] private enum AllocationType : uint { COMMIT = 0x1000, RESERVE = 0x2000, } [Flags()] public enum MemoryProtection : uint { EXECUTE_READWRITE = 0x40, } [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [DllImport("kernel32")] private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType); [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private delegate int GetSPRegister32(); [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] private delegate long GetSPRegister64(); static unsafe void Main(string[] args) { IntPtr _codePointer; GetSPRegister32 _x86Call; GetSPRegister64 _x64Call; byte[] codeBytes = x86GetSP; if (IntPtr.Size == 8) { codeBytes = x64GetSP; } _codePointer = VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), AllocationType.COMMIT | AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE ); Marshal.Copy(codeBytes, 0, _codePointer, codeBytes.Length); if (IntPtr.Size == 4) { _x86Call = (GetSPRegister32)Marshal.GetDelegateForFunctionPointer( _codePointer, typeof(GetSPRegister32)); Console.WriteLine(_x86Call()); } else { _x64Call = (GetSPRegister64)Marshal.GetDelegateForFunctionPointer( _codePointer, typeof(GetSPRegister64)); long result = _x64Call(); Console.WriteLine($"0x{result:x}"); } if (_codePointer != IntPtr.Zero) { VirtualFree(_codePointer, 0, 0x8000); _codePointer = IntPtr.Zero; } } } } </pre> <br /> 위의 코드를 Visual Studio 내에서 디버깅해 보면 실제 SP 레지스터의 값과 비교할 수 있습니다. 이 과정에서 다음의 차이를 알 수 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Visual Studio의 Registers 창에 출력된 RSP 값] RSP = 000000193918E470 [코드로 읽어낸 RSP 값] 0x193918e528 </pre> <br /> 대략 그 차이가 0xb8이 나옵니다. 이렇게 큰 차이가 나는 것은 <a target='tab' href='https://www.sysnet.pe.kr/2/0/1762'>닷넷에서 EIP/RIP 레지스터 값을 구하는 방법</a> 글에서도 언급했지만 delegate의 내부 구현 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='use_func_ptr'></a> <br /> 저런 오차를 C# 9.0에 나온 함수 포인터를 이용하면 극복할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 9.0 - (6) 함수 포인터(Function pointers) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12374'>https://www.sysnet.pe.kr/2/0/12374</a> </pre> <br /> 즉, delegate를 통해 호출하지 않고 직접 함수 포인터로 호출하는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > delegate*<long> getSP = (delegate*<long>)_codePointer; long addressByFunc = getSP(); Console.WriteLine($"0x{addressByFunc:x}"); </pre> <br /> 이렇게 바꾸고 다시 비주얼 스튜디오로 확인해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Visual Studio의 Registers 창에 출력된 RSP 값] RSP = 000000193918E470 [코드로 읽어낸 RSP 값] 0x193918e468 </pre> <br /> (System.Delegate의 내부 구현이 사라져) 순수하게 native 메서드 호출로 바뀌었으므로 (반환 주솟값이 반영된) 0x08 (32비트의 경우 0x04) 값만 차이가 나는 것입니다.<br /> <br /> 따라서 현재 caller 입장에서의 RSP 값을 단순히 8(또는 4)을 보정하는 것으로 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private readonly static byte[] x86GetSP = { 0x8b, 0xc4, // mov eax,esp 0x83, 0xe8, 0x04, // add eax, 4 0xc3, // ret }; private readonly static byte[] x64GetSP = { 0x48, 0x8b, 0xc4, // mov rax, rsp 0x48, 0x83, 0xe8, 0x08, // add rax, 8 0xc3, // ret }; </pre> <br /> 테스트해보면, 정확히 일치한 값을 얻게 됩니다. ^^<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='get_rsp_1.png' src='/SysWebRes/bbs/get_rsp_1.png' /><br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1984&boardid=331301885'>첨부 파일은 이 글의 소스 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Runtime.InteropServices; namespace ConsoleApp1 { internal class Program { private readonly static byte[] x86GetSP = { 0x8b, 0xc4, // mov eax,esp 0x83, 0xc0, 0x04, // add eax, 4 0xc3, // ret }; private readonly static byte[] x64GetSP = { 0x48, 0x8b, 0xc4, // mov rax, rsp 0x48, 0x83, 0xc0, 0x08, // add rax, 8 0xc3, // ret }; [Flags()] private enum AllocationType : uint { COMMIT = 0x1000, RESERVE = 0x2000, } [Flags()] public enum MemoryProtection : uint { EXECUTE_READWRITE = 0x40, } [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [DllImport("kernel32")] private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType); static unsafe void Main(string[] args) { IntPtr _codePointer; byte[] codeBytes = x86GetSP; if (IntPtr.Size == 8) { codeBytes = x64GetSP; } _codePointer = VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), AllocationType.COMMIT | AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE ); Marshal.Copy(codeBytes, 0, _codePointer, codeBytes.Length); if (IntPtr.Size == 4) { delegate*<int> getSP = (delegate*<int>)_codePointer; int addressByFunc = getSP(); Console.WriteLine($"0x{addressByFunc:x}"); } else { delegate*<long> getSP = (delegate*<long>)_codePointer; long addressByFunc = getSP(); Console.WriteLine($"0x{addressByFunc:x16}"); } if (_codePointer != IntPtr.Zero) { VirtualFree(_codePointer, 0, 0x8000); _codePointer = IntPtr.Zero; } } } } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5510
(왼쪽의 숫자를 입력해야 합니다.)