64 비트 프로세스 환경 블록에 액세스하기 위해 32 비트 프로세스에서 64 비트 코드를 처리 할 필요가 없습니다. 32 비트 코드를 사용하여 주소를 얻을 수 있으며 32 비트 주소 공간 내에 위치합니다. 32 비트 주소 공간 외부에 할당 된 메모리에 액세스해야하는 경우에만 64 비트 코드를 사용해야하며 Windows는 32 비트 프로세스에서이를 수행하지 않을 것이라고 생각합니다.
실제로 32 비트 실행 파일에 64 비트 기능이 필요할 경우 _asm _emit
을 사용하는 것보다 더 나은 방법이 있습니다. 가장 먼저 할 일은 전체 64 비트 함수를 일반 어셈블리로 작성하고 일반 외부 어셈블러로 어셈블하는 것입니다.예를 들어, 여기 MASM 구문 64 비트 포인터로부터 판독하는 함수이다 :
는
_TEXT SEGMENT
__read64ptr:
mov rax, [rsp + 8]
mov eax, [rax]
mov edx, [rax + 4]
retf
_TEXT ENDS
END
이 간단한 함수 스택 인수로 64 비트 포인터 걸린다. 주소가 지정된 64 비트 값이 EAX와 EDX에 놓입니다. 이 함수는 32 비트 원거리 호출 명령으로 호출됩니다.
반환 값은 반환 주소의 32 비트 오프셋과 셀렉터에 대한 두 개의 32 비트 스택 슬롯을 사용한다는 점에 유의하십시오. RETF 명령어가 64 비트 모드에서 실행 되더라도 기본적으로 (RET 명령어 근처의 64 비트와 달리) 32 비트 스택 크기를 사용하며, RETF 명령어에 저장된 32 비트 원거리 복귀 주소와 올바르게 작동합니다 스택.
불행히도 우리는 Visual Studio에서 제공하는 도구로이 어셈블리 파일을 직접 사용할 수 없습니다. 64 비트 버전의 MASM은 64 비트 개체 파일 만 만들고 링커는 32 비트 및 64 비트 개체 파일을 혼합 할 수 없습니다. NASM을 사용하여 64 비트 코드를 32 비트 객체로 어셈블하고 Microsoft의 링커와 링크 할 수 있어야하지만 Microsoft 도구 만 사용하여 코드를 간접적으로 사용할 수도 있습니다.
는, 그렇게 할 파일을 조립하고
.text
섹션에 사는 C 배열에 수동 기계 코드를 복사하려면 :
:
#pragma code_seg(push, ".text")
#pragma code_seg(pop)
char const __declspec(allocate(".text")) _read64ptr[] = {
0x48, 0x8b, 0x44, 0x24, 0x08, /* mov rax, [rsp + 8] */
0x8b, 0x00, /* mov eax. [rax] */
0x8b, 0x50, 0x04, /* mov edx, [rax + 4] */
0xcb /* retf */
};
방금과 같은 코드를 사용할 필요를 호출하려면
struct {
void const *offset;
unsigned short selector;
} const _read64ptr_ind = { _read64ptr, 0x33 };
unsigned long long
read64ptr(unsigned long long address) {
unsigned long long value;
_asm {
push DWORD PTR [address + 4]
push DWORD PTR [address]
call FWORD PTR [_read64ptr_ind]
add esp, 8
mov DWORD PTR [value], eax
mov DWORD PTR [value + 4], edx
}
return value;
}
Microsoft 인라인 어셈블리에 call 33h:_read64ptr
을 쓸 방법이 없기 때문에 간접 참조는 _read64ptr_ind
이어야합니다. 또한이 예제에서는 64 비트 코드 선택기 0x33
이 하드 코딩되어 있으므로 변경되지 않을 것입니다.
unsigned long long
readgsqword(unsigned long off) {
unsigned long long value;
_asm {
mov edx, [off]
mov eax, gs:[edx]
mov edx, gs:[edx + 4]
mov DWORD PTR [value], eax
mov DWORD PTR [value + 4], edx
}
return value;
}
int
main() {
printf("32-bit TEB address %08lx\n",
__readfsdword(offsetof(NT_TIB, Self)));
printf("32-bit PEB address %08lx\n", __readfsdword(0x30));
unsigned long long teb64 = readgsqword(offsetof(NT_TIB64, Self));
printf("64-bit TEB address %016llx\n", teb64);
printf("64-bit PEB address %016llx\n", readgsqword(0x60));
printf("64-bit PEB address %016llx\n", read64ptr(teb64 + 0x60));
}
: 여기
은 (모두 32 비트 어드레스 공간에있는 경우에도) 64 비트 TEB에서 64 비트 PEB의 어드레스를 판독하기 위해 상기 코드를 사용하는 예이다 내 컴퓨터에서 실행하면 다음과 같은 출력을 생성합니다
32-bit TEB address 7efdd000
32-bit PEB address 7efde000
64-bit TEB address 000000007efdb000
64-bit PEB address 000000007efdf000
64-bit PEB address 000000007efdf000
모든 구조가 32 비트 포인터 및 64 비트 코드없이 사용하여 액세스 할 수 있습니다 볼 수 있듯이. 특히이 예에서는 32 비트 코드 만 사용하여 64 비트 PEB에 대한 32 비트 포인터를 얻는 방법을 보여줍니다.
Windows가 32 비트 프로세스에서 올바르게 실행되는 64 비트 코드를 올바르게 처리한다는 보장은 없습니다. 64 비트 코드를 실행하는 동안 언제든지 인터럽트가 발생하면 프로세스가 중단 될 수 있습니다.
아니요, 컴파일러의 asm 출력으로 보내는 모든 바이트는 컴파일 타임 상수 여야합니다. 32 비트 모드에서'_asm' 만 지원하는 MSVC를 해결하기위한 시도로 유용한 x86-64 코드를 만드는 데 별 도움이되지 않을지 의심 스럽습니다. 아마도 asm에 함수를 작성하고 C++에서 함수를 호출해야합니다. 삽입하는 명령어의 동작을 컴파일러에 알릴 방법이 없기 때문입니다. –
또한 주소는 링크 타임 상수이며 실제로 컴파일 타임이 아니므로 매크로를 사용하여 주소를 잘라내는 것은 작동하지 않습니다. 컴파일러와 어셈블러는 각 바이트를 별도로 알 수 없으므로 공간을 남겨 둘 필요가 있습니다 오브젝트 파일의 심볼 테이블에 재배치를 넣는다. 이 값은 링커에서 링크 할 때 채워집니다. –
asm 명령을 데이터로 사용하고 직접 인코딩하는 경우 구조체와 memcpy를 사용하지 않는 이유는 무엇입니까? –