2016-10-22 11 views
1

Visual Studio 2015에서 _asm _emit과 함께 부울 산술 연산을 수행하고 x86 프로그램에 일부 x64 opcode를 삽입하려면 메모리 명령에 mov을 많이 사용해야하며, 그래서 주소를 가지고 매크로의 어떤 종류를 만들기 위해 노력하고 너무 작은 엔디안에서 방출 :C 전 처리기 및 "_asm _emit"지시문

#define EmitDword(x)\ 
{\ 
    _asm _emit (x & 0x000000FF) \ 
    _asm _emit ((x >> 8) & 0x000000FF) \ 
    _asm _emit ((x >> 16) & 0x000000FF) \ 
    _asm _emit ((x >> 24) & 0x000000FF) \ 
} 

그러나 나는 생각이 inline assembler syntax error in 'first operand'; 이 매크로에 변수의 주소를 전달하는 오류를 얻을 , 그래서 직접 기계어 코드로 방출 될 것이고, 다음과 같이 할 수 있습니다 :

#define EmitDword(x)\ 
{\ 
     _asm _emit (x & 0x000000FF) \ 
     _asm _emit ((x >> 8) & 0x000000FF) \ 
     _asm _emit ((x >> 16) & 0x000000FF) \ 
     _asm _emit ((x >> 24) & 0x000000FF) \ 
} 

/* mov qword ptr [addr],reg */ 
#define X64_MovToMem(addr,reg)\ 
{\ 
    _asm _emit 0x48\ 
    _asm _emit 0x89\ 
    _asm _emit reg\ 
    _asm _emit 0x25\ 
    EmitDword(addr)\ 
} 

#define _rax 4 
void test() 
{ 
    DWORD64 someData; 
    X64_MovToMem(&someData,_rax); 
} 

전처리 인라인 어셈블리를 사용하여 비정상 값을 방출하는 방법이 있습니까?

+1

아니요, 컴파일러의 asm 출력으로 보내는 모든 바이트는 컴파일 타임 상수 여야합니다. 32 비트 모드에서'_asm' 만 지원하는 MSVC를 해결하기위한 시도로 유용한 x86-64 코드를 만드는 데 별 도움이되지 않을지 의심 스럽습니다. 아마도 asm에 함수를 작성하고 C++에서 함수를 호출해야합니다. 삽입하는 명령어의 동작을 컴파일러에 알릴 방법이 없기 때문입니다. –

+0

또한 주소는 링크 타임 상수이며 실제로 컴파일 타임이 아니므로 매크로를 사용하여 주소를 잘라내는 것은 작동하지 않습니다. 컴파일러와 어셈블러는 각 바이트를 별도로 알 수 없으므로 공간을 남겨 둘 필요가 있습니다 오브젝트 파일의 심볼 테이블에 재배치를 넣는다. 이 값은 링커에서 링크 할 때 채워집니다. –

+0

asm 명령을 데이터로 사용하고 직접 인코딩하는 경우 구조체와 memcpy를 사용하지 않는 이유는 무엇입니까? –

답변

3

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 비트 코드를 실행하는 동안 언제든지 인터럽트가 발생하면 프로세스가 중단 될 수 있습니다.

+0

안녕하세요, 답변 주셔서 감사합니다. 나는이 대답을 받아 들일 것이다. 왜냐하면 최고의 것이기 때문에, x64 매크로 코드를 작성하고 어셈블하고 직접 복사하는 것이 가장 좋은 방법이다. 것은 x64 메모리 위치에 액세스해야한다는 것입니다. 나는 여물통 PEB_LDR_DATA를 반복하고, Windows 10의 주소는 x64 비트입니다. 또한로드 된 모듈 이름 (UNICODE_STRING64)의 wchar 버퍼가있는 일부 항목에는 x64 주소도 있습니다. 질문이 하나 더 있습니다. 발생하면 wow64 프로세스 실행을 중단시킬 수있는 시스템 인터럽트에 대해 설명 할 수 있습니까? – Vlad

