NOP is used to align instruction addresses. This is usually encountered for example when writing Shellcode to exploit buffer overflow or format string vulnerability.
Say you have a relative jump to 100 bytes forwards, and make some modifications to the code. The chances are that your modifications mess up the jump target's address and as such you'd have to also change the aforementioned relative jump. Here, you can add
NOPs to push the target address forward. If you have multiple
NOPs between the target address and the jump instruction, you can remove the
NOPs to pull the target address backward.
This would not be a problem if you are working with an assembler which supports labels. You can simply do
JXX someLabel(where JXX is some conditional jump) and the assembler will replace the
someLabel with the address of that label. However, if you simply modify the assembled machine code(the actual opcodes) by hand(as it sometimes happens with writing shellcode), you also have to change the jump instruction manually. Either you modify it, or then move the target code address by using
Another use-case for
NOP instruction would be something called a NOP sled. In essence the idea is to create a large enough array of instructions which cause no side-effects(such as
NOP or incrementing and then decrementing a register) but increase the instruction pointer. This is useful for example when one wants to jump to a certain piece of code which address isn't known. The trick is to place the said NOP sled in front of the target code and then jumping somewhere to the said sled. What happens is that the execution continues hopefully from the array which has no side-effects and it traverses forwards instruction-per-instruction until it hits the desired piece of code. This technique is commonly used in aforementioned buffer overflow exploits and especially to counter security measures such as ASLR.
Yet another particular use for the
NOP instruction is when one is modifying code of some program. For example, you can replace parts of conditional jumps with
NOPs and as such circumvent the condition. This is a often used method when "cracking" copy protection of software. At simplest it's just about removing the assembly code construct for
if(genuineCopy) ... line of code and replacing the instructions with
NOPs and.. Voilà! No checks are made and non-genuine copy works!
Note that in essence both examples of shellcode and cracking do the same; modify existing code without updating the relative addresses of operations which rely on relative addressing.