수색…


소개

x86 패밀리는 오랫동안 사용되어 왔으며, 공개 된 지식 (또는 그렇게 공개적이지는 않은)이 발견되고 개발 된 많은 트릭과 기술이 있습니다. 이러한 트릭의 대부분은 많은 명령어가 효과적으로 동일한 작업을 수행한다는 사실을 이용합니다. 그러나 다른 버전은 더 빠르거나 메모리를 절약하거나 플래그에 영향을 미치지 않습니다. 여기에는 많은 트릭이 발견되었습니다. 각자 장단점이 있으므로 목록에 있어야합니다.

비고

의심 스러울 때, 당신은 항상 포괄적 인 인텔 64 및 IA-32 아키텍처 최적화 레퍼런스 매뉴얼을 참조 할 수 있습니다. 이것은 x86 아키텍쳐 자체의 훌륭한 자원입니다.

레지스터 비공개

레지스터를 0으로 만드는 명백한 방법은 MOV0 으로 만드는 것입니다 (예 :

B8 00 00 00 00    MOV eax, 0

이것은 5 바이트 명령어입니다.

플래그 ( MOV 가 플래그에 영향을주지 않습니다)를 망가 뜨리려한다면 XOR 명령을 사용하여 레지스터를 비트 단위로 XOR 할 수 있습니다.

33 C0             XOR eax, eax

이 명령어는 2 바이트 만 필요하며 모든 프로세서에서 더 빠르게 실행 됩니다.

캐리 플래그를 레지스터로 이동

배경

캐리 ( C ) 플래그가 레지스터에 넣으려는 값을 보유하고 있다면 순진한 방법은 다음과 같이하는 것입니다.

    mov  al, 1
    jc   NotZero
    mov  al, 0
NotZero:

'sbb'사용

점프를 피하면서보다 직접적인 방법은 "차용과 함께 빼기"를 사용하는 것입니다.

    sbb  al,al    ; Move Carry to al

C 가 0이면 al 은 0이됩니다. 그렇지 않으면 0xFF ( -1 )가됩니다. 0x01 이 필요하면 다음을 추가하십시오.

    and  al, 0x01 ; Mask down to 1 or 0

찬성

  • 같은 크기의 약
  • 2 개 또는 1 개의 적은 명령어
  • 비싼 점프

단점

  • 이 기술에 익숙하지 않은 독자에게는 불투명합니다.
  • 다른 플래그를 변경합니다.

0에 대한 레지스터 테스트

배경

레지스터에 0이 들어 있는지 알아 보려면 순진한 방법은 다음과 같습니다.

    cmp   eax, 0

그러나 이것에 대한 opcode를 보면 다음과 같이 나타납니다 :

83 F8 00      cmp   eax, 0

test 사용

    test   eax, eax      ; Equal to zero?

당신이 얻는 opcode를 검사하십시오 :

85 c0         test   eax, eax

찬성

  • 2 바이트 만!

단점

  • 이 기술에 익숙하지 않은 독자에게는 불투명하다.

이 기술에 대한 Q & A 질문을 살펴볼 수도 있습니다.

리눅스 시스템 콜은 덜 부 풀린다.

32 비트 리눅스에서 시스템 호출은 일반적으로 sysenter 명령어를 사용하여 수행됩니다 (오래된 프로그램은 현재 사용되지 않는 int 0x80 사용하기 때문에 보통 말합니다). 그러나 이것은 프로그램에서 많은 공간을 차지할 수 있습니다. 단축하고 속력을 높이기 위해 모서리를자를 수 있습니다.
이는 일반적으로 32 비트 Linux에서 시스템 호출의 레이아웃입니다.

mov eax, <System call number>
mov ebx, <Argument 1> ;If applicable
mov ecx, <Argument 2> ;If applicable
mov edx, <Argument 3> ;If applicable
push <label to jump to after the syscall>
push ecx
push edx
push ebp
mov ebp, esp
sysenter

그것은 엄청난 권리 다! 그러나이 혼란을 피하기 위해 우리가 끌어낼 수있는 몇 가지 트릭이 있습니다.
첫 번째는 ebp를 3 개의 32 비트 레지스터 크기 (12 바이트)만큼 줄인 esp 값으로 설정하는 것입니다. 이것은 ebp, edx 및 ecx를 쓰레기로 덮어 쓰는 것이 좋습니다 (예 : 어쨌든 그 레지스터에 직접 값을 이동시킬 때와 같이). LEA 명령을 사용하여이 작업을 수행 할 수 있으므로 필요하지 않습니다. ESP 자체의 가치에 영향을줍니다.

mov eax, <System call number>
mov ebx, <Argument 1>
mov ecx, <Argument 2>
mov edx, <Argument 3>
push <label to jump to after the syscall>
lea ebp, [esp-12]
sysenter

그러나 시스템 호출이 sys_exit이라면 아무것도 수행하지 않고 도망 갈 수 있습니다!

mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter

3 또는 5로 곱하기

배경

레지스터와 상수의 곱을 얻고 다른 레지스터에 저장하려면 순진한 방법은 다음과 같습니다.

    imul ecx, 3      ; Set ecx to 5 times its previous value
    imul edx, eax, 5 ; Store 5 times the contend of eax in edx

lea 사용

곱셈은 ​​값 비싼 연산입니다. 이동과 추가의 조합을 사용하는 것이 더 빠릅니다. esp 또는 rsp 가 아닌 32 비트 또는 64 비트 레지스터의 경쟁을 3 또는 5 씩 섞는 특별한 경우에 lea 명령을 사용할 수 있습니다. 이것은 주소 계산 회로를 사용하여 제품을 신속하게 계산합니다.

    lea ecx, [2*ecx+ecx] ; Load 2*ecx+ecx = 3*ecx into ecx
    lea edx, [4*edx+edx] ; Load 4*edx+edx = 5*edx into edx

많은 어셈블러들도 이해할 것입니다.

    lea ecx, [3*ecx]
    lea edx, [5*edx]

다른 모든 가능한 피승수가 ebp 또는 rbp 경우, 결과 명령어 길이는 imul 을 사용할 때와 동일합니다.

찬성

  • 훨씬 더 빨리 실행

단점

  • 당신의 피승수 인 경우 ebp 또는 rbp 는 한 바이트 이상 그들을 사용합니다 imul
  • 어셈블러가 바로 가기를 지원하지 않는다면 더 많이 입력하십시오.
  • 이 기술에 익숙하지 않은 독자에게는 불투명하다.


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow