1.4. Command Format of the Intel Microprocessor


1.4. Command Format of the Intel Microprocessor

1.4.1. General Considerations

When you consider the list of Intel Pentium microprocessor, you might ask, how are these commands stored in computer memory? And what is the difference, for example, between MOV EAX, EBX and MOV EAX, EDI commands? The goal of this section is to demonstrate some regular patterns of encoding Intel processor commands. If you become interested in analysis of the command format, this will help you considerably when investigating executable code.

Fig. 1.5 shows the memory area, in which the program code is located. This dump was created using the OllyDbg debugger, which will be covered later in this chapter. To decrypt this sequence of bytes and turn it into machine commands (to be more precise, into Assembly commands), it is necessary to know the formats of these commands. For the moment, I'll concentrate your attention on this topic.

image from book
Figure 1.5: Dump of the program code

First, it is necessary to point out that the command length might range from 1 byte to 10 bytes or even more. Fig. 1.6 shows the general format of the Intel processor command. As you can see, the command structure might be complex. Fortunately, however, it is possible to understand its structure, because the processor correctly interprets the command code and executes it. With luck, your intention of understanding it will not be a hopeless job.

image from book
Figure 1.6: The Intel processor command format

To begin with, consider prefixes. As you can see, prefixes are optional. It would be logical to assume that all prefixes shown in Fig. 1.6 must have strictly defined codes to ensure that the prefix and the command code cannot be confused in the course of decryption. There are four types of prefixes:

  • The command prefix can take the following values:

    • F3H — The repeat prefix REPE/REPZ

    • F2H — The repeat prefix REPNE/REPNZ

    • F0x — The bus blocking prefix LOCK

  • The address size (size replacement) prefix takes the 67H value.

  • The operand size (size replacement) prefix takes the 66H value.

  • Segment replacement prefixes take the following values:

    • 2EH — For the CS register

    • 36H — For the SS register

    • 3EH — For the DS register

    • 26H — For the FS register

    • 64H — For the FS register

    • 65H — For the GS register

It is important to mention that no more than two prefixes of the same type can be encountered within the same command. An attempt at writing such a command would cause a processor error.

Thus, knowing prefix codes, it is possible to tell with certainty, from which component the command begins: from the code or from the prefix. Note that this is the case only if you know for sure, from which address the command being studied starts. Otherwise, disassembling will start from the middle of the command, and the resulting Assembly code won't correspond to the reality.

1.4.2. Command Code

Now, consider the code of the processor command The code provided in Listing 1.17 presents a small and easy fragment of an Assembly program.

Listing 1.17: A small Assembly program for studying formats of the Intel processor commands

image from book

 PUSH      EAX PUSH      EBX PUSH      ECX POP       ECX POP       EBX POP       EAX RET 

image from book

As you can see, the contents of three registers, each register in turn, are stored in the stack. Then the values saved in the stack are popped into the same registers. After completion of these commands, the program exits the procedure. Thus, if you consider the memory area, in which these commands are stored, you'll notice the following sequence of bytes:

                          50 53 51 59 5B 58 C3 

The first idea that comes to mind is that each of the preceding commands requires 1 byte. You can see typical 8-bit commands. For example, C3H is nothing but the code of the RET (to be more precise, the RETN) command. The first 6 bytes, however, are the most interesting. First, consider PUSH commands. Here are binary equivalents of these commands: 010100008 (PUSH EAX), 01010011B (PUSH EBX), and 01010001B (PUSH ECX). Note that these commands differ only in their least significant bytes. Consequently, the following conclusion is self-evident: The command as such is encrypted in the command code; in other words, it specifies, which action and which register are subject to the given operation. To confirm this assumption, consider binary codes of the following three POP commands: 01011001B (POP ECX), 01011011B (POP EBX), and 01011000B (POP EAX). The situation becomes clearer. For instance, compare binary representations of the PUSH EBX and POP EBX commands. Note that the first 2 bits are matching. In essence, however, the first 2 bits are also matching for other pairs of commands, such as PUSH EAX/POP EAX and PUSH ECX/POP ECX. To be precise, the first 3 bits are matching. On the other hand, for all PUSH commands, the first 5 bits are matching (01010B). Accordingly, the situation is the same for all POP commands (01011B). The regular pattern just discovered is not a random one. Actually, not only operations but also registers are encrypted in the code of the PUSH reg and POP reg commands. Register codes are universal. They can be encountered not only in the command code but also in the Mod R/M field. This issue will be covered later in this chapter.

