2011-11-02 4 views
8

현재 다른 위치로 이동 한 코드가 변수와 클래스 포인터를 읽는 32 비트에서 코드 대체 스키마를 사용하고 있습니다. x86_64는 절대 주소 지정을 지원하지 않기 때문에 코드의 새 위치에서 변수에 올바른 주소를 가져 오는 데 문제가 있습니다. 문제의 세부 사항은 rip relative addressing 때문에 명령어 포인터 주소가 컴파일시와 다르다는 것입니다.x86_64의 런타임 코드 대체를위한 절대 주소 지정

x86_64 또는 다른 방법으로 절대 주소 지정을 사용하여 명령 포인터가 아닌 변수의 주소를 가져 오는 방법이 있습니까?

다음과 같음 : leaq variable(%%rax), %%rbx도 도움이됩니다. 나는 단지 명령어 포인터에 의존하지 않기를 원한다.

답변

6

코드 모델을 x86_64에 사용해보십시오. gcc에서는 -mcmodel = large으로 선택할 수 있습니다. 컴파일러는 코드와 데이터 모두에 대해 64 비트 절대 주소 지정을 사용합니다.

위치 독립적 코드가 생성되지 않도록 -fno-pic을 추가 할 수도 있습니다.

편집 : I는 -mcmodel = 큰으로 소정의 테스트 애플리케이션을 구축하고, 생성 된 바이너리 (어드레스이 경우에) 바로 절대적인 64 비트의 하중이다

400b81:  48 b9 f0 30 60 00 00 movabs $0x6030f0,%rcx 
400b88:  00 00 00 
400b8b:  49 b9 d0 09 40 00 00 movabs $0x4009d0,%r9 
400b92:  00 00 00 
400b95:  48 8b 39    mov (%rcx),%rdi 
400b98:  41 ff d1    callq *%r9 

유사 서열을 포함 간접적 인 호출이나 간접적 인 부하가 뒤 따른다. 명령 시퀀스

moveabs variable, %rbx 
addq %rax, %rbx 

플래그와 같은 부작용이 답변 등

+0

-mcmodel = large는 솔루션이어야합니다. 나는 왜 gcc osx 컴파일러가 그것을 지원하지 않는지 조사해야만한다. – nux

+0

아마 오래되었다. 작은 (표준) 및 중간 코드 모델이 일찍 추가되었으며 나중에 대형 모델이 추가되었습니다. – hirschhornsalz

+0

정말 고맙게 여기며 정말 고맙게 생각하고 문제를 해결하는 absolut를 해결합니다. – nux

2

당신이 요구하는 것은 할 수 있지만 그리 쉬운 것은 아닙니다.

한 가지 방법은 지침에서 코드 이동을 보완하는 것입니다. RIP 상대 주소 지정 (05h, 0dh, 15h, 1dh, 25h, 2dh, 35h 또는 3dh의 ModRM 바이트)을 사용하는 모든 지침을 찾아야하며 이동량에 따라 disp32 필드를 조정해야합니다 따라서 가상 주소 공간에서 +/- 2GB로 제한되며, 이는 64 비트 주소 공간이 4GB보다 크므로 보장 할 수 없습니다.

또한 대부분 예를 들어, 하나 이상의 함께 모든 원래의 명령을 대체 그 등가물로 그 명령을 대체 할 수

; These replace the original instruction and occupy exactly as many bytes as the original instruction: 
    JMP Equivalent1 
    NOP 
    NOP 
Equivalent1End: 

; This is the code equivalent to the original instruction: 
Equivalent1: 
    Equivalent subinstruction 1 
    Equivalent subinstruction 2 
    ... 
    JMP Equivalent1End 

두 가지 방법이 적어도 일부 기초적인 86 분해 루틴이 필요합니다.

전자의 경우 원본 코드의 패치 된 복사본이 포함 된 메모리가 원본 코드의 +/- 2GB 내에 있도록하려면 Windows에서 VirtualAlloc() (또는 일부 해당 기능의 Linux)을 사용해야 할 수 있습니다. 그리고 특정 주소의 할당은 여전히 ​​실패 할 수 있습니다.

후자는 단순한 기본 디스어셈 외에 전체 명령 디코딩 및 생성을 요구합니다.

해결해야 할 다른 단점이있을 수 있습니다.

RFLAGS 레지스터에 TF 플래그를 설정하여 모든 명령어의 실행이 끝나면 CPU가 single-step 디버그 인터럽트를 생성하도록하여 명령어 경계를 찾을 수도 있습니다. 디버그 예외 핸들러는이를 catch하고 다음 명령어의 RIP 값을 기록해야합니다. Windows에서 Structured Exception Handling (SEH)을 사용하여이 작업을 수행 할 수 있다고 생각합니다 (디버그 인터럽트를 시도한 적이 없음). Linux에 대해서는 확실하지 않습니다. 이를 위해서는 모든 명령어를 실행해야합니다.

Btw는 64 비트 모드에서 절대 주소 지정을합니다. 예를 들어 0A0h부터 0A3h까지의 opcode를 사용하는 MOV to/accumulator 명령을 참조하십시오.

+0

확실히 변화와의 등가 "leaq offset64bit (%의 RAX) %의 RBX"(존재하지 않음)이다 . 늘어나는만큼 당신을 이해, 접근 방식은 런타임에 movs에 대한 주소 값을 다시 계산하는 것입니다. 이 계획에 무거운 것 같습니다. 그래서 만약 내가 다른 구현에 대해 생각할 수있는 유일한 방법입니다. – nux