C Control Structures

In this section, I cover some classical control structures of the C programming language and their conversion into Assembly language in the course of translation.

Conditional Constructs

The following is an incomplete conditional construct:

 if(simple condition)           {                 .                 .                 .           } 

If the condition is simple, such as i = 1, it is replaced by the following sequence of commands:

 CMP   EAX, 1          JNZ   L1          .          .          .     L1: 

A complete conditional construct looks as follows :

 if(simple condition)     {           .           .           .     } else     {           .           .           .     }           CMP  EAX, 1           JNZ  L1           .           .           .           JMP  L2     L1:           .           .           .     L2: 

Nested Conditional Constructs

Everything is self-evident in the following code:

 CMP  EAX, 1           JNZ  L1           CMP  EBX, 2           JNZ  L1     L1: 

Naturally, this Assembly construct is equivalent to one aggregate condition connected by the AND operator. The OR operator is replaced by a conditional check in the ELSE block.

The Switch Operator

The switch operator is widely used in window functions. Sound knowledge of its Assembly structure will allow you to easily find such constructs in the disassembled code, such as follows:

 switch(i)     {      case 1:           .           .           .      break;      case 3:           .           .           .      break;      case 5:           .           .           .      break;     } 

Here is the Assembly code corresponding to this construct:

 DEC   EAX           JZ    L1           SUB   EAX,  2           JZ    L2           SUB   EAX, 2           JZ    L3           JMP   L4     L1:           .           .           .           JMP   L4     L2:           .           .           .           JMP   L4     L3:           .           .           .     L4: 

As you can see, this is an interesting structure. Such an approach allows better optimization when checking a large number of conditions.

In reality, the switch operator can be encoded in another way. Here is the alternative variant of representing the switch operator:

 CMP  EAX, 10     JE   1     CMP  EAX, 5     JE   2     CMP  EAX, 11     JE   3     ... 

Loops

In this chapter, I have already mentioned loop organization using the C programming language. I'd only like to mention the two widely used structures.

The first structure is as follows:

 L1:          .          .          .          CMP   EAX, 10          JL    L1 

The second structure is as follows:

 L2:          CMP   EAX, 10          JA    L1          .          .          .          JMP   L2     L1: 

The first structure corresponds to the following fragment:

 int i;     ...     do {     ...        }     while  (i < 10); 

Or, in Pascal, it corresponds to the following:

 var i: integer;     ...     repeat     ...     until  (i>=10); 

The second structure corresponds to the following fragment:

 unsigned int i;     ...     while  (i <= 10)  {     ...     } 

Or, in Pascal, it corresponds to the following:

 var i:integer;     ...     while  (i<=10)  do     begin     ...     end; 

The first structure can be slightly modified and can appear as follows:

 JMP   L2     L1:            CMP   EAX, 10     L2:            JL    L1 

Thus, the DO loop converts into the while loop.

I didn't mention the monster of the FOR loop, but, in the C language, it is principally the same as WHILE loop.

Local Variables

Usually, you'll work with local variables. Local variables are stored in predefined segments. Thus, a really good disassembler would easily localize them if the references to them are recognized correctly. With local variables, the task often becomes more complicated. This is especially true for records and arrays. A good disassembler will considerably simplify your problem. For example, consider the fragment in Listing 25.3 disassembled using IDA Pro.

Listing 25.3: Two local arrays in a program disassembled using IDA Pro
image from book
 CODE:00401108 _main proc near  ;  DATA XREF: DATA:0040B044 CODE:00401108 CODE:00401108 var_54     = DWORD PTR -54h CODE:00401108 VAR_28    = BYTE PTR -28H CODE:00401108 ARGC      = DWORD PTR 8 CODE:00401108 ARGV      = DWORD PTR 0CH CODE:00401108 ENVP      = DWORD PTR 10H CODE:00401108 CODE:00401108        PUSH  EBP CODE:00401109        MOV   EBP,  ESP CODE:0040110B        ADD   ESP,  0FFFFFFACH CODE:0040110E        PUSH  EBX CODE:0040110F        XOR   EBX,  EBX 
image from book
 

Carefully look at the fragment from Listing 25.3. As you can see, the debugger has correctly recognized everything. Two variables, var_54 and var_28 , are DWORD arrays. The first array is allocated 28h bytes (e.g., 40 bytes or 10 array elements), and the second variable is allocated 54h-28h=2CH=44 (or 11 array elements). Consequently, 84 bytes are reserved for local variables. And what is the meaning of the ADD ESP, 0FFFFFFACH command? It's hard to deceive a qualified code analyzer! Here, 0-0FFFFFFACH = 54H is equal to 84 in decimal notation. This means that the memory space for storing local variables is allocated in the first place.