For the moment, consider the codes of 32-bit working registers:

  • EAX000B

  • EDI111B

  • EBX011B

  • ESI110B

  • ECX001B

  • ESP100B

  • EDX010B

  • EBP101B

At first glance, everything is straightforward, because a regular pattern has been discovered. However, the situation is not that simple. There are also 16-bit registers, as well as 8-bit registers. But the most disappointing issue is that the codes of the PUSH and POP operations dealing with registers other than the previously-listed working registers or even memory cells that play the role of operands, are different. All of these issues will be covered in due order.

Note that PUSH and POP commands are not applicable to 8-bit registers. Thus, problems with addressing 8-bit registers do not arise in relation to PUSH and POP commands. However, there are also 16-bit registers. It might seem surprising, but 16-bit registers are encoded in the same way as 32-bit registers. For example, the AX register has the 110B code. And what about commands that push data into the stack where 16-bit registers can be encountered? The answer is straightforward: The operand size replacement prefix preceding the command code is used, in other words, 66H. Thus, for example, the PUSH AX command will be represented by 2 bytes: 66 50. A command such as POP EAX will be represented by the following sequence: 66 58. Having encountered this prefix, the processor is informed that within the current command it is necessary to replace a 32-bit operand with a 16-bit one. This allows you to draw the following conclusion: Using 32-bit registers is more efficient than using 16-bit registers.

Unfortunately, the regular patterns related to the codes of the registers in the POP and PUSH commands are limited to this rule. Here are the codes of these commands applicable to the segment registers:

  • PUSH CS0EH

  • PUSH DS1EH

  • PUSH SS16H

  • PUSH ES06H

  • PUSH FS0FAOH

  • PUSH GS0FA8H

  • POP DS1FH

  • POP SS17H

  • POP ES07H

  • POP FS0FA1H

  • POP GS

The only pattern that can be discovered here is that the codes of the pairs of commands (such as PUSH DS/POP DS) differ by one. Also, it is necessary to note that these commands for segment registers FS and GS have 2-byte codes. Because these registers were introduced in newer models of the Intel family of processors, there were no 1-byte codes for them. In general, processor developers are always constrained in their choice of possible solutions; therefore, you shouldn't expect overall regularity in those solutions.

Continuing the investigation process, it is logical to find an answer to the following question: Are the command bytes used anywhere besides at designating the command and the register codes?

Consider conditional jump commands. First, it is necessary to find out how the near jumps (in other words, jumps within 256 bytes) are encoded. Consider a small fragment of an Assembly program (Listing 1.18). Note that although this example has no practical meaning, it allows you to detect certain interesting patterns.

Listing 1.18: A fragment of the Assembly program intended for studying the jump instructions format

image from book

         JZ  _LAB         JNZ _LAB         JB  _LAB         JNB _LAB         JG  _LAB         JNG _LAB _LAB: 

image from book

Viewing this code with the debugger, you'll see the following sequence of bytes:

                74 0A 75 08 72 06 73 04 7F 02 7E 00 

Clearly, every command takes 2 bytes, and the second byte defines the address, to which the jump will take place, provided that appropriate condition is carried out. As you can easily see after considering carefully the codes of the first and the last commands, this is simply displacement (see Fig. 1.6) from the end of the command. Thus, everything is clear — at least for the moment.

Now, consider the first bytes of the command more carefully. The general pattern is as follows:

  • JZ01110100B

  • JNZ01110101B

  • JB01110010B

  • JNB01110011B

  • JG01111111B

  • JNG01111110B

