Книга: Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software
Назад: 15. Anti-Disassembly
Дальше: Defeating Disassembly Algorithms

Disassembly is not a simple problem. Sequences of executable code can have multiple disassembly representations, some that may be invalid and obscure the real functionality of the program. When implementing anti-disassembly, the malware author creates a sequence that tricks the disassembler into showing a list of instructions that differ from those that will be executed.

Anti-disassembly techniques work by taking advantage of the assumptions and limitations of disassemblers. For example, disassemblers can only represent each byte of a program as part of one instruction at a time. If the disassembler is tricked into disassembling at the wrong offset, a valid instruction could be hidden from view. For example, examine the following fragment of disassembled code:

                jmp     short near ptr loc_2+1 ; ---------------------------------------------------------------------------  loc_2:                                  ; CODE XREF: seg000:00000000j                 call    near ptr 15FF2A71h                  or      [ecx], dl                 inc     eax ; ---------------------------------------------------------------------------                 db    0

This fragment of code was disassembled using the linear-disassembly technique, and the result is inaccurate. Reading this code, we miss the piece of information that its author is trying to hide. We see what appears to be a call instruction, but the target of the call is nonsensical . The first instruction is a jmp instruction whose target is invalid because it falls in the middle of the next instruction.

Now examine the same sequence of bytes disassembled with a different strategy:

                jmp     short loc_3 ; ---------------------------------------------------------------------------                 db 0E8h ; ---------------------------------------------------------------------------  loc_3:                                  ; CODE XREF: seg000:00000000j                 push    2Ah                 call    Sleep 

This fragment reveals a different sequence of assembly mnemonics, and it appears to be more informative. Here, we see a call to the API function Sleep at . The target of the first jmp instruction is now properly represented, and we can see that it jumps to a push instruction followed by the call to Sleep. The byte on the third line of this example is 0xE8, but this byte is not executed by the program because the jmp instruction skips over it.

This fragment was disassembled with a flow-oriented disassembler, rather than the linear disassembler used previously. In this case, the flow-oriented disassembler was more accurate because its logic more closely mirrored the real program and did not attempt to disassemble any bytes that were not part of execution flow. We’ll discuss linear and flow-oriented disassembly in more detail in the next section.

So, disassembly is not as simple as you may have thought. The disassembly examples show two completely different sets of instructions for the same set of bytes. This demonstrates how anti-disassembly can cause the disassembler to produce an inaccurate set of instructions for a given range of bytes.

Some anti-disassembly techniques are generic enough to work on most disassemblers, while some target specific products.

Назад: 15. Anti-Disassembly
Дальше: Defeating Disassembly Algorithms

sss
sss