As relates to arrays, it should be mentioned that C language initially made provision for two methods of accessing array elements: using indexes and using pointers. In other words, it is possible to write both a[i]=10 and * (a+i)=10 . At the same time, the second approach proved to be more efficient Naturally, now it is possible to use both approaches. However, Borland C++ 5.0 and Microsoft C++ 6.0 produce the same Assembly code for both variants. This is encouraging because it means that compilers are being improved. By the way, comparing the Assembly code produced by different compilers would be instructive. I won't concentrate on this topic here; however, even a superficial comparison of the Borland C++ 5.0 and Microsoft Visual C++ 7.0 compilers has shown that Microsoft's development tools optimize the code more efficiently .

Now, it is necessary to return to the code fragment provided in Listing 25.3. Consider how the beginning of the main function appears in the W32Dasm.

Listing 25.4: The code fragment from Listing 25.3 in W32Dasm
image from book
 :00401108 55            PUSH  EBP :00401109 8BEC          MOV   EBP,  ESP :0040110B 83C4AC        ADD   ESP,  FFFFFFAC :0040110E 53            PUSH  EBX :0040110F 33DB          XOR   EBX,  EBX 
image from book
 

As you can see W32Dasm is less informative. On the basis of the fragment presented in Listing 25.4, it is only possible to conclude that local variables are allocated 84 bytes. The structure of local variables can be clarified by analyzing the code presented later.

When analyzing the code generated by Visual C++ 7.0, you'll find that local variables are not always recognizable. This translator has a powerful code optimizer. In some cases, it simply omits local variables, frequently without using the ESP register. The variable either gets transformed into a constant or is stored in a register. Naturally, this optimizes the resulting code. For example, consider the following fragment of some function:

 ...     int b=10;     ...     c=c+b; 

The b variable is local If it isn't used anywhere except this fragment, then, principally, it is a constant. Therefore, the translator doesn't reserve stack memory for it. Instead of this, it generates a command such as ADD EAX , 10 , assuming that the c variable is stored in the EAX register. In the next section, I provide an example illustrating such a behavior of the Visual C++ translator.

Functions and Procedures

Functions and procedures are easily identified. You already know the structure of calls and the internal parts of procedures. It only remains to recall some basic concepts.

A procedure call appears as follows:

 PUSH   par1     PUSH   par2     PUSH   par3     CALL   232343 

Everything is straightforward here. The main goal is recognizing parameters and the order, in which they are placed into the stack. You should also bear in mind that there is a special protocol for passing data through registers (see Chapter 20). For example, when using the fastcall convention, the first three parameters are loaded into the EAX, ECX , and EDX registers, and other parameters (if there are any) are loaded into the stack in a standard way. The procedure call might be followed by the ADD ESP, N command for clearing the stack.

The internal part of the procedure was considered many times (see Chapters 2 and 20). I hope that you are now capable of recognizing it; therefore, I won't draw your attention to this topic. However, bear in mind that the presence of local variables doesn't automatically mean that they will be allocated a place in the stack and will use the EBP register. For the purposes of optimization, the translator can either replace it with a constant expression or store it in a general-purpose register. For accessing parameters, the ESP register will be used. To explain this statement with a practical example, consider a simple C function:

 int func(int a, int b)     {           int i;           i = a + b + 0 x 1234;           return i;     } 

Translate it using Visual C++, and you'll get a surprising result:

 :00401000 8B442408            MOV EAX, DWORD PTR [ESP+08]     :00401004 8B4C2404            MOV ECX, DWORD PTR [ESP+04]     :00401008 8D840134120000      LEA EAX, DWORD PTR [ECX+EAX+00001234]     :0040100F C3                  RET 

As you can see, the local variable is not reserved, and the EBP register is not used. However, everything is theoretically correct. Why do you need to reserve the memory for storing a variable if you can do without it and use only registers?

Finally, don't forget that functions return the result either through the EAX register or through the EDX:EAX pair (for 64-bit variables). All this information will enable you to quickly understand the goal of a specific function.



The Assembly Programming Master Book
The Assembly Programming Master Book
ISBN: 8170088178
EAN: 2147483647
Year: 2004
Pages: 140
Authors: Vlad Pirogov

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