The conclusion is self-evident: The code of the conditional jump operation is simply 70H, and the 4 least significant bits define the condition. At the same time, it is obvious that the least significant bit defines inversion: For JZ, this bit is zero, for JNZ this bit is one, etc. At the same time, some rules of intuitive logic are observed: equal to zero corresponds to the zero value of this bit, and greater than means that this bit is set to one. Bits 1–3 define the condition as such. Because 3 bits allow eight different conditions to be specified, it is possible to combine Table 1.10 and the newly-obtained results to compose Table 1.26.

Table 1.26: Conditional jumps codes

Command

Code

JB/JNAE/JC

001

JBE/JNA

011

JE/JZ

010

JL/JNGE

110

JLE/JNG

111

JO

000

JP/JPE

101

JS

100

A natural question might arise: What about conditional jumps, for which the offset in a 32-bit segment plays the role of the address? To investigate this issue, modify the code fragment presented in Listing 1.18. The modified version is in Listing 1.19.

Listing 1.19: Investigating conditional jump codes when the offset in a 32-bit segment acts as the address

image from book

         JZ  _LAB         JNZ _LAB         JB  _LAB         JNB _LAB         JG  _LAB         JNG _LAB         DB   1000H DUP(0) _LAB: 

image from book

By inserting a data block after the JNB command, you will force the assembler to generate jumps with the 32-bit offset. The result will appear as shown in Listing 1.20.

Listing 1.20: The dump of the code presented in Listing 1.19

image from book

 OF 84 1E  10 00 00 OF 85 18  10 00 00 OF 82 12  10 00 00 OF 83 0C  10 00 00 OF 8F 06  10 00 00 OF 8E 00  10 00 00 

image from book

In the preceding listing, the result is presented as a table, where each row corresponds to its associated command. As you can see, now the command code is made up of 2 bytes. The first byte of each row is always 0FH. The structure of the second byte has been already considered. The operation code is 80H, and it is followed by the condition code and inversion bit. As relates to the address, it looks strange at first. Well, the address (to be more precise, the offset), is simply a normal 32-bit number, for which the standard principle must be used: The most significant byte in the word must have the higher address, and the most significant word must have the higher address. Thus, the result 1E 10 00 00 is nothing but 00 00 10 1E, and this is the exact distance in bytes between the JNZ _LAB command and the RET command. The situation is the same for other conditional jump commands.

1.4.3. The MOD R/M Byte

Consider a seemingly easy operation: MOV EAX, EBX. Its code is made up of 2 bytes: 8B C3. Because there are lots of variants for sending data between registers, it would be logical to assume that both registers are encoded here: EAX and EBX. Also, it would be logical to assume that the first byte is the opcode and the registers are encoded in the second byte. Thus, C3 in binary representation is 11000011. To make a comparative analysis, consider the MOV EBX, EAX command. The code of this command is 8B D8. By the way, this confirms the assumption that the first byte contains the opcode. However, D8H corresponds to 11011000B. Compare this byte to the binary representation of C3. Naturally, these bytes differ from each other by the following bit triplets: 000B and 011B. These are the previously-mentioned codes of the EAX and EBX register. Great! The code of the MOV command operating over two 32-bit registers has been practically clarified. You have encountered the MOD R/M byte, the structure of which will now be considered in more detail (see Fig. 1.6).

The MOD R/M byte has the following three fields (see Fig. 1.6):

  • The MOD field — Along with the R/M field, this field forms 32 possible values: 8 registers and 24 indexing modes. In the example provided earlier, this field had the value 11 and specified that the R/M field would represent the register code.

  • The REG/Code field — This field designates either the register code or the additional 3 bits of the operation code.

  • The R/M field — This field can designate the register as the location of the operand or encode the addressing mode, along with the MOD field.

A reasonable question can arise: What would the difference be between the MOV EAX, EBX and the MOV AX, 5X operations? You have probably guessed this already. The latter command will start with the prefix— the additional 66H byte mentioned previously.

