x86 어셈블리의 NOP 명령어 및 align 문


15

내가 마지막으로 수업을한지 1 년이 지났다. 이 클래스에서는 Irvine 라이브러리와 함께 MASM을 사용하여보다 쉽게 ​​프로그래밍 할 수있었습니다.

우리는 대부분의 지시를 겪은 후 NOP 지시는 본질적으로 아무것도하지 않으며 그것을 사용하는 것에 대해 걱정할 필요가 없다고 말했다. 어쨌든, 그것은 중간 쯤 됐고 제대로 실행되지 않는 예제 코드가 있으므로 NOP 명령을 추가하라는 메시지가 표시되어 정상적으로 작동했습니다. 나는 수업이 끝난 후 왜 그리고 무엇을 실제로했는지 물었고 그는 알지 못한다고 말했다.

누구 아세요?


NOP는 아무것도하지 않지만 사이클을 소비합니다. 우리가 추측 할 수있는 코드가 없으면 귀하의 질문에 대답 할 수 있다고 생각하지 않습니다. 글쎄, 내 추측은 NOP 슬라이드 가 될 것입니다 ...
yannis

11
NOP는 실제로 무언가를합니다. 명령 포인터가 증가합니다.
EricSchaefer

답변:


37

NOP명령 주소를 정렬하는 데 종종 시간 이 사용됩니다. 버퍼 오버플로 또는 형식 문자열 취약점 을 악용하기 위해 Shellcode 를 작성할 때 일반적으로 발생 합니다 .

상대가 100 바이트 앞으로 이동하고 코드를 약간 수정했다고 가정 해보십시오. 수정 사항이 점프 대상의 주소를 엉망으로 만들 수 있습니다. 따라서 위에서 언급 한 상대 점프도 변경해야합니다. 여기에서을 추가 NOP하여 대상 주소를 전달할 수 있습니다. NOP대상 주소와 점프 명령 사이에 s 가 여러 개인 경우 NOPs를 제거 하여 대상 주소를 뒤로 당길 수 있습니다 .

레이블을 지원하는 어셈블러를 사용하는 경우에는 문제가되지 않습니다. JXX someLabelJXX가 조건부 점프 인 경우 간단하게 수행 할 수 있으며 어셈블러가 someLabel해당 레이블의 주소로 대체합니다 . 그러나 수동으로 조립 된 기계어 코드 (실제 opcode)를 손으로 수정하는 경우 (쉘 코드 작성과 함께 발생하는 경우) 점프 명령을 수동으로 변경해야합니다. 수정하거나 NOPs 를 사용하여 대상 코드 주소를 이동하십시오 .

NOP명령어의 또 다른 유스 케이스 는 NOP sled 입니다. 본질적으로 아이디어는 부작용을 일으키지 않는 충분히 큰 명령 배열을 만드는 것입니다.NOP또는 레지스터를 증가시킨 다음 감소시키는 것) 명령 포인터를 증가시킵니다. 예를 들어 주소를 알 수없는 특정 코드로 이동하려는 경우에 유용합니다. 요령은 상기 NOP 슬 레드를 목표 코드 앞에 놓고 상기 슬 레드 어딘가에 점프하는 것이다. 결과는 부작용이없는 배열에서 실행이 계속되기를 희망하며 원하는 코드 조각에 부딪 칠 때까지 명령 당 명령을 순회합니다. 이 기술은 앞에서 언급 한 버퍼 오버플로 악용 및 특히 ASLR 과 같은 보안 조치에 대응하는 데 일반적으로 사용됩니다 .

NOP명령에 대한 또 다른 특정 용도 는 일부 프로그램의 코드를 수정하는 경우입니다. 예를 들어 조건부 점프의 일부를 NOPs로 바꾸어 조건을 우회 할 수 있습니다. 이것은 소프트웨어의 복사 방지를 " 크래킹 " 할 때 자주 사용되는 방법 입니다. 가장 간단한 방법 if(genuineCopy) ...은 코드 라인에 대한 어셈블리 코드 구문을 제거 하고 명령어를 NOPs 및 ..로 대체하는 것입니다 . 점검이없고 비정품 복사가 작동합니다!

본질적으로 쉘 코드와 크래킹의 두 가지 예는 동일합니다. 상대 주소 지정에 의존하는 작업의 상대 주소를 업데이트하지 않고 기존 코드를 수정합니다.


2
시간을내어 설명해 주셔서 감사합니다. 나는 마침내 이해한다!
alvonellos

특정 실시간 시스템 (PLC를 염두에두고)을 사용하면 실행중인 기존 프로그램에 새로운 로직을 "패치"할 수 있습니다. 이러한 시스템은 모든 작은 로직 앞에 NOP를 남기므로 삽입하는 새로운 로직으로 점프하여 NOP를 덮어 쓸 수 있습니다. 새로운 로직이 끝나면 교체하려는 원래 로직의 끝으로 이동합니다. 새로운 로직에는 NOP가 앞에 있으므로 새로운 로직도 교체 할 수 있습니다.
Scott Whitlock

