Mục đích của lệnh NOP và câu lệnh căn chỉnh trong cụm x86


14

Đã được một năm kể từ lần cuối tôi tham gia một lớp lắp ráp. Trong lớp đó, chúng tôi đã sử dụng MASM với các thư viện Irvine để giúp lập trình dễ dàng hơn.

Sau khi chúng tôi thực hiện hầu hết các hướng dẫn, anh ấy nói rằng về cơ bản, hướng dẫn NOP không làm gì cả và không phải lo lắng về việc sử dụng nó. Dù sao, đó là về giữa kỳ và anh ta có một số mã ví dụ sẽ không chạy đúng, vì vậy anh ta bảo chúng tôi thêm một lệnh NOP và nó hoạt động tốt. Tôi hỏi tôi sau giờ học tại sao và những gì nó thực sự đã làm, và anh ấy nói anh ấy không biết.

Có ai biết không?


NOP không làm gì cả, nhưng nó tiêu thụ chu kỳ. Tôi không nghĩ câu hỏi của bạn có thể được trả lời, nếu không có mã chúng tôi chỉ có thể đoán. Chà, dự đoán của tôi sẽ là một slide NOP ...
yannis

11
NOP thực sự làm một cái gì đó. Nó tăng Con trỏ lệnh.
EricSchaefer

Câu trả lời:


37

Thông thường thời gian NOPđược sử dụng để sắp xếp các địa chỉ hướng dẫn. Điều này thường gặp phải, ví dụ khi viết Shellcode để khai thác lỗi tràn bộ đệm hoặc định dạng lỗ hổng chuỗi .

Giả sử bạn có một bước nhảy tương đối lên 100 byte về phía trước và thực hiện một số sửa đổi cho mã. Cơ hội là các sửa đổi của bạn làm rối tung địa chỉ của mục tiêu nhảy và do đó, bạn cũng phải thay đổi bước nhảy tương đối đã nói ở trên. Tại đây, bạn có thể thêm NOPs để đẩy địa chỉ đích về phía trước. Nếu bạn có nhiều NOPs giữa địa chỉ đích và lệnh nhảy, bạn có thể xóa NOPs để kéo địa chỉ đích về phía sau.

Điều này sẽ không thành vấn đề nếu bạn đang làm việc với trình biên dịch chương trình hỗ trợ nhãn. Bạn có thể chỉ cần làm JXX someLabel(trong đó JXX là một số bước nhảy có điều kiện) và trình biên dịch sẽ thay thế someLabelbằng địa chỉ của nhãn đó. Tuy nhiên, nếu bạn chỉ cần sửa đổi mã máy đã lắp ráp (opcodes thực tế) bằng tay (vì đôi khi xảy ra với việc viết shellcode), bạn cũng phải thay đổi lệnh nhảy bằng tay. Hoặc bạn sửa đổi nó, hoặc sau đó di chuyển địa chỉ mã đích bằng cách sử dụng NOPs.

Một trường hợp sử dụng khác cho NOPhướng dẫn sẽ là một cái gì đó gọi là NOP sled . Về bản chất, ý tưởng là tạo ra một mảng hướng dẫn đủ lớn, không gây ra tác dụng phụ (nhưNOPhoặc tăng và sau đó giảm một thanh ghi) nhưng tăng con trỏ lệnh. Điều này hữu ích, ví dụ khi một người muốn chuyển đến một đoạn mã nhất định mà địa chỉ không được biết đến. Bí quyết là đặt NOP đã nói trượt trước mã mục tiêu và sau đó nhảy đến một nơi nào đó để trượt. Điều xảy ra là việc thực thi tiếp tục hy vọng từ mảng không có tác dụng phụ và nó đi qua hướng dẫn cho mỗi lệnh cho đến khi nó chạm vào đoạn mã mong muốn. Kỹ thuật này thường được sử dụng trong các khai thác tràn bộ đệm nói trên và đặc biệt là để chống lại các biện pháp bảo mật như ASLR .