And what about MOV commands where 8-bit registers are encountered? Because the developers were short of 3-bit codes, it would be logical to assume that the code of the command would change. This assumption proves true. For example, the MOV BL, AL command would be encoded by 2 bytes: 8A D8. Note that there are eight 8-bit registers; consequently, they also can be encoded using the same 3 bits:

  • AL000B

  • BL011B

  • CL001B

  • DL010B

  • AH100B

  • BH111B

  • CH101B

  • DH110B

Now, you'll easily notice that the D8H byte corresponds to the BL and AL registers. By the way, it is possible to guess that the MOV command, where there is one register and one immediate operand, must do without the MOD R/M byte. In this case, it is necessary to encode only one operand. For example, the code of the MOV EBX, 1234H command will be equivalent to BB 34120000, and the MOV ECX, 1234H command will correspond to B9 34120000. Try to investigate this issue on your own. As can be easily seen, the command code corresponds to B8H, and the first 3 bits define the register, into which the immediate operand will be saved. However, you'll be surprised when you consider the MOV EAX, 1234H command. The command code will be B8 34120000. This way, the developers have taken into account that the command for moving data into the EAX register (accumulator) will be carried out more often than the command for copying data into other registers. Thus, they made this command shorter.

Consider the fragment in Listing 1.21.

Listing 1.21: A test program for studying the format of MOV commands

image from book

 MOV EAX, DATA1 MOV EBX, DATA1 MOV ECX, DATA1 MOV EDX, DATA1 MOV EDI, DATA1 MOV ESI, DATA1 

image from book

Here, DATA1 is some 32-bit variable. Having disassembled this fragment, you'll obtain the result in Listing 1.22.

Listing 1.22: The disassembled listing of the program shown in Listing 1.21

image from book

 Al   00104000 851D 00104000 850D 00104000 8B15 00104000 853D 00104000 8B35 00104000 

image from book

As can be easily noticed, the EAX register also differs from the other registers. There is a special code for moving data from the memory into the register. As relates to the other commands, the MOD R/M byte is present. Having converted the hex code into binary code, you'll see that the MOD field in all commands equals zero (00B), the REG field encodes the register, and the R/M field equals 101B. It would be logical to assume that the MOD and R/M fields define a certain addressing mode, as with all previously-presented commands, except the one that uses the EAX register. This is so. The current mode assumes that the effective address is determined by only one number - the offset in a 32-bit register. By the way, what would happen if you interchanged the operands in the previously-described commands? Only the command code would change. Everything else should not change, because neither the addressing method nor the register used in the command has changed.

Consider the MOV [EBX], ECX command. As you can see, this command uses indirect addressing through the EBX register. The code of such operations in this command is 89H. The MOD R/M byte contains information about the registers and the mode of addressing — 0B. It can be clearly seen that the MOD field contains 00 and the REG and R/M fields contain the codes of the ECX and EBX registers, respectively. Now, take on a more complicated task: Investigate the MOV [ebx + 10], ECX command. For this command, the disassembler produces the following sequence of bytes: 89 45 0A. As you can see, the command code remained the same. Obviously, the last byte is the offset. The structure of the MOD R/M byte appears as follows: 01001011B. As a result, in comparison to the MOV [EBX], ECX command, only the MOD field has changed. The reason for this is clear: It occurred because the addressing has changed. I hope that you would be able to compose the following table (Table 1.27) explaining the behavior of the MOD R/M byte on your own.

Table 1.27: MOD RIM byte structure in 32-bit addressing

Effective address

MOD field value

R/M field value

[EAX]

00

000

[EBX]

00

011

[ECX]

00

001

[EDX]

00

010

[ESI]

00

110

[EDI]

00

111

Offset32

00

101

[...]

00

100

Offset8 [EAX]

01

000

Offset8 [EBX]

01

011

Offset8 [ECX]

01

001

Offset8 [EDX]

01

010

Offset8 [ESI]

01

110

Offset8 [EDI]

01

111

Offset8 [EBP]

01

101

Offset8 [EBP]

01

100

Offset32 [EAX]

10

000

Offset32 [EBX]

10

011

Offset32 [EBX]

10

001

Offset32 [ECX]