10

다른 명령이 재 순서화 될 수 없을 때 지연 슬롯 에서 nop가 사용될 수있다.

lw   v0,4(v1)
jr   v0

MIPS에서 이것은 jr이 레지스터 v0을 읽을 때 레지스터 v0이 이전 명령의 값으로 아직로드되지 않았기 때문에 버그가됩니다.

이 문제를 해결하는 방법은 다음과 같습니다.

lw   v0,4(v1)
nop
jr   v0
nop

이는로드 레지스터 명령이 실행되기 전에로드 워드 명령이 완료되도록로드 워드 및 점프 레지스터 명령 다음에 처리 된 슬롯을 nop로 채 웁니다.

추가 읽기- 지연 슬롯의 SPARC 채우기에 대한 비트 . 그 문서에서 :

지연 슬롯에 무엇을 넣을 수 있습니까?

  • 분기 여부에 관계없이 실행해야 할 유용한 명령입니다.
  • 유용한 일부 명령어는 분기 할 때 (또는 분기하지 않을 때) 작동하지만 다른 경우에는 해를 입히지 않습니다.
  • 다른 모든 것이 실패하면 NOP 명령

지연 슬롯에 넣지 말아야 할 것은 무엇입니까?

  • 지점 결정에 따라 CC를 설정하는 모든 것 분기 명령은 분기 여부를 결정하지만 지연 명령이 끝날 때까지 실제로 분기를 수행하지는 않습니다. (지사 만 결정이 아니라 지연됩니다.)
  • 다른 지사 명령. (이 작업을 수행하지 않으면 어떻게되는지 정의되지 않습니다! 결과는 예측할 수 없습니다!)
  • "세트"명령. 이것은 실제로 하나가 아닌 두 개의 명령어이며, 그중 절반 만이 지연 슬롯에 있습니다. (어셈블러가 이에 대해 경고합니다.)

지연 슬롯에 넣을 내용의 세 번째 옵션에 유의하십시오. 당신이 본 버그는 누군가 지연 슬롯에 넣지 말아야 할 것 중 하나를 채우는 것일 수 있습니다. 해당 위치에 nop을 넣으면 버그가 수정됩니다.

참고 : 질문을 다시 읽은 후 지연 슬롯이없는 x86 용입니다 (분기는 대신 파이프 라인을 중단시킵니다). 따라서 버그의 원인 / 해결책은 아닙니다. RISC 시스템에서는 이것이 정답 일 수 있습니다.


4
질문에 x86 태그가 지정되어 있고 x86에는 지연 슬롯이 없습니다. 또한 중대한 변화이기 때문에 절대로 그렇게하지 않을 것입니다.
MSalters

6

NOP를 사용하는 최소한 하나의 이유는 정렬입니다. x86 프로세서는 주 메모리에서 상당히 큰 블록으로 데이터를 읽으며, 블록에서 시작으로의 읽기는 항상 정렬되므로 코드 블록이있는 경우 많이 읽 히면이 블록을 정렬해야합니다. 이것은 약간의 속도 향상을 초래할 것입니다.


블록을 정확하게 정렬 할 필요는 없으며 이전 블록의 마지막 몇 바이트를 가져와야 할 필요가 없습니다. 따라서 0x100216B의 정렬 된 블록에 대상 주소를 포함하는 14 바이트의 명령이 여전히 있지만으로 이동하는 것은 좋지 않기 때문에 점프하는 것이 0x099D좋습니다.
Peter Cordes

3

NOP (x86뿐만 아니라 일반 어셈블리)의 목적은 시간 지연을 도입하는 것입니다. 예를 들어 1 초 지연으로 일부 LED로 출력해야하는 마이크로 컨트롤러를 프로그래밍하려고합니다. 이 지연은 NOP (및 분기)로 구현할 수 있습니다. 물론 ADD 나 다른 것을 사용할 수는 있지만 코드를 더 읽을 수 없게 만듭니다. 또는 모든 레지스터가 필요할 수 있습니다.


1
일반적으로 1 초와 같은 장시간 프레임의 경우 타이머가 사용됩니다. NOPS는 나노 및 마이크로 초의 클록 크기 내에서 에포크에 사용됩니다.
mattnz

이것은 최신 x86이 아닌 마이크로 컨트롤러 에서만 의미가 있습니다. 대부분의 x86 코드는 최신 슈퍼 스칼라 비 순차 CPU의 파이프 라인 너비를 포화시키지 않으므로 대부분의 코드에서 모든 명령 사이에 NOP를 추가하면 영향이 적습니다 ( "평균"코드의 수는 명령 수를 두 배로 늘리면 5-20 %, 일부 코드는 속도 저하가 나타나지 않지만 몇 개의 꽉 루프가 거의 2 배의 속도 저하를 나타냅니다.) 어쨌든 낡고 오래된 x86 코드는 전통적으로 NOP가 아닌 지연 루프에 대한 loop명령을 사용 했습니다 .
Peter Cordes

3

일반적으로 80x86에서는 프로그램 정확성을 위해 NOP 명령이 필요하지 않지만, 일부 시스템에서는 전략적으로 배치 된 NOP로 인해 코드가 더 빨리 실행될 수 있습니다. 예를 들어, 8086에서 코드는 2 바이트 청크로 페치되며 프로세서에는 내부 "프리 페치"버퍼가있어 3 개의 청크를 보유 할 수 있습니다. 일부 명령어는 페치 할 수있는 것보다 빠르게 실행되지만 다른 명령어는 실행하는 데 시간이 걸립니다. 느린 명령어 중에 프로세서는 프리 페치 버퍼를 채우려 고 시도하므로 다음 몇 개의 명령어가 빠르면 빠르게 실행될 수 있습니다. 느린 명령어 다음의 명령어가 짝수 단어 경계에서 시작되면 다음 6 바이트 분량의 명령어가 프리 페치됩니다. 홀수 바이트 경계에서 시작하면 5 바이트 만 프리 페치됩니다.

이러한 메모리 정렬 문제는 프로그램 속도에 영향을 줄 수 있지만 일반적으로 정확성에는 영향을 미치지 않습니다. 반면, NOP가 정확성에 영향을 줄 수있는 구형 프로세서에는 프리 페치 관련 문제가 있습니다. 명령어가 이미 프리 페치 된 코드 바이트를 변경하면 8086 (및 80286 및 80386)은 더 이상 메모리에있는 것과 일치하지 않더라도 프리 페치 된 명령어를 실행합니다. 메모리를 변경하는 명령어와 변경된 코드 바이트 사이에 NOP를 추가하면 코드 바이트가 쓰여질 때까지 페치되지 않을 수 있습니다. 그런데 많은 복사 방지 체계가 이러한 종류의 동작을 악용했습니다. 그러나이 동작이 보장되지는 않습니다. 프로세서 종류에 따라 프리 페치를 다르게 처리 할 수 ​​있습니다. 일부는 읽은 메모리가 수정되면 프리 페치 된 바이트를 무효화 할 수 있으며 인터럽트는 일반적으로 프리 페치 버퍼를 무효화합니다. 인터럽트가 반환되면 코드가 다시 가져옵니다.


3

다른 답변에 설명되지 않은 x86 특정 사례가 있습니다 : 인터럽트 처리. 일부 스타일의 경우 인터럽트가 비활성화되면 코드 섹션이있을 수 있습니다. 주 코드는 인터럽트 핸들러와 공유되는 일부 데이터와 작동하기 때문에 그러한 섹션 사이에 인터럽트를 허용하는 것이 합리적입니다. 순진하게 글을 쓴다면


    STI
    CLI

인텔을 인용하여 보류중인 인터럽트를 처리하지 않습니다.

IF 플래그가 설정되면 프로세서는 다음 명령이 실행 된 후 마스크 가능한 외부 인터럽트에 응답하기 시작합니다.

따라서 최소한 다음과 같이 다시 작성해야합니다.


    STI
    NOP
    CLI

두 번째 변형에서는 보류중인 모든 인터럽트가 NOP와 CLI 사이에서만 처리됩니다. (물론, STI 명령을 두 배로 늘리는 등 여러 가지 대안이있을 수 있습니다. 그러나 명시 적 NOP는 적어도 저에게는 더 분명합니다.)


-2

NOP는 작동 없음을 의미합니다

일반적으로 머신 코드를 삽입 또는 삭제하거나 특정 코드의 실행을 지연시키는 데 사용됩니다.

크래커 및 디버거에서도 중단 점을 설정하는 데 사용됩니다.

XCHG BX, BX도 같은 결과를 낳을 것입니다.

아직 처리중인 작업이 거의 없어서 오류가 발생한 것처럼 들립니다.

VB에 익숙하다면 다음과 같은 예를들 수 있습니다.

vb에서 로그인 시스템을 만들고 3 개의 다른 탭에서 페이스 북, youtube 및 twitter 3 페이지를 함께로드하는 경우

그리고 하나의 로그인 버튼을 모두 사용하십시오. 인터넷 연결이 느리면 오류가 발생할 수 있습니다. 이는 페이지 중 하나가 아직로드되지 않았 음을 의미합니다. 이를 극복하기 위해 Application.DoEvents를 넣었습니다. 어셈블리 NOP와 같은 방식으로 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.