Tuy nhiên, một cách sử dụng cụ thể khác cho NOPhướng dẫn là khi một người đang sửa đổi mã của một số chương trình. Ví dụ, bạn có thể thay thế các phần của bước nhảy có điều kiện bằng NOPs và như vậy tránh được điều kiện. Đây là phương pháp thường được sử dụng khi " bẻ khóa " bảo vệ bản sao phần mềm. Đơn giản nhất, đó chỉ là về việc loại bỏ cấu trúc mã lắp ráp cho if(genuineCopy) ...dòng mã và thay thế các hướng dẫn bằng NOPs và .. Voilà! Không có kiểm tra được thực hiện và bản sao không chính hãng hoạt động!

Lưu ý rằng về bản chất cả hai ví dụ về shellcode và crack đều làm như nhau; sửa đổi mã hiện có mà không cập nhật địa chỉ tương đối của các hoạt động dựa trên địa chỉ tương đối.


2
Đây là một câu trả lời tuyệt vời, cảm ơn vì đã dành thời gian để giải thích điều này! Cuối cùng tôi cũng hiểu!
alvonellos

Một số hệ thống thời gian thực (PLC xuất hiện trong tâm trí) cho phép bạn "vá" logic mới vào một chương trình hiện có trong khi nó đang chạy. Các hệ thống này để lại NOP trước mỗi đoạn logic nhỏ để bạn có thể ghi đè lên NOP bằng cách chuyển sang logic mới mà bạn đang chèn. Khi kết thúc logic mới, nó sẽ nhảy đến cuối logic gốc mà bạn đang thay thế. Logic mới cũng sẽ có NOP ở phía trước để bạn cũng có thể thay thế logic mới.
Scott Whitlock

10

Một nop có thể được sử dụng trong khe trễ aa khi không có lệnh nào khác có thể được sắp xếp lại để được đặt trong đó.

lw   v0,4(v1)
jr   v0

Trong MIPS, đây sẽ là một lỗi vì tại thời điểm jr đang đọc thanh ghi v0 thì thanh ghi v0 chưa được tải với giá trị từ hướng dẫn trước.

Cách để khắc phục điều này sẽ là:

lw   v0,4(v1)
nop
jr   v0
nop

Điều này sẽ lấp đầy các khe nhỏ sau khi tải từ lệnh và lệnh nhảy đăng ký bằng một nút để lệnh hoàn thành tải trước khi lệnh thanh ghi nhảy được thực thi.

Đọc thêm - một chút về SPARC điền vào các khe trễ . Từ tài liệu đó:

Những gì có thể được đưa vào các khe trễ?

  • Một số hướng dẫn hữu ích nên được thực hiện cho dù bạn phân nhánh hay không.
  • Một số hướng dẫn chỉ hoạt động hữu ích khi bạn phân nhánh (hoặc khi bạn không phân nhánh), nhưng không gây hại gì nếu được thực hiện trong trường hợp khác.
  • Khi thất bại, một lệnh NOP

Những gì KHÔNG PHẢI được đưa vào khe trễ?

  • Bất cứ điều gì đặt CC mà quyết định chi nhánh phụ thuộc vào. Lệnh rẽ nhánh đưa ra quyết định về việc phân nhánh hay không ngay lập tức nhưng thực tế nó không thực hiện nhánh cho đến sau lệnh trì hoãn. (Chỉ chi nhánh bị trì hoãn, không phải là quyết định.)
  • Một hướng dẫn chi nhánh khác. (Điều gì xảy ra nếu bạn làm điều này thậm chí không được xác định! Kết quả là không thể đoán trước!)
  • Một hướng dẫn "thiết lập". Đây thực sự là hai hướng dẫn, không phải một, và chỉ một nửa trong số đó sẽ nằm trong khe trễ. (Trình biên dịch sẽ cảnh báo bạn về điều này.)

Lưu ý tùy chọn thứ ba trong những gì cần đặt vào khe trễ. Lỗi mà bạn thấy có khả năng là ai đó đã lấp đầy một trong những thứ không được đưa vào vị trí trì hoãn. Đặt một nop ở vị trí đó sau đó sẽ sửa lỗi.