10

010

Offset32 [EDX]

10

010

Offset32 [ESI]

10

110

Offset32 [EDI]

10

111

Offset32 [EBP]

10

101

Offset32 [...]

10

100

EAX/AX/AL

11

000

EBX/BX/BL

11

011

ECX/CX/CL

11

001

EDX/DX/DL

11

010

ESP/SP/AH

11

100

EBP/BP/CH

11

101

ESI/SI/DH

11

110

FDI/DI/BH

11

111

Note 

In Table 1.27, offset8 stands for the 1-byte offset, Offset32 stands for the 4-byte offset, and the [... ] string means that for this combination of the MOD and R/M fields the MOD R/M byte will be followed by the SIB byte.

Consider Table 1.27 more carefully. As you can see, the MOD R/M byte doesn't allow you to define such an important property of relative addressing as the scaling coefficient. Another byte is used for this purpose, the SIB byte. The value of the R/M field set to 100B indicates that the SIB byte must be present.

1.4.4. The SIB Byte

Finally, it is time to consider the SIB byte. Its name stands for scale index base. Accordingly, this byte has the following three fields (see Fig. 1.6).

  • Bits 7-6, the Scale field, specify the scaling coefficient.

  • Bits 5-3, the Index field, specify the register — index.

  • Bits 2-0 define the register that is the Base.

Consider the fragment in Listing 1.23, written in Assembly language.

Listing 1.23: A fragment of the test program for studying the role of the SIB byte

image from book

 MOV [EAX*4][EBX+5], EAX MOV [EBX*4][EAX+5], EAX MOV [ECX*8][EDX+5], EAX MOV [EDX*8][ECX+5], EAX 

image from book

Here are the bytes corresponding to the preceding commands (Listing 1.24).

Listing 1.24: The machine code corresponding to the fragment shown in Listing 1.23

image from book

 89 44 83  05 89 44 98  05 89 44 CA  05 89 44 Dl  05 

image from book

The operation code in all cases equals 89H. The 44H hex number is nothing but the MOD R/M byte. Convert it to the binary format: 44H = 01000100B. Thus, it becomes clear that MOD = 01. This means that the offset must be present in this command. This is so: The offset equals 5, and the byte representing it is the last byte. The REG field is 000B, which means that the data are copied from the EAX register. As relates to the R/M field, it equals 100B, and this is exactly the exception (see Table 1.27), which means that the SIB byte must follow it. By the way, note that all of the preceding commands differ only by this byte. Start investigation from the first command. For this command, the code is as follows: 83H = 10000011B. The Scale field, equal to 10B, sets the scaling coefficient. The Index field is 000B; this is the index register. It equals EAX. The EAX register is used for forming the resulting address. The Base field equals 011B and defines the base register, which equals the EBX register. Thus, everything is clear with this byte. Now it would be expedient to consider the general method of using the e SIB byte (Table 1.28).

Table 1.28: Structure of the SIB byte

Scaling index

Scale field value

Index field value

[EAX]

00

000

[EBX]

00

011

[ECX]

00

001

[EDX]

00

010

[EBP]

00

101

[ESI]

00

110

[EDT]

00

111

Unused

00

100

[EAX*2]

01

000

[EBX*2]

01

011

[ECX*2]

01

001

[EDX*2]

01

010

[EBP*2]

01

101

[ESI*2]

01

110

[EDI*2]

01

111

Unused

01

100

[EAX*4]

10

000

[EBX*4]

10

011

[ECX*4]

10

001

[EDX*4]

10

010

[EBP*4]

10

101

[EST*4]

10

110

[EDT*4]

10

111

Unused

10

100

[EAX*8]

11

000

[EBX*8]

11

011

[ECX*8]

11

001

[EDX*8]

11

010

[EBP*8]

11

101

[ESI*8]

11

110

[EDI*8]

11

111

Unused

11

100