+0

@Vlad : 커널은 32 비트 프로세스라고 생각하기 때문에 사용자 공간이 64 비트 모드 인 동안 인터럽트가 발생하더라도 32 비트 compat 모드에서 사용자 공간으로 돌아갈 수 있습니다. –

+0

MSVC inline-asm에서'push whatever' /'db 9Ah' /'dd _read64ptr' /'dw 33h'를 사용하여 직접 원거리 통화에 적합한 인코딩을 내보낼 수 있습니다. 컴파일 타임에 링크 타임 값을 잘라내려는 OP의 시도와 달리 링커는 4 바이트 주소를 채워야합니다. –

1

미안하지만 당신이하려는 것은 의미가 없습니다.

  • "x86 프로그램에 일부 x64 opcode 삽입"- x86 프로그램 인 경우 x64 opcode를 실행하지 않습니다.
  • "_asm _emit (x & 0x000000FF)"- 방출을 위해 docs을 읽었습니까? C 코드가 아닌 바이트 만 내보낼 수 있습니다.
  • "emit emit emit을 내 보냅니다."- 왜 1 바이트 씩 4 번 쓰기 (이 코드는 어쨌든하지 않습니다)가 4 바이트 쓰기 1 번보다 빠르다고 생각합니까?
  • "불변 값 방출을 달성 할 수있는 방법이 있습니까?"- emit을 사용하는 경우 작성하려고하는 값이 현재 저장되어있는 레지스터를 알아야합니다 (거의 불가능한 것 코드가 변경 될 때마다 컴파일러가 다른 레지스터를 사용할 수 있고 레지스터에 값이 없을 수도 있기 때문에 달성 할 수 있습니다.

나는 원래의 문제를 해결하기 위해이 글을 쓰는 방법을 과거로 보려고합니다. 그러나 :

나는 "asm이 빠릅니다."라고 생각하는 경향이 있다는 것을 알고 있습니다. 그러나 C 코드는 어셈블러로 변환됩니다. 컴파일러는 이미 emm을 통해 cobble 할 수있는 것보다 훨씬 더 효율적인 asm 코드를 생성하고 있습니다.

그리고 64 비트 명령어를 사용하면 더 나은 코드를 생성 할 수 있습니다 (확실히 가능). 64 비트 실행 파일을 만들어야합니다.

다음 C 컴파일러보다 효율적인 asm 코드를 만들 수 있다고 확신하는 경우 전체 asm 루틴을 만든 다음 C 코드에서 호출하십시오. x64 어셈블러를 x86 프로그램에 연결할 수 없습니다.

+0

그는 리틀 엔디안 순서로 4 바이트의 주소를 내보내려고하고 실제로'x & 0x000000FF'를 계산하는 명령을 내 보내지 않으려한다고 생각합니다. 나는 그것이 의미가있는 것처럼 보이지 않는다는 것에 동의한다. 나는 "x64 opcode를 x86 프로그램으로 내 보낸다"는 말을 듣고 그는 기계 코드를 실행하기를 완전히 확신하지 못했습니다. 그래서 struct 나 뭔가를 사용하는 것이 좋습니다. 나는 대답을 올리는 것을 생각했지만, 그 질문은 대답할만한 충분한 의미가 있다고 생각하지는 않았다. –

+0

"나는 메모리 명령을 많이 사용해야한다"는 말은 성능 향상을 목적으로하는 것처럼 들린다. "asm은 C보다 빠르다"는 ASm의 작동 방식에 대한 제한된 이해와 결합하여 이와 같은 질문을 제기 할 수 있다는 믿음. x64를 사용하는 것은 일을 더 빠르게 만드는 또 다른 시도입니다. –

+0

오, 예, 대답은 "물론 가능하지 않을 수도 있습니다"라는 질문 이외의 질문을 해석하는 방법을 찾으려고했습니다. 그러나 나는 그 질문에 그 문장을 근거로 당신이 옳을 수도 있다고 생각합니다. –