Lưu ý: sau khi đọc lại câu hỏi, đây là câu hỏi dành cho x86, không có vị trí trì hoãn (thay vào đó chỉ phân nhánh đường ống). Vì vậy, đó sẽ không phải là nguyên nhân / giải pháp cho lỗi. Trên các hệ thống RISC, đó có thể là câu trả lời.


4
Lưu ý rằng câu hỏi được gắn thẻ x86 và x86 không có vị trí trễ. Sẽ không bao giờ, vì đó là một sự thay đổi đột phá.
MSalters

6

ít nhất một lý do để sử dụng NOP là căn chỉnh. Bộ xử lý x86 đọc dữ liệu từ bộ nhớ chính trong các khối khá lớn và bắt đầu khối để đọc luôn được căn chỉnh, vì vậy nếu một khối có mã, sẽ được đọc nhiều, khối này phải được căn chỉnh. Điều này sẽ dẫn đến tăng tốc ít.


Không chính xác là khối cần phải được căn chỉnh, đó là điều bạn không muốn phải tìm nạp vài byte cuối cùng của khối trước đó. Vì vậy, thật tốt khi chuyển đến 0x1002, bởi vì vẫn còn 14 byte hướng dẫn trong khối 16B được căn chỉnh có chứa địa chỉ đích, nhưng không tốt để nhảy tới 0x099D.
Peter Cordes

3

Một mục đích cho NOP (trong tổng hợp, không chỉ x86) để giới thiệu thời gian trễ. Ví dụ, bạn muốn lập trình một vi điều khiển phải xuất ra một số đèn LED với độ trễ 1 giây. Sự chậm trễ này có thể được thực hiện với NOP (và các chi nhánh). Tất nhiên bạn có thể sử dụng một số THÊM hoặc một cái gì đó khác, nhưng điều đó sẽ làm cho mã không thể đọc được hơn; hoặc có thể bạn cần tất cả các thanh ghi.


1
Thông thường đối với các khung thời gian dài, chẳng hạn như 1 giây, bộ hẹn giờ được sử dụng. NOPS được sử dụng cho các kỷ nguyên trong một độ lớn của đồng hồ - nano và micro giây.
mattnz

Điều này chỉ có ý nghĩa trên một vi điều khiển, không phải là x86 hiện đại. Hầu hết các mã x86 không bão hòa độ rộng đường ống của các CPU không theo thứ tự siêu hiện đại, do đó, việc thêm NOP giữa mỗi lệnh trong hầu hết các mã sẽ chỉ có tác động nhỏ (tôi đoán số cho mã "trung bình" có thể là 5 đến 20% cho tăng gấp đôi số lượng hướng dẫn, với một số mã cho thấy không có chậm lại nhưng một vài vòng chặt chẽ cho thấy gần một suy giảm 2x.) Dù sao, hay quạu mã x86 cũ truyền thống sử dụng các loophướng dẫn cho vòng chậm trễ , không NOPs.
Peter Cordes

3

Nói chung trên 80x86, các hướng dẫn NOP không bắt buộc về tính chính xác của chương trình, mặc dù đôi khi trên một số máy, một NOP được đặt ở vị trí chiến lược có thể khiến mã chạy nhanh hơn. Ví dụ, trên 8086, mã sẽ được tìm nạp trong các đoạn hai byte và bộ xử lý có bộ đệm "prefetch" bên trong có thể chứa ba khối như vậy. Một số hướng dẫn sẽ thực thi nhanh hơn mức có thể tìm nạp, trong khi các hướng dẫn khác sẽ mất một lúc để thực thi. Trong các hướng dẫn chậm, bộ xử lý sẽ cố gắng lấp đầy bộ đệm tìm nạp trước, để nếu một vài lệnh tiếp theo nhanh, chúng có thể được thực thi nhanh chóng. Nếu lệnh theo lệnh chậm bắt đầu trên một ranh giới từ chẵn, sáu lệnh tiếp theo có giá trị sẽ được tìm nạp trước; nếu nó bắt đầu trên một ranh giới byte lẻ, chỉ có năm byte sẽ được tìm nạp trước.