Hopefully, the difference between MOV [EAX*8] [EBX + 10], ECX and MOV [EAX] [EBX*8 + 10], ECX is now clearer. In the first command, the scaling index is represented by the EAX register; in the second command, this role is delegated to the EBX register and, accordingly, the situation is opposite for the base. Also, it becomes clear that commands such as MOV [EAX*4] [EBX*2], EAX are technically impossible.

1.4.5. Simple Example of Manual Disassembling

Now, having gained the necessary experience, you can try to disassemble the code shown in Fig. 1.5. The 55H code stands for the PUSH EBP command. This can be easily discovered if you recall that the code of the PUSH command is 50H and the code of the EBP register equals five (101B). After these codes comes the 8DH code. Clearly, this isn't a prefix, because the prefix codes are well known. Principally, it is possible to consult the manual or enter the command under the debugger. It turns out that this code corresponds to the LEA command. Because the command must have two operands, it is obvious that the command must have the MOD R/M byte. The next byte is ech. After representing it in the binary format, you'll obtain the following result: ECH = 11101100B. If everything is correct, then the first 2 bits define register addressing, in which case the data are stored directly in the register (see Table 1.27). In this case, the next 3 bits (REG) define the register, into which the data will be loaded, and the last bits define the register, from which the data will be obtained. This source register happens to be the ESP register (the 100B code).

The LEA command normally is used for obtaining the address of some variable. How would the command code appear if this is the case? The situation is clear and straightforward. Assume that you have encountered the following command: LEA EBP, DATA1. The result of its disassembling will appear as follows: 8D 2D 00 10 40 00. Clearly, the last 4 bytes of this byte sequence stand for the variable address. How would the MOD R/M byte appear? This is 2DH = 00101101B. Pay attention to the last 3 bits and consult Table 1.27 (with MOD = 00). The last 3 bits specify the 101B number, which means that the effective address will be the offset within a segment — in other words, the direct address of the variable. Hence, it becomes clear that the second byte is followed by the offset.

Now recall Fig. 1.3. The next byte equals 53H. Thus, it is clear that in this case you are dealing with the PUSH EBX command (the 3 = 011B code stands for the EBX register). The next byte is C7H. This is the code of the MOV command, where the destination is either a register or a memory cell (the DWORD data type) and the source is an immediate operand. Clearly, the next byte must be the MOD R/M byte. The value of this byte is 05H = 00000101B. Hence, it is possible to conclude that an immediate operand is loaded into a memory cell. The next 4 bytes must be the address of that cell. Here they are: D0 86 40 00. Now, it becomes possible to determine that the address of the required cell is 004086D0H. Finally, the last byte of this command is 32H. Thus, it is possible to draw the following conclusion: You have decoded the MOV DWORD PTR [004086D0H], 32 command. Why DWORD, you might ask? This is because the command code is C7H. If the command were MOV BYTE PTR [004086D0H], 32, then the C6H code would be used. Now, consider the commands decoded after carrying out this exercise (Listing 1.25).

Listing 1.25: The commands decoded using the manual disassembling technique

image from book

 PUSH EBP LEA EBP, ESP PUSH EBX MOV DWORD PTR [4086D0H], 32 

image from book

Manual disassembling is a tedious job, isn't it? However, having mastered the techniques described in this section and with some hands-on practice, you'll discover that there isn't anything particularly difficult about it.

As it turns out, some microprocessor commands can be represented by at least two different sets of codes. Here is a typical example of such a situation. The MASM32 translator converts the MOV EBX, 34H command into the following sequence of codes: BB 34 00 00 00. In this case, the code of the EBX register is encoded in the first 3 bits of the command code (011B) However, there is another possibility of encoding the same command from a more general point of view — namely, using the MOD R/M byte. When using this representation, the command will appear as the following sequence of bytes: C7 C3 34 00 00 00. As you can see, the second variant of the command representation is 1 byte longer.

1.4.6. Disassembling Problems

The commonly-adopted point of view is that Assembly language is practically the same as machine language. Apparently, it is possible to draw a seemingly obvious conclusion that the code of any Assembly program can be unambiguously reconstructed by the machine code. However, despite this opinion, the situation is not that simple. There are certain problems, which will be covered in this section.

