You use a disassembler to read the code, but accept that static analysis is a hard problem you might as well be looking at garbage.
You use runtime disassembly, the primtive disassembly feature built into most systems level debuggers (and the one good systems level debugger ;-)
You can replace instruction in memory with the 'a'ssemble command you can test your hypothesis without affecting that actual code on disk. Once you path the program in memory and see everything to your liking, you fire up a hex editor and patch that memory region. Patching is an art; if you new instruction can overwrite a region of the code, you can insert a jump to the new instructions, then 'ret'urn from them with another jmp to your call point. If, otoh, you're adding more code than the instructions you're replacing, then you will need to find more places to stuff it, or you might need to reconstruct the program header and adjust the sizes.
Reversing and binary patching are two different but related arcane arts. Reversing is the understanding, patching is the modification. Many people do both, but just understanding one doesn't mean you can understand the other. When it comes right down to it, binary patching can be done in a million different ways, regardless of how the reversing was done, and it doesn't get nearly the respect it deserves. Most people tend to lump reversing and all related (binary patching, anti-anti-debugging magic, etc) arts together, but they all deserve separate discussions and explanations.
(Sorry for the extremely long winded, slightly off-topic reply.)
You use runtime disassembly, the primtive disassembly feature built into most systems level debuggers (and the one good systems level debugger ;-)
You can replace instruction in memory with the 'a'ssemble command you can test your hypothesis without affecting that actual code on disk. Once you path the program in memory and see everything to your liking, you fire up a hex editor and patch that memory region. Patching is an art; if you new instruction can overwrite a region of the code, you can insert a jump to the new instructions, then 'ret'urn from them with another jmp to your call point. If, otoh, you're adding more code than the instructions you're replacing, then you will need to find more places to stuff it, or you might need to reconstruct the program header and adjust the sizes.
This is called reverse engineering, google it.