Các vấn đề căn chỉnh bộ nhớ như vậy có thể ảnh hưởng đến tốc độ chương trình, nhưng chúng thường không ảnh hưởng đến tính chính xác. Mặt khác, có một số vấn đề liên quan đến tìm nạp trước trên các bộ xử lý cũ hơn mà NOP có thể ảnh hưởng đến tính chính xác. Nếu một lệnh thay đổi một byte mã đã được tìm nạp trước, thì 8086 (và tôi nghĩ rằng 80286 và 80386) sẽ thực thi lệnh được tìm nạp trước mặc dù nó không còn khớp với những gì trong bộ nhớ. Thêm một hoặc hai NOP giữa hướng dẫn làm thay đổi bộ nhớ và byte mã bị thay đổi có thể ngăn byte mã được tìm nạp cho đến khi nó được ghi. Lưu ý, nhân tiện, nhiều chương trình chống sao chép đã khai thác loại hành vi này; lưu ý quá, tuy nhiên, hành vi này không được đảm bảo. Các biến thể bộ xử lý khác nhau có thể xử lý tìm nạp trước khác nhau, một số có thể làm mất hiệu lực các byte được tìm nạp trước nếu bộ nhớ mà chúng được đọc bị sửa đổi và các ngắt thường sẽ làm mất hiệu lực bộ đệm tìm nạp trước; mã sẽ được tải lại khi ngắt trở lại.


3

Có một trường hợp cụ thể x86 vẫn không được mô tả trong các câu trả lời khác: xử lý ngắt. Đối với một số kiểu của nó, có thể có các phần mã khi ngắt bị vô hiệu hóa vì mã chính hoạt động với một số dữ liệu được chia sẻ với các trình xử lý ngắt, nhưng thật hợp lý khi cho phép ngắt giữa các phần đó. Nếu một người ngây thơ viết


    STI
    CLI

điều này sẽ không xử lý các ngắt đang chờ xử lý vì, trích dẫn Intel:

Sau khi cờ IF được đặt, bộ xử lý bắt đầu phản hồi các ngắt ngoài có thể che được sau khi lệnh tiếp theo được thực thi.

vì vậy điều này sẽ được viết lại ít nhất là:


    STI
    NOP
    CLI

Trong biến thể thứ hai, tất cả các ngắt đang chờ xử lý sẽ được xử lý chỉ giữa NOP và CLI. (Tất nhiên, có thể có nhiều biến thể thay thế, khi nhân đôi hướng dẫn STI. Nhưng NOP rõ ràng rõ ràng hơn, ít nhất là đối với tôi.)


-2

NOP có nghĩa là không hoạt động

Nó thường được sử dụng để chèn hoặc xóa mã máy hoặc trì hoãn thực thi một mã cụ thể.

Cũng được sử dụng bởi các cracker và debuggers để thiết lập các điểm dừng.

Vì vậy, có thể làm một cái gì đó như: XCHG BX, BX cũng sẽ dẫn đến kết quả tương tự.

Âm thanh với tôi như thể có một vài hoạt động vẫn đang được xử lý và do đó nó gây ra lỗi.

Nếu bạn quen thuộc với VB, tôi có thể cho bạn một ví dụ:

Nếu bạn tạo một hệ thống đăng nhập trong vb và tải 3 trang cùng nhau - facebook, youtube và twitter trong 3 tab khác nhau.

Và sử dụng 1 nút đăng nhập cho tất cả. Nó có thể báo lỗi nếu kết nối internet của bạn chậm. Có nghĩa là một trong những trang chưa được tải. Vì vậy, chúng tôi đưa vào Application.DoEvents để khắc phục điều này. Cách tương tự trong lắp ráp NOP có thể được sử dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.