The first problem relates to the reconstruction of the data structure. The only possibility of determining the data structure is analyzing the way, in which these data are used in commands. This is where the problem arises. The data can be accessed in different ways. For example, consider a command that at first glance can be disassembled easily — let this be the MOV DWORD PTR [4086D0H], 32 command. This command shows that some data element (variable) is located at the 4086D0H address. The addressing mode is direct, so everything is clear. However, what would you say about the MOV EAX, [EBX] command? To find out what is stored in the EBX register, it is necessary to analyze the program code. You are lucky if the command being analyzed is preceded, for example, by the sequence in Listing 1.26.

Listing 1.26: The command sequence clarifying that some 32-bit variable is located at the 4176A8H address

image from book

 MOV EAX, 4176A0H ADD EAX, 8 MOV EBX, EAX 

image from book

On the basis of these commands, it becomes clear that some 32-bit variable is stored at the 4176A8H address. However, the actual address is often formed several hundred commands from the command that uses it. Sophisticated manipulations often are used to form that address. If this is the case, then such an address can be determined only by executing the program step by step, in other words, by using debugging mechanisms.

Furthermore, obtaining the variable address often is not enough; it is also necessary to know its size. For example, when dealing with an array, it isn't easy to determine how many elements are contained there. Even knowing the address of the next variable doesn't always help, because there might be additional alignment bytes between two variables.

This situation is further aggravated by the availability of two different commands that allow you to obtain an address of some object in memory. Traditionally, the LEA command was intended for obtaining the address of the specified variable, for example: LEA EAX, a1. Thus, having encountered a byte sequence such as 8D 05 08 10 40 00, you would immediately discover that the address equal to 401008H is loaded into the EAX register. (The 8DH code corresponds to the LEA command, and 05H stands for the MOD R/M byte. Recall that such an analysis has already been conducted several times.) However, there is another command in Assembly language that does the same thing, namely, the MOV reg32, offset var command. The offset keyword makes the assembler substitute the variable address instead of the variable value into the command. Thus, it is difficult to understand without code analysis whether you are dealing with an immediate operand or an address. Note that sometimes carrying out such an analysis might be a difficult task.

Another problem relates to determining jump addresses and procedure addresses. Control can be passed to the procedure not only by the CALL command but also by the JMP or even RET command. Listing 1.27 shows an example program that demonstrates the four methods of calling procedures. With all that being so, the last three methods of calling procedures might result in serious complications, which under certain conditions prevent the researcher from determining that a certain code section is in fact a procedure called from some other location.

Listing 1.27: A test program illustrating different methods of calling procedures

image from book

 .586P .MODEL FLAT, STDCALL TEXT SEGMENT START: ; Explicit call         CALL PR1         LEA  EAX, PR1 ; Implicit call         CALL EAX         PUSH OFFSET L1 ; Return address in the stack         JMP EAX L1:         PUSH OFFSET L2         PUSH EAX ; Now the stack top contains the procedure address. ; The next stack element contains the return address from the procedure.         RETN           ; The call using the RET command L2:         RETN PR1 PROC         RETN PR1 ENDP TEXT ENDS END START 

image from book

The code of the program illustrating different methods of calling procedures is provided in Listing 1.27.

The most important problem is finding the correct address, from which the required block of commands starts. If the procedure couldn't be identified using cross-references, then you might hope that you'll at least correctly decode the block where procedures are located. Alas, even this goal isn't always guaranteed to be reached — at least programmatically. It isn't clear where the block of procedures starts. Assume, however, that you have located the first procedure, to which there is a direct call. Further assume that you have located its end. Unfortunately, no one can guarantee that another procedure is located directly after it. Any number of NOP instructions can separate two procedures. For instance, MASM32 can insert such operations using the ALIGN directive.

Various issues of recognizing data, procedures, and other program structures will be covered in more detail in Chapter 3.

1.4.7. x87 Floating-Point Unit Commands

You'd probably like to know more about the arithmetic coprocessor. Is there any principal difference between the FPU commands and the normal commands of the Intel Pentium microprocessor? Running a few steps forward, I'll answer that there are no principal differences. However, there are certain specific features. The minimum length of an FPU command is 2 bytes. The first byte of a command, which was always called the operation code for the processor commands, but which isn't called an opcode for FPU commands (see later in this chapter), always has the 5 most significant bits set to 11011B. This means that the most significant nibble of the first byte of an FPU command is always equal to DH. This allows investigators to easily identify a coprocessor command within a sequence of bytes in the main memory.

In addition to the first byte, an FPU command contains the MOD R/M byte and, possibly, an operand pointing to the memory location from which or into which the operand is copied. For example, consider the FLD QWORD PTR [20814000H] command that pushes into the coprocessor stack some long floating-point number from the memory location pointed at by the operand or address. This command is represented by the following sequence of bytes: DD05 20814000. The first byte will appear as follows in the binary format: 11011101B. The 5 most significant bits were already mentioned, while the 3 least significant bits are of great interest to investigators. This command is part of the group of FPU commands intended for manipulations over operands located in the main memory. If the least significant bit of this byte equals one, this means that the command passes the data from the coprocessor stack into the memory or copies the data from the memory. All other commands have this bit set to zero. For example, these might be arithmetic or comparison operations. Bytes 2 and 1 for the commands under consideration determine the type of the memory format (MF). The following four values are possible:

  • 00 — A short floating-point number (32 bits)

  • 01 — A short integer binary number (32 bits)

  • 10 — A long floating-point number (64 bits)

  • 11 — A 10-byte number (80 bits)

In this case, you are dealing with the value 10, in other words, with a long floating-point number. Thus, it becomes possible to state that the first byte of the FPU command code can no longer be considered an operation code.

Now, consider the structure of Mod R/M byte: 05H = 00000101B. Thus, the following are true: MOD = 00B, REG = 000B, and R/M = 101B. Having consulted Table 1.27, you can draw an obvious conclusion — namely, that the address is defined by the direct offset (by the R/M value). Thus, the 3 bits in the middle (called REG) are nothing but the operation code.

Consider the code of another command: FADD ST(1), ST(0) (see Table 1.21). This command adds the operands located in ST(0) and ST(1) and loads the result into the ST(1) register. The command code equals DC C1. In binary representation, this code will appear as follows: 11011100 11000001. Consider the first byte. Bit 0 is set to zero and is the part of the operation code in arithmetic and comparison operations, where coprocessor registers participate. The value of bit 1 defines whether or not the stack is popped after the operation. In this command, the stack is not popped, because the value of bit 1 is set to zero. Bit 2 shows whether the result is returned into the stack top (0) or into some other register (1). In the case being considered, the result is returned into the ST(1) register. Now, proceed with the analysis of the second byte. The MOD field is set to 11B, which means that the operation is executed over the operands stored in registers. The R/M field stores the 001B value, which defines the second register participating in the operation (ST(1)). The first register is always the ST(0) register. Finally, the code of the operation just considered is 0000B.

Now, consider the FSQRT command that computes the square root from the operand located at the top of the stack. For this and similar commands, including (except for transcendental functions) loading of some constants and some arithmetic operations, it is typical to use only one stack register— ST(0). The code of this operation is D9 FA, which in the binary format appears as follows: 11011001 11111010. For such an operation, all bits are constant except for the first 4 bits of the second byte of the operation (1010B), which define the operation being executed.

Finally, there is another type of operation that controls the FPU. These operations do not accept any operands. An example of such an operation is the FINIT operation (see Table 1.23), which initializes the coprocessor at start-up. The code of this operation is DB E3, or 11011011 11100011 in the binary format. For these operations, as in the previous case, only the first 4 bits of the second byte are significant. These bits define, which operation is being executed.

Thus, the section on Intel Pentium microprocessor command formats has been completed.




Disassembling Code. IDA Pro and SoftICE
Disassembling Code: IDA Pro and SoftICE
ISBN: 1931769516
EAN: 2147483647
Year: 2006
Pages: 63
Authors: Vlad Pirogov

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net