# X86 Disassembly/Floating Point Numbers

## Floating Point Numbers

This page will talk about how **floating point** numbers are used in
assembly language constructs. This page will not talk about new
constructs, it will not explain what the FPU instructions do, how
floating point numbers are stored or manipulated, or the differences in
floating-point data representations. However, this page will demonstrate
briefly how floating-point numbers are used in code and data structures
that we have already considered.

The x86 architecture does not have any registers specifically for
floating point numbers, but it does have a special stack for them. The
floating point stack is built directly into the processor, and has
access speeds similar to those of ordinary registers. Notice that the
FPU stack is not the same as the regular system stack.

## Calling Conventions

With the addition of the floating-point stack, there is an entirely new
dimension for passing parameters and returning values. We will examine
our calling conventions here, and see how they are affected by the
presence of floating-point numbers. These are the functions that we will
be assembling, using both GCC, and cl.exe:

``` C
 __cdecl double MyFunction1(double x, double y, float z)
 {
    return (x + 1.0) * (y + 2.0) * (z + 3.0);
 }

 __fastcall double MyFunction2(double x, double y, float z)
 {
    return (x + 1.0) * (y + 2.0) * (z + 3.0);
 }

 __stdcall double MyFunction3(double x, double y, float z)
 {
    return (x + 1.0) * (y + 2.0) * (z + 3.0);
 }
```

### CDECL

Here is the cl.exe assembly listing for MyFunction1:

``` asm
 PUBLIC _MyFunction1
 PUBLIC __real@3ff0000000000000
 PUBLIC __real@4000000000000000
 PUBLIC __real@4008000000000000
 EXTRN  __fltused:NEAR
 ;  COMDAT __real@3ff0000000000000
 CONST  SEGMENT
 __real@3ff0000000000000 DQ 03ff0000000000000r  ; 1
 CONST  ENDS
 ;  COMDAT __real@4000000000000000
 CONST  SEGMENT
 __real@4000000000000000 DQ 04000000000000000r  ; 2
 CONST  ENDS
 ;  COMDAT __real@4008000000000000
 CONST  SEGMENT
 __real@4008000000000000 DQ 04008000000000000r  ; 3
 CONST  ENDS
 _TEXT  SEGMENT
 _x$ = 8                            ; size = 8
 _y$ = 16                       ; size = 8
 _z$ = 24                       ; size = 4
 _MyFunction1 PROC NEAR
 ; Line 2
    push    ebp
    mov ebp, esp
 ; Line 3
    fld QWORD PTR _x$[ebp]
    fadd    QWORD PTR __real@3ff0000000000000
    fld QWORD PTR _y$[ebp]
    fadd    QWORD PTR __real@4000000000000000
    fmulp   ST(1), ST(0)
    fld DWORD PTR _z$[ebp]
    fadd    QWORD PTR __real@4008000000000000
    fmulp   ST(1), ST(0)
 ; Line 4
    pop ebp
    ret 0
 _MyFunction1 ENDP
 _TEXT  ENDS
```

Our first question is this: are the parameters passed on the stack, or
on the floating-point register stack, or some place different entirely?
Key to this question, and to this function is a knowledge of what
**fld** and **fstp** do. fld (Floating-point Load) pushes a floating
point value onto the FPU stack, while fstp (Floating-Point Store and
Pop) moves a floating point value from ST0 to the specified location,
and then pops the value from ST0 off the stack entirely. Remember that
**double** values in cl.exe are treated as 8-byte storage locations
(QWORD), while floats are only stored as 4-byte quantities (DWORD). It
is also important to remember that floating point numbers are not stored
in a human-readable form in memory, even if the reader has a solid
knowledge of binary. Remember, these aren\'t integers. Unfortunately,
the exact format of floating point numbers is well beyond the scope of
this chapter.

x is offset +8, y is offset +16, and z is offset +24 from ebp.
Therefore, z is pushed first, x is pushed last, and the parameters are
passed right-to-left on the *regular stack* not the floating point
stack. To understand how a value is returned however, we need to
understand what **fmulp** does. fmulp is the \"Floating-Point Multiply
and Pop\" instruction. It performs the instructions:

`ST1 := ST1 * ST0`\
`FPU POP ST0`

This multiplies ST(1) and ST(0) and stores the result in ST(1). Then,
ST(0) is marked empty and stack pointer is incremented. Thus, contents
of ST(1) are on the top of the stack. So the top 2 values are multiplied
together, and the result is stored on the top of the stack. Therefore,
in our instruction above, \"fmulp ST(1), ST(0)\", which is also the last
instruction of the function, we can see that the last result is stored
in ST0. Therefore, floating point parameters are passed on the regular
stack, but floating point results are passed on the FPU stack.

One final note is that MyFunction2 cleans its own stack, as referenced
by the **ret 20** command at the end of the listing. Because none of the
parameters were passed in registers, this function appears to be exactly
what we would expect an STDCALL function would look like: parameters
passed on the stack from right-to-left, and the function cleans its own
stack. We will see below that this is actually a correct assumption.

For comparison, here is the GCC listing:

``` asm
 LC1:
    .long   0
    .long   1073741824
    .align 8
 LC2:
    .long   0
    .long   1074266112
 .globl _MyFunction1
    .def    _MyFunction1;   .scl    2;  .type   32; .endef
 _MyFunction1:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    fldl    8(%ebp)
    fstpl   -8(%ebp)
    fldl    16(%ebp)
    fstpl   -16(%ebp)
    fldl    -8(%ebp)
    fld1
    faddp   %st, %st(1)
    fldl    -16(%ebp)
    fldl    LC1
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    flds    24(%ebp)
    fldl    LC2
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    leave
    ret
    .align 8
```

This is a very difficult listing, so we will step through it (albeit
quickly). 16 bytes of extra space is allocated on the stack. Then, using
a combination of fldl and fstpl instructions, the first 2 parameters are
moved from offsets +8 and +16, to offsets -8 and -16 from ebp. Seems
like a waste of time, but remember, optimizations are off. **fld1**
loads the floating point value 1.0 onto the FPU stack. **faddp** then
adds the top of the stack (1.0), to the value in ST1 (\[ebp - 8\],
originally \[ebp + 8\]).

### FASTCALL

Here is the cl.exe listing for MyFunction2:

``` asm
 PUBLIC @MyFunction2@20
 PUBLIC __real@3ff0000000000000
 PUBLIC __real@4000000000000000
 PUBLIC __real@4008000000000000
 EXTRN  __fltused:NEAR
 ;  COMDAT __real@3ff0000000000000
 CONST  SEGMENT
 __real@3ff0000000000000 DQ 03ff0000000000000r  ; 1
 CONST  ENDS
 ;  COMDAT __real@4000000000000000
 CONST  SEGMENT
 __real@4000000000000000 DQ 04000000000000000r  ; 2
 CONST  ENDS
 ;  COMDAT __real@4008000000000000
 CONST  SEGMENT
 __real@4008000000000000 DQ 04008000000000000r  ; 3
 CONST  ENDS
 _TEXT  SEGMENT
 _x$ = 8                            ; size = 8
 _y$ = 16                       ; size = 8
 _z$ = 24                       ; size = 4
 @MyFunction2@20 PROC NEAR
 ; Line 7
    push    ebp
    mov ebp, esp
 ; Line 8
    fld QWORD PTR _x$[ebp]
    fadd    QWORD PTR __real@3ff0000000000000
    fld QWORD PTR _y$[ebp]
    fadd    QWORD PTR __real@4000000000000000
    fmulp   ST(1), ST(0)
    fld DWORD PTR _z$[ebp]
    fadd    QWORD PTR __real@4008000000000000
    fmulp   ST(1), ST(0)
 ; Line 9
    pop ebp
    ret 20                  ; 00000014H
 @MyFunction2@20 ENDP
 _TEXT  ENDS
```

We can see that this function is taking 20 bytes worth of parameters,
because of the \@20 decoration at the end of the function name. This
makes sense, because the function is taking two **double** parameters (8
bytes each), and one **float** parameter (4 bytes each). This is a grand
total of 20 bytes. We can notice at a first glance, without having to
actually analyze or understand any of the code, that there is only one
register being accessed here: **ebp**. This seems strange, considering
that FASTCALL passes its regular 32-bit arguments in registers. However,
that is not the case here: all the floating-point parameters (even z,
which is a 32-bit float) are passed on the stack. We know this, because
by looking at the code, there is no other place where the parameters
could be coming from.

Notice also that **fmulp** is the last instruction performed again, as
it was in the CDECL example. We can infer then, without investigating
too deeply, that the result is passed at the top of the floating-point
stack.

Notice also that x (offset \[ebp + 8\]), y (offset \[ebp + 16\]) and z
(offset \[ebp + 24\]) are pushed in reverse order: z is first, x is
last. This means that floating point parameters are passed in
right-to-left order, on the stack. This is exactly the same as CDECL
code, although only because we are using floating-point values.

Here is the GCC assembly listing for MyFunction2:

``` asm
    .align 8
 LC5:
    .long   0
    .long   1073741824
    .align 8
 LC6:
    .long   0
    .long   1074266112
 .globl @MyFunction2@20
    .def    @MyFunction2@20;    .scl    2;  .type   32; .endef
 @MyFunction2@20:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    fldl    8(%ebp)
    fstpl   -8(%ebp)
    fldl    16(%ebp)
    fstpl   -16(%ebp)
    fldl    -8(%ebp)
    fld1
    faddp   %st, %st(1)
    fldl    -16(%ebp)
    fldl    LC5
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    flds    24(%ebp)
    fldl    LC6
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    leave
    ret $20
```

This is a tricky piece of code, but luckily we don\'t need to read it
very close to find what we are looking for. First off, notice that no
other registers are accessed besides **ebp**. Again, GCC passes all
floating point values (even the 32-bit float, z) on the stack. Also, the
floating point result value is passed on the top of the floating point
stack.

We can see again that GCC is doing something strange at the beginning,
taking the values on the stack from \[ebp + 8\] and \[ebp + 16\], and
moving them to locations \[ebp - 8\] and \[ebp - 16\], respectively.
Immediately after being moved, these values are loaded onto the floating
point stack and arithmetic is performed. z isn\'t loaded till later, and
isn\'t ever moved to \[ebp - 24\], despite the pattern.

LC5 and LC6 are constant values, that most likely represent floating
point values (because the numbers themselves, 1073741824 and 1074266112
don\'t make any sense in the context of our example functions. Notice
though that both LC5 and LC6 contain two **.long** data items, for a
total of 8 bytes of storage? They are therefore most definitely
**double** values.

### STDCALL

Here is the cl.exe listing for MyFunction3:

``` asm
 PUBLIC _MyFunction3@20
 PUBLIC __real@3ff0000000000000
 PUBLIC __real@4000000000000000
 PUBLIC __real@4008000000000000
 EXTRN  __fltused:NEAR
 ;  COMDAT __real@3ff0000000000000
 CONST  SEGMENT
 __real@3ff0000000000000 DQ 03ff0000000000000r  ; 1
 CONST  ENDS
 ;  COMDAT __real@4000000000000000
 CONST  SEGMENT
 __real@4000000000000000 DQ 04000000000000000r  ; 2
 CONST  ENDS
 ;  COMDAT __real@4008000000000000
 CONST  SEGMENT
 __real@4008000000000000 DQ 04008000000000000r  ; 3
 CONST  ENDS
 _TEXT  SEGMENT
 _x$ = 8                        ; size = 8
 _y$ = 16                       ; size = 8
 _z$ = 24                       ; size = 4
 _MyFunction3@20 PROC NEAR
 ; Line 12
    push    ebp
    mov ebp, esp
 ; Line 13
    fld QWORD PTR _x$[ebp]
    fadd    QWORD PTR __real@3ff0000000000000
    fld QWORD PTR _y$[ebp]
    fadd    QWORD PTR __real@4000000000000000
    fmulp   ST(1), ST(0)
    fld DWORD PTR _z$[ebp]
    fadd    QWORD PTR __real@4008000000000000
    fmulp   ST(1), ST(0)
 ; Line 14
    pop ebp
    ret 20                  ; 00000014H
 _MyFunction3@20 ENDP
 _TEXT  ENDS
 END
```

x is the highest on the stack, and z is the lowest, therefore these
parameters are passed from right-to-left. We can tell this because x has
the smallest offset (offset \[ebp + 8\]), while z has the largest offset
(offset \[ebp + 24\]). We see also from the final fmulp instruction that
the return value is passed on the FPU stack. This function also cleans
the stack itself, as noticed by the call *\'ret 20*. It is cleaning
exactly 20 bytes off the stack which is, incidentally, the total amount
that we passed to begin with. We can also notice that the implementation
of this function looks exactly like the FASTCALL version of this
function. This is true because FASTCALL only passes DWORD-sized
parameters in registers, and floating point numbers do not qualify. This
means that our assumption above was correct.

Here is the GCC listing for MyFunction3:

``` asm
    .align 8
 LC9:
    .long   0
    .long   1073741824
    .align 8
 LC10:
    .long   0
    .long   1074266112
 .globl @MyFunction3@20
    .def    @MyFunction3@20;    .scl    2;  .type   32; .endef
 @MyFunction3@20:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    fldl    8(%ebp)
    fstpl   -8(%ebp)
    fldl    16(%ebp)
    fstpl   -16(%ebp)
    fldl    -8(%ebp)
    fld1
    faddp   %st, %st(1)
    fldl    -16(%ebp)
    fldl    LC9
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    flds    24(%ebp)
    fldl    LC10
    faddp   %st, %st(1)
    fmulp   %st, %st(1)
    leave
    ret $20
```

Here we can also see, after all the opening nonsense, that \[ebp - 8\]
(originally \[ebp + 8\]) is value x, and that \[ebp - 24\] (originally
\[ebp - 24\]) is value z. These parameters are therefore passed
right-to-left. Also, we can deduce from the final fmulp instruction that
the result is passed in ST0. Again, the STDCALL function cleans its own
stack, as we would expect.

### Conclusions

Floating point values are passed as parameters on the stack, and are
passed on the FPU stack as results. Floating point values do not get put
into the general-purpose integer registers (eax, ebx, etc\...), so
FASTCALL functions that only have floating point parameters collapse
into STDCALL functions instead. **double** values are 8-bytes wide, and
therefore will take up 8-bytes on the stack. **float** values however,
are only 4-bytes wide.

## Float to Int Conversions

## FPU Compares and Jumps


# X86 Disassembly/Code Optimization

## Code Optimization

An **optimizing compiler** is perhaps one of the most complicated, most
powerful, and most interesting programs in existence. This chapter will
talk about optimizations, although this chapter will not include a table
of common optimizations.

## Stages of Optimizations

There are two times when a compiler can perform optimizations: first, in
the intermediate representation, and second, during the code generation.

### Intermediate Representation Optimizations

While in the intermediate representation, a compiler can perform various
optimizations, often based on dataflow analysis techniques. For example,
consider the following code fragment:

``` C
 x = 5;
 if(x != 5)
 {
   //loop body
 }
```

An optimizing compiler might notice that at the point of \"if (x !=
5)\", the value of x is always the constant \"5\". This allows
substituting \"5\" for x resulting in \"5 != 5\". Then the compiler
notices that the resulting expression operates entirely on constants, so
the value can be calculated now instead of at run time, resulting in
optimizing the conditional to \"if (false)\". Finally the compiler sees
that this means the body of the if conditional will never be executed,
so it can omit the entire body of the if conditional altogether.

Consider the reverse case:

``` C
 x = 5;
 if(x == 5)
 {
    //loop body
 }
```

In this case, the optimizing compiler would notice that the IF
conditional will always be true, and it won\'t even bother writing code
to test x.

### Control Flow Optimizations

Another set of optimization which can be performed either at the
intermediate or at the code generation level are control flow
optimizations. Most of these optimizations deal with the elimination of
useless branches. Consider the following code:

``` C
 if(A)
 {
    if(B)
    {
       C;
    }
    else
    {
       D;
    }
    end_B:
 }
 else
 {
    E;
 }
 end_A:
```

In this code, a simplistic compiler would generate a jump from the C
block to end_B, and then another jump from end_B to end_A (to get around
the E statements). Clearly jumping to a jump is inefficient, so
optimizing compilers will generate a direct jump from block C to end_A.

This unfortunately will make the code more confused and will prevent a
nice recovery of the original code. For complex functions, it\'s
possible that one will have to consider the code made of only if()-goto;
sequences, without being able to identify higher level statements like
if-else or loops.

The process of identifying high level statement hierarchies is called
\"code structuring\".

### Code Generation Optimizations

Once the compiler has sifted through all the logical inefficiencies in
your code, the code generator takes over. Often the code generator will
replace certain slow machine instructions with faster machine
instructions.

For instance, the instruction:

``` asm
 beginning:
 ...
 loopnz beginning
```

operates *much* slower than the equivalent instruction set:

``` asm
 beginning:
 ...
 dec ecx
 jne beginning
```

So then why would a compiler ever use a loopxx instruction? The answer
is that most optimizing compilers never use a loopxx instruction, and
therefore as a reverser, you will probably never see one used in real
code.

What about the instruction:

``` asm

 mov eax, 0
```

The mov instruction is relatively quick, but a faster part of the
processor is the arithmetic unit. Therefore, it makes more sense to use
the following instruction:

``` asm
 xor eax, eax
```

because xor operates in very few processor cycles (and saves three bytes
at the same time), and is therefore faster than a \"mov eax, 0\". The
only drawback of a xor instruction is that it changes the processor
flags, so it cannot be used between a comparison instruction and the
corresponding conditional jump.

## Loop Unwinding

When a loop needs to run for a small, but definite number of iterations,
it is often better to **unwind the loop** in order to reduce the number
of jump instructions performed, and in many cases prevent the
processor\'s branch predictor from failing. Consider the following C
loop, which calls the function `MyFunction()` 5 times:

``` C
for(x = 0; x < 5; x++)
{
  MyFunction();
}
```

Converting to assembly, we see that this becomes, roughly:

``` asm
mov eax, 0
loop_top:
cmp eax, 5
jge loop_end
call _MyFunction
inc eax
jmp loop_top
loop_end:
```

Each loop iteration requires the following operations to be performed:

1.  Compare the value in eax (the variable \"x\") to 5, and jump to the
    end if greater then or equal
2.  Increment eax
3.  Jump back to the top of the loop.

Notice that we remove all these instructions if we manually repeat our
call to `MyFunction()`:

``` asm
call _MyFunction
call _MyFunction
call _MyFunction
call _MyFunction
call _MyFunction
```

This new version not only takes up less disk space because it uses fewer
instructions, but also runs faster because fewer instructions are
executed. This process is called **Loop Unwinding**.

## Inline Functions

The C and C++ languages allow the definition of an `inline` type of
function. Inline functions are functions which are treated similarly to
macros. During compilation, calls to an inline function are replaced
with the body of that function, instead of performing a `call`
instruction. In addition to using the `inline` keyword to declare an
inline function, optimizing compilers may decide to make other functions
inline as well.

Function inlining works similarly to loop unwinding for increasing code
performance. A non-inline function requires a call instruction, several
instructions to create a stack frame, and then several more instructions
to destroy the stack frame and return from the function. By copying the
body of the function instead of making a call, the size of the machine
code increases, but the execution time *decreases*.

It is not necessarily possible to determine whether identical portions
of code were created originally as macros, inline functions, or were
simply copy and pasted. However, when disassembling it can make your
work easier to separate these blocks out into separate inline functions,
to help keep the code straight.


# X86 Disassembly/Code Obfuscation

## Code Obfuscation

**Code Obfuscation** is the act of making the assembly code or machine
code of a program more difficult to disassemble or decompile. The term
\"obfuscation\" is typically used to suggest a deliberate attempt to add
difficulty, but many other practices will cause code to be obfuscated
without that being the intention. Software vendors may attempt to
obfuscate or even encrypt code to prevent reverse engineering efforts.
There are many different types of obfuscations. Notice that many code
optimizations (discussed in the previous chapter) have the side-effect
of making code more difficult to read, and therefore optimizations act
as obfuscations.

## What is Code Obfuscation?

There are many things that obfuscation could be:

-   Encrypted code that is decrypted prior to runtime.
-   Compressed code that is decompressed prior to runtime.
-   Executables that contain Encrypted sections, and a simple decrypter.
-   Code instructions that are put in a hard-to read order.
-   Code instructions which are used in a non-obvious way.

This chapter will try to examine some common methods of obfuscating
code, but will not necessarily delve into methods to break the
obfuscation.

## Interleaving

Optimizing Compilers will engage in a process called **interleaving** to
try and maximize parallelism in pipelined processors. This technique is
based on two premises:

1.  That certain instructions can be executed out of order and still
    maintain the correct output
2.  That processors can perform certain pairs of tasks simultaneously.

### x86 NetBurst Architecture

The Intel **NetBurst Architecture** divides an x86 processor into 2
distinct parts: the supporting hardware, and the primitive core
processor. The primitive core of a processor contains the ability to
perform some calculations blindingly fast, but not the instructions that
you or I am familiar with. The processor first converts the code
instructions into a form called \"micro-ops\" that are then handled by
the primitive core processor.

The processor can also be broken down into 4 components, or modules,
each of which is capable of performing certain tasks. Since each module
can operate separately, up to 4 separate tasks can be handled
*simultaneously* by the processor core, so long as those tasks can be
performed by each of the 4 modules:

Port0 : Double-speed integer arithmetic, floating point load, memory store

```{=html}
<!-- -->
```

Port1 : Double-speed integer arithmetic, floating point arithmetic

```{=html}
<!-- -->
```

Port2 : memory read

```{=html}
<!-- -->
```

Port3 : memory write (writes to address bus)

So for instance, the processor can simultaneously perform 2 integer
arithmetic instructions in both Port0 and Port1, so a compiler will
frequently go to great lengths to put arithmetic instructions close to
each other. If the timing is just right, up to 4 arithmetic instructions
can be executed in a single instruction period.

Notice however that writing to memory is particularly slow (requiring
the address to be sent by Port3, and the data itself to be written by
Port0). Floating point numbers need to be loaded to the FPU before they
can be operated on, so a floating point load and a floating point
arithmetic instruction cannot operate on a single value in a single
instruction cycle. Therefore, it is not uncommon to see floating point
values loaded, integer values be manipulated, and then the floating
point value be operated on.

## Non-Intuitive Instructions

Optimizing compilers frequently will use instructions that are not
intuitive. Some instructions can perform tasks for which they were not
designed, typically as a helpful side effect. Sometimes, one instruction
can perform a task more quickly than other specialized instructions can.

The only way to know that one instruction is faster than another is to
consult the processor documentation. However, knowing some of the most
common substitutions is very useful to the reverser.

Here are some examples. The code in the first box operates more quickly
than the one in the second, but performs exactly the same tasks.

**Example 1**

*Fast*

``` asm
xor eax, eax
```

*Slow*

``` asm
mov eax, 0
```

**Example 2**

*Fast*

``` asm
shl eax, 3
```

*Slow*

``` asm
push edx
push 8
mul dword [esp]
add esp, 4
pop edx ;# edx is not preserved by "mul"
```

Sometimes such transformations could be made to make the analysis more
difficult:

**Example 3**

*Fast*

``` asm
push $next_instr
jmp $some_function
$next_instr:...
```

*Slow*

``` asm
call $some_function
```

**Example 4**

*Fast*

``` asm
pop eax
jmp eax
```

*Slow*

``` asm
retn
```

### Common Instruction Substitutions

lea : The lea instruction has the following form:

``` asm
 lea dest, (XS:)[reg1 + reg2 * x]
```

Where XS is a segment register (SS, DS, CS, etc\...), reg1 is the base
address, reg2 is a variable offset, and x is a multiplicative scaling
factor. What lea does, essentially, is load the memory address being
pointed to in the second argument, into the first argument. Look at the
following example:

``` asm
 mov eax, 1
 lea ecx, [eax + 4]
```

Now, what is the value of ecx? The answer is that ecx has the value of
(eax + 4), which is 5. In essence, lea is used to do addition and
multiplication of a register and a constant that is a byte or less (-128
to +127).

Now, consider:

``` asm
 mov eax, 1
 lea ecx, [eax+eax*2]
```

Now, ecx equals 3.

The difference is that lea is quick (because it only adds a register and
a small constant), whereas the **add** and **mul** instructions are more
versatile, but slower. lea is used for arithmetic in this fashion very
frequently, even when compilers are not actively optimizing the code.

xor : The xor instruction performs the bit-wise exclusive-or operation on two operands. Consider then, the following example:

``` asm
 mov al, 0xAA
 xor al, al
```

What does this do? Let\'s take a look at the binary:

`    10101010 ;10101010 = 0xAA`\
`xor 10101010`\
`    --------`\
`    00000000`

The answer is that \"xor reg, reg\" sets the register to 0. More
importantly, however, is that \"xor eax, eax\" sets eax to 0 *faster*
(and the generated code instruction is smaller) than an equivalent \"mov
eax, 0\".

mov edi, edi : On a 64-bit x86 system, this instruction clears the high 32-bits of the rdi register.

```{=html}
<!-- -->
```

shl, shr : left-shifting, in binary arithmetic, is equivalent to multiplying the operand by 2. Right-shifting is also equivalent to integer division by 2, although the lowest bit is dropped. in general, left-shifting by $N$ spaces multiplies the operand by $2^N$, and right shifting by $N$ spaces is the same as dividing by $2^N$. One important fact is that resulting number is an integer with no fractional part present. For example:

``` asm
 mov al, 31 ; 00011111
 shr al, 1  ; 00001111 = 15, not 15.5
```

xchg : xchg exchanges the contents of two registers, or a register and a memory address. A noteworthy point is the fact that xchg operates faster than a move instruction. For this reason, xchg will be used to move a value from a source to a destination, when the value in the source no longer needs to be saved.

As an example, consider this code:

``` asm
mov ebx, eax
mov eax, 0
```

Here, the value in `eax` is stored in `ebx`, and then `eax` is loaded
with the value zero. We can perform the same operation, but using `xchg`
and `xor` instead:

``` asm
xchg eax, ebx
xor eax, eax
```

It may surprise you to learn that the second code example operates
significantly faster than the first one does.

## Obfuscators

There are a number of tools on the market that will automate the process
of code obfuscation. These products will use a number of transformations
to turn a code snippet into a less-readable form, although it will not
affect the program flow itself (although the transformations may
increase code size or execution time).

## Code Transformations

Code transformations are a way of reordering code so that it performs
exactly the same task but becomes more difficult to trace and
disassemble. We can best demonstrate this technique by example. Let\'s
say that we have 2 functions, FunctionA and FunctionB. Both of these two
functions are comprised of 3 separate parts, which are performed in
order. We can break this down as such:

``` C
 FunctionA()
 {
   FuncAPart1();
   FuncAPart2();
   FuncAPart3();
 }

 FunctionB()
 {
   FuncBPart1();
   FuncBPart2();
   FuncBPart3();
 }
```

And we have our main program, that executes the two functions:

``` C
 main()
 {
   FunctionA();
   FunctionB();
 }
```

Now, we can rearrange these snippets to a form that is much more
complicated (in assembly):

``` asm
 main:
       jmp FAP1
 FBP3: call FuncBPart3
       jmp end
 FBP1: call FuncBPart1
       jmp FBP2
 FAP2: call FuncAPart2
       jmp FAP3
 FBP2: call FuncBPart2
       jmp FBP3
 FAP1: call FuncAPart1
       jmp FAP2
 FAP3: call FuncAPart3
       jmp FBP1
 end:
```

As you can see, this is much harder to read, although it perfectly
preserves the program flow of the original code. This code is much
harder for a human to read, although it isn\'t hard at all for an
automatic disassembler (such as IDA Pro) to read.

## Opaque Predicates

An **Opaque Predicate** is a predicate inside the code, that cannot be
evaluated during static analysis. This forces the attacker to perform a
dynamic analysis to understand the result of the line. Typically this is
related to a branch instruction that is used to prevent in static
analysis the understanding which code path is taken.

## Code Encryption

Code can be encrypted, just like any other type of data, except that
code can also work to encrypt and decrypt *itself.* Encrypted programs
cannot be directly disassembled. However, such a program can also not be
run directly because the encrypted opcodes cannot be interpreted
properly by the CPU. For this reason, an encrypted program must contain
some sort of method for decrypting itself prior to operation.

The most basic method is to include a small stub program that decrypts
the remainder of the executable, and then passes control to the
decrypted routines.

### Disassembling Encrypted Code

To disassemble an encrypted executable, you must first determine how the
code is being decrypted. Code can be decrypted in one of two primary
ways:

1.  All at once. The entire code portion is decrypted in a single pass,
    and left decrypted during execution. Using a debugger, allow the
    decryption routine to run completely, and then dump the decrypted
    code into a file for further analysis.
2.  By Block. The code is encrypted in separate blocks, where each block
    may have a separate encryption key. Blocks may be decrypted before
    use, and re-encrypted again after use. Using a debugger, you can
    attempt to capture all the decryption keys and then use those keys
    to decrypt the entire program at once later, or you can wait for the
    blocks to be decrypted, and then dump the blocks individually to a
    separate file for analysis.


# X86 Disassembly/Debugger Detectors

## Detecting Debuggers

It may come as a surprise that a running program can actually detect the
presence of an attached user-mode debugger. Also, there are methods
available to detect kernel-mode debuggers, although the methods used
depend in large part on which debugger is trying to be detected.

This subject is peripheral to the narrative of this book, and the
section should be considered an optional one for most readers.

## IsDebuggerPresent API

The Win32 API contains a function called \"IsDebuggerPresent\", which
will return a boolean true if the program is being debugged. The
following code snippet will detail a general usage of this function:

``` C
 if(IsDebuggerPresent())
 {
   TerminateProcess(GetCurrentProcess(), 1);
 }
```

Of course, it is easy to spot uses of the IsDebuggerPresent() function
in the disassembled code, and a skilled reverser will simply patch the
code to remove this line. For OllyDbg, there are many plugins available
which hide the debugger from this and many other APIs.

## PEB Debugger Check

The Process Environment Block stores the value that IsDebuggerPresent
queries to determine its return value. To avoid suspicion, some
programmers access the value directly from the PEB instead of calling
the API function. The following code snippet shows how to access the
value:

``` asm
mov eax, [fs:0x30]
mov al, [eax+2]
test al, al
jne @DebuggerDetected
```

## Kernel Mode Debugger Check

On Windows 32 and 64-bit Win \<XP?, 7,8.1 and 10.

There is a structure called \_KUSER_SHARED_DATA at offset 0x2D4 it
contains the field named \'KdDebuggerEnabled\' which is set to 0x03 if a
KDM is active or 0x00 if not.

Base address of the structure is static (0x7FFE0000) across different
Windows versions even \< XP.

The field is updated constantly with the last 2 bits set to \'11\' by
the kernel.

The following assembly instruction will work in both 32 and 64-bit
applications:

``` asm
cmp byte ptr ds:[7FFE02D4], 3
je @DebuggerDetected
```

This has quite a few advantages. Known Source of
information.

## Timeouts

Debuggers can put break points in the code, and can therefore stop
program execution. A program can detect this, by monitoring the system
clock. If too much time has elapsed between instructions, it can be
determined that the program is being stopped and analyzed (although this
is not always the case). If a program is taking too much time, the
program can terminate.

Notice that on preemptive multithreading systems, such as modern Windows
or Linux systems will switch away from your program to run other
programs. This is called thread switching. If the system has many
threads to run, or if some threads are hogging processor time, your
program may detect a long delay and may falsely determine that the
program is being debugged.

## Detecting SoftICE

SoftICE is a local kernel debugger, and as such,
it can\'t be detected as easily as a user-mode debugger can be. The
IsDebuggerPresent API function will not detect the presence of SoftICE.

To detect SoftICE, there are a number of techniques that can be used:

1.  Search for the SoftICE install directory. If SoftICE is installed,
    the user is probably a hacker or a reverser.
2.  Detect the presence of **int 1**. SoftICE uses interrupt 1 to debug,
    so if interrupt 1 is installed, SoftICE is running.

## Detecting OllyDbg

OllyDbg is a popular 32-bit usermode debugger. Unfortunately, the last
few releases, including the latest version (v1.10) contain a
vulnerability in the handling of the Win32 API function
OutputDebugString(). 1 A
programmer trying to prevent his program from being debugged by OllyDbg
could exploit this vulnerability in order to make the debugger crash.
The author has never released a fix, however there are unofficial
versions and plugins available to protect OllyDbg from being exploited
using this vulnerability.


# Foundations of Computer Science/Introduction

Have you ever wondered what computing is and how a computer works? What
exactly is computer science? Why---beyond the obvious reasons---is it
important? What do computer scientists do? What types of problems do
they work on? What approaches do they use to solve those problems? How,
in general, do computer scientists think?

**Question 1:** What do you think of when you hear \"computer science?\"
Write a paragraph or list, or draw an image or diagram of what comes to
mind.

**Question 2:** What are the parts of computer science that are most
interesting or important to you currently? Why?

When you hear the term \"computer science\" perhaps you think of a
specific computer. Or someone you know who works with computers. Or a
particular computer use, say online games or social networks. There are
many, many different aspects of computing and computer science.

There are a number of reasons why it is useful and important to know
something about this computer science. Computers affect many, many
aspects of our lives in different ways. For many people, computers are
playing or will play a significant role in the work they do, in their
recreational pursuits, in how they communicate with others, in their
education, in their health care, etc. Think about the many different
ways you encounter computers and computing, either directly or
indirectly, in your daily life.

What, more specifically, will this book cover? The foremost purpose of
this text is to give you a greater understanding of the fundamentals of
computer science: What is computer science, anyway? Is the same as
computer programming? What is a computer? For example, most people would
agree that a \"laptop computer\" is a computer, as is a \"tablet
computer\", but what about a smartphone? And how do computers work? For
example, we can store not only numbers and text in computers, but also
images, video files, and audio files; how do computers handle such
disparate data? And what are some interesting and important subareas of
computer science? For example, what is important to know about subareas
such as computer graphics, networking, or databases? And why is any of
this important? Isn\'t it sufficient for most people just to use
computers, rather than have a deeper understanding of computers and
computer science?

These are all fundamental questions about computing, and in this book
we\'ll look at them and other questions. In summary, one purpose of this
book is to provide an overview of computer science that not only exposes
you to computer science fundamentals---such as how a computer works on a
rudimentary level---but also explores why these fundamentals are
important.

There are two parts of this overview that are particularly important:
while the main theme is an overview of computer science, two essential
subthemes are how mathematics is used in computer science and how
computer science affects, and is affected by, society.

Both subthemes fit well in an overview of computer science book.
Computer science relies heavily on mathematics (in fact, some colleges
have computer science and mathematics programs in a joint department).
Certain uses of mathematics in computer science are obvious---for
example, in computational tools such as spreadsheets---but there are
also many less obvious ways that mathematics is essential to computer
science. For example at the lowest level in a computer, data (whether
that data is numeric, text, audio, video, etc.) is all represented in
binary, i.e., as strings of 0\'s and 1\'s. This means that to understand
something very basic about computers you need to understand binary
numbers and operations.

Computers also affect society in many ways, from the use of
computer-generated imagery in films, to large government or commercial
databases, to the multiple societal effects of the Internet. And society
affects computers, for example through user behavior and through
different types of regulation.

While mathematics and technology and society might seem too different to
be included comfortably in the same book, there are actually many
computer science topics that are useful to explore from both
perspectives---in a sense, these different viewpoints are \"two sides of
the same coin.\" For example, one topic in the book is computer
security. Mathematics plays a role in security, for example in
encryption. And computer security also has many societal aspects, for
example national security, infrastructure security, and individual
security. Most of the topics in this text similarly have both
mathematical underpinnings and societal aspects, and exploring these
topics from both perspectives will result in a richer understanding.

## What this book isn\'t

There are a number of different types of introductory computer science
books. So, in addition to explaining what this text is, it is also
useful to state what it is not.

*This is not a programming book.* Programming is a central activity in
computer science, but it is not the whole of computer science. Because
programming is important, we\'ll spend some time on it. However, because
computer science is much more than programming, and because this is an
overview book, that time will be only a small part of this work.

*This is not a computer applications book.* Many other books cover basic
computer applications. For example, a popular choice is teaching how to
use a word processor, a spreadsheet, a database management program, and
presentation software. These and other applications are important parts
of computer science, and so in this book you will get a chance to learn
about some applications that might be new to you. However---like
programming--- using applications is only part of learning about
computer science, and so application use will be only a small part of
this book.

\'\'This is not a \"computer literacy\" or \"computer fluency\" book.
There are a variety of definitions of computer literacy or computer
fluency. For example, the Wikipedia definition, derived from a report
from the U.S. Congress of Technology Assessment, is \"the knowledge and
ability to use computers and related technology efficiently, with a
range of skills covering levels from elementary use to programming and
advanced problem solving.\"[^1] Parts of this book will involve using
computers to gain a variety of skills. For example, you will do a
variety of computer-related tasks such as performing web searches,
constructing web pages, doing elementary computer programming, and
working with databases. However, this is just one part, rather than the
totality, of the text. So this book shares some characteristics of a
computer literacy book, but overall it has a wider focus than that type
of a textbook.

*This is not a \"great ideas in computer science\" book.* One current
trend in computer science introductory materials is to study computer
science through its important, fundamental ideas.[^2] And this book does
cover some key ideas. For example, an early topic we\'ll study is how
all data in computers, whether those data are numeric, text, video, or
others, are represented within the computer as 0\'s and 1\'s. In
general, the topics in the book are fundamental to computer science.
However, this text also differs from a great ideas book. It is not
focused solely on ideas, but explores broadly a number of
computer-related issues, subtopics, and computer skills. Moreover, this
book focuses more on mathematical thinking, and on technology and
society, than a typical great ideas book would.

In addition to programming, applications, computer fluency, and great
ideas, there are a number of other types of introductory computer
science textbooks. Some survey a variety of computer science topics.
Others focus on professional software development practices. Still
others look at look at computing through a particular \"lens\" such as
networks or computational biology. And so on. This book has some common
characteristics with these other courses, but also has significant
differences. In particular, the biggest difference is this book blends
an overview of computer science with a strong emphasis on mathematics,
and on society and technology; this is a balance of emphases that has a
number of advantages, but is not usually seen in introductory computer
science courses.

## What is this book about?

Both mathematical thinking and technology and society are significant
parts of this book. Many textbooks present an introduction to computer
science though programming, or through how computers work, or through
some other aspect of computing. However, there is not a suitable text
that combines an overview of computer science with both sufficient
mathematical and sufficient society and technology emphases.

At first glance, it might seem odd that a book introducing computer
science would deal with liberal education. What does computer science
have to do with liberal education? Understanding computers well involves
exploring them from a variety of different viewpoints. This includes
understanding not only how computers work---including, for example, the
mathematical underpinnings of computer science---but also how they
affect, and are affected by, society. In summary, to have a good
understanding of computers and computer science it is important to
explore them from a variety of perspectives, including the perspectives
embodied in liberal education.

### Mathematical thinking

**Question 3.** What do you think of when you hear the word
\"mathematics?\" Write a paragraph or list, or draw an image or diagram
of what comes to mind.

**Question 4.** Based on your experience with computers, write a list of
some places where mathematics is used in computing.

What do computers and mathematics have in common? Why is it appropriate
for an overview of computer science book to require mathematical
thinking?

Much of the use of mathematics in this book is applying mathematical
ideas and operations to solve computer science problems. There are a
number of important mathematical underpinnings of computer science, and
so understanding computer science involves being able to solve
mathematical problems involving these underpinnings. At the same time,
the different uses of mathematics in this text exemplify characteristics
of mathematics as a whole, and of the close tie between the fields of
mathematics and computer science. For instance, the mathematics in the
book illustrates the following:

-   The reliance of many key ideas in computer science, such as data
    representation, on mathematics.
-   The use of special mathematics- or logic-related notation and
    terminology in many parts of computer science.
-   The ability to represent and work with many different types of data
    in the computer, and the related ability to represent and work with
    quantities in different representations using a variety of
    operations.
-   The need for rigor in solving problems, analyzing situations, or
    specifying computational processes.
-   The use of numbers and arithmetic in solving computational problems.
    However, rather than being simple arithmetic problems, these
    problems often have some special characteristics such as involving
    repeated operations, or involving extremely large or extremely small
    numbers.
-   The existence of a variety of different algorithms for solving such
    diverse problems as pattern matching, counting specified values in a
    table of data, or finding the shortest path between two nodes in a
    graph.

Solving many of the problems in this book will involve doing some
mathematics, and therefore manipulating mathematical or logical symbols.
Here are a few examples:

-   In exploring low-level logical operations you\'ll need to manipulate
    binary representation and logical operators.
-   In studying the growth rate of algorithms you\'ll need to work with
    the *Ο* and *Θ* notations commonly used by computer scientists.
-   In specifying computational processes you\'ll need to use
    \"pseudocode\" or a programming language. These share many
    notational characteristics with mathematical or logical symbols,
    especially when the computational processing involves a large number
    of numeric computations.

The level of mathematics in this book is introductory-level college
mathematics. As such, the mathematics is not advanced, and there is no
mathematical prerequisite for this book beyond the requirements needed
for general college admission. At the same time, the mathematics in this
book goes beyond high school mathematics even though many of the types
of mathematics used in this text appear in some high school mathematics
courses.

As an example, one appearance of mathematics in this book is binary (or
base 2) representation. This is a topic that often appears in high
school mathematics courses, and the basics of binary representation are
not complicated. In this book we review such basics as how to convert
numbers between decimal (base 10) and binary representation, and how to
do simple operations such as adding two binary numbers. However, we also
use binary representation in additional ways that underpin the workings
of computers. Here are a few examples:

-   We\'ll look at a few different ways to represent numbers in binary
    representation. For example, integers are often represented in
    binary not using the usual straightforward binary representation,
    but in \"two\'s complement\" form. So part of this book is learning
    not only about the \"usual\" binary representation, but also about
    these alternatives.
-   We\'ll look at various issues with binary representation, such as
    the number of \"bits\" used, that are important in determining the
    range and precision of numbers used by computers.
-   In addition to representing numbers, we will also look at how
    computers use binary representation to represent and operate on
    other types of data such as text, colors, and images.
-   In addition to basic operations such as binary addition, we will
    also look at other operations on binary representations. For
    example, logical operations are important in masking colors in image
    processing, and in implementing arithmetic operations in low-level
    computer hardware.

In summary, even though many of the mathematical topics in this book
appear in high school mathematics, they go beyond the usual high school
treatment of those topics in breadth or depth.

### Technology and society

**Question 5.** What do you think of when you hear \"technology and
society?\" Write a paragraph or list, or draw an image or diagram of
what comes to mind.

**Question 6.** Based on your experience with computing, write a list of
examples of how computing affects, and is affected by, society.

The topic of this book is computers and computing. Computers have
affected society in numerous and diverse ways, some of which we\'ll
explore in this book. And current and future computer applications will
affect society in even more ways.

Through this book you should get an understanding of how computers work.
This includes understanding the basics of computer hardware and computer
software.

More broadly, however, computer science relies on results from other
areas of science, engineering, and related fields. The most prominent
example of this we will see in this text is various ways that
mathematics is essential in computer science.

Technology affects society. However, it is not a one-way street. Society
also affects technology. For example, society fosters technology by
means such as government support for research. As another example,
different individuals, businesses, and other organizations adopt and use
technology in ways often not foreseen by the technology\'s creators.

In this book we\'ll look at a variety of instances of how society
affects technology. These include government funding for the early
Internet, Internet regulation, how business considerations affect
computing products, and societal aspects of computer security.

In many topics in computers and society there are multiple stakeholders.
These can include individual users, developers, companies (producers,
consumers, and intermediaries), government bodies, professional
organizations, and other types of organizations. These different
stakeholders often have different views and different goals.

In this book we will often look at technology and society issues from
numerous perspectives. Sometimes we will focus on a specific perspective
or the role of a specific stakeholder. However, other times we will
explore issues more broadly: Who are the stakeholders? What is their
role in this issue? What are their goals?

One often hears conflicting views on computer and society issues.
Computers are beneficial for society. Computers are harmful to society.
The Internet is making it easier for people to communicate and is
bringing people together. The Internet is making people more isolated.
Computers and automation are robbing people of jobs. Computers and
automation create jobs.[^3]

In this book we\'ll often explore issues that are contentious and/or
complicated. How do we avoid a superficial, one-sided understanding of
such issues? How do we resolve conflicting claims about such issues?

Computing technology not only has had massive effects on society, but
continues to affect society. Not a day goes by without some
technological advance involving computing. In many ways the \"computer
revolution\" is just beginning.

One goal of this book is that you\'ll learn enough about computing in
general, about trends in computing, and about computing and society that
you\'ll be able to evaluate new technology. Note that \"evaluate\" might
mean different things in different contexts. For instance, it might mean
give an informed projection about whether a new computer product will be
successful or not. Or it might mean predict future computer advances in
a certain area. Or it might mean analyze whether a new computer
application is more likely to be more beneficial than harmful.

## Additional questions for thought and discussion

Here are some additional introductory questions.

**Question 7.** How do you use computers? List the most important ways.

**Question 8.** Write down a list of movies in which computing plays a
major role. For each movie, indicate whether computing is portrayed as
beneficial, harmful, beneficial in some ways but harmful in others, or
neutral.

**Question 9.** Do you think computers, on the whole, have more positive
effects than negative ones, more negative ones than positive, or about
equal positive and negative effects? Why?

**Question 10.** List some ways computers are beneficial to society.
Then list some ways they are harmful.

**Question 11.** Suppose you were to write a novel, play, screenplay,
etc. about some aspect of computers and society. Describe what the theme
or themes of your work would be.

**Question 12.** What does \"technology\" mean? What are some important
ways you use technology in your daily life?

**Question 13.** Suppose you had to write a short essay or short story
entitled \"Computers and Me.\" What would be some key points or themes
in that work?

**Question 14.** Suppose you had to write a short essay or short story
entitled \"Technology and Me.\" What would be some key points or themes
in that work?

## Notes

```{=html}
<references />
```

[^1]: See Computer literacy at
    the English Wikipedia. Accessed May 20, 2015.

[^2]: For example, see

    This site organizes principles into seven categories: computation,
    communication, coordination, recollection, automation, evaluation,
    and design. There are a number of good ideas, insights, and
    frameworks in this and related approaches, and in fact many of the
    key ideas in this book will relate in some way to Denning\'s
    principles.

[^3]: See


# Foundations of Computer Science/What is Computing

## What is Computing

In this course, we try to focus on computing principles (big ideas)
rather than computer technologies, which are tools and applications of
the principles. Computing is
defined by a set of principles or ideas, which underlies a myriad of
technologies that are created based on the principles. Technologies can
be complex and constantly evolving but principles stays the same. In the
second half of the course, we will study various technologies to
demonstrate the power of computing and how principles are applied.

In addition to principles of computing and technologies there are
practices of computing - what professionals do to advance computing.

The chart to the right illustrates the difference between principles of
computing and practices of computing. Principles underlie technologies
and practices. A consumer exploits the power of computing through the
applications built for them for various tasks. We believe everyone needs
to know the principles of computing because such principles are widely
applicable. As professionals in the field of computing we need to know
the two ends and everything in the middle - the practices (activities
and skills that make computing useful and effective).

!This chart illustrates the difference between principles of computing
and practices of
computing.{width="600"}

We will use the terms computing and
computation interchangeably
throughout the book.

### Principles of Computing

Computing is fundamentally about information processes. One of the big
ideas of computing is that information processes can be carried out
purely mechanically via symbol manipulation. The agent that does the
computing, whether a thinking human being or a machine (computer), does
not matter. Toward the end of the book we will see this is true for all
modern computers - digital computers manipulate two symbols (zero and
one) blindly according to instructions.

#### An Analogy

The following analogy from the \"Thinking as Computation\" book [^1]
illustrates the idea. Imagine that we have the following table of
symbols.

      a    b    c    d    e    f    g    h    i    j
  --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
  a   aa   ab   ac   ad   ae   af   ag   ah   ai   aj
  b   ab   ac   ad   ae   af   ag   ah   ai   aj   ba
  c   ac   ad   ae   af   ag   ah   ai   aj   ba   bb
  d   ad   ae   af   ag   ah   ai   aj   ba   bb   bc
  e   ae   af   ag   ah   ai   aj   ba   bb   bc   bd
  f   af   ag   ah   ai   aj   ba   bb   bc   bd   be
  g   ag   ah   ai   aj   ba   bb   bc   bd   be   bf
  h   ah   ai   aj   ba   bb   bc   bd   be   bf   bg
  i   ai   aj   ba   bb   bc   bd   be   bf   bg   bh
  j   aj   ba   bb   bc   bd   be   bf   bg   bh   bi

The symbols can be any set of symbols, we pick the letters from the
English alphabet for simplicity. We can define a procedure P that takes
two symbols (\'a\' through \'j\') as the input and produces two symbols
in the same set as the output. Internally, the procedure uses the first
input symbol to find a row that starts with the same symbol, then uses
the second input symbol to find a column with the same symbol at the
top, and then report/return the symbols at the cross point. It is not
hard to imagine such a table lookup procedure can be done purely
mechanically (blindly) by a simple agent (e.g. a device or a machine).
Of course a human being can do it but this type of symbol manipulation
requires no human intelligence. Two conclusions can be drawn from this
thought experiment:

-   Symbol manipulation can be done mechanically.
-   The machine that performs the manipulation does not need to know the
    meaning of the symbols nor the purpose of the manipulation.

This procedure can be meaningful if we know how to interpret the
symbols. For example, if the symbols \'a\' through \'j\' represent
quantities of 0 through 9 respectively, this procedure performs single
decimal digit addition. For instance, p(d, f) = p(3, 5) = ai = 08, which
is the correct result of 3+5. The following table is essentially the
same as the previous one except that it uses symbols that are meaningful
to humans.

      0    1    2    3    4    5    6    7    8    9
  --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
  0   00   01   02   03   04   05   06   07   08   09
  1   01   02   03   04   05   06   07   08   09   10
  2   02   03   04   05   06   07   08   09   10   11
  3   03   04   05   06   07   08   09   10   11   12
  4   04   05   06   07   08   09   10   11   12   13
  5   05   06   07   08   09   10   11   12   13   14
  6   06   07   08   09   10   11   12   13   14   15
  7   07   08   09   10   11   12   13   14   15   16
  8   08   09   10   11   12   13   14   15   16   17
  9   09   10   11   12   13   14   15   16   17   18

Now that we have a simple procedure P that can instruct a simple agent
to add two single digit decimal numbers, we can devise a new procedure
P1 that can add three single-digit decimal numbers as shown in the
following chart.

!This chart illustrates a way to build a P1 procedure from a P1
procedure.{width="600"}

The new procedure P1 employs three instances of procedure P to add three
decimal digits and return two digits as the result. We can view the
procedures as machines with inputs and outputs and the lines are pipes
that allow the symbols to go from one place to another place. It is not
hard to imagine that an agent that can carry out P can carry out P1 as
P1 is entirely made up of P. Note that the dotted rectangle represents
the new procedure P1 made up of instances of P and the answer given by
P1 for the sample inputs is correct. Again the symbols used in the
process can be any set of symbols because internally simple table
lookups are performed.

Now imagine that we could use P1 to construct more complex procedures,
for example procedure P2 in the following chart.

!This chart illustrates a way to build a P2 procedure from a P1
procedure.{width="600"}

P2 uses P1 to add two double-digit numbers, in fact we can simply add
more P1s to the design to deal with any numbers of digits.

By now we can make the following observations:

-   Whatever machine that can perform P can perform P1, P2, and etc.
-   We have made procedures that perform seemingly intelligent
    activities by making them more complex and at the same time kept
    them doable by simple machines.

If we follow the same line of reasoning, it is not hard to imagine we
can create increasingly more complex procedures to instruct the simple
machine to do progressively more intelligent things, such as

-   integer subtraction
-   compare two integers (subtraction and check the sign of the result)
-   integer multiplication (repeated addition)
-   represent fractions using a pair of integers and do arithmetic on
    them
-   use matrices of integers to represent systems of equations and solve
    them using matrix operations
-   use systems of equations to model complex physical systems and
    perform numerical simulations of these systems

In summary, from this example we can see that simple symbolic operations
can be assembled to form larger procedures to perform amazing activities
through computational processes. Such activities are not limited to
numerical calculations. If we can represent abstract ideas as symbols
(as we represent abstract quantities as concrete numbers) and device
procedures to manipulate the symbols according to the relations among
the ideas we can model reasoning as computational processes. This is
what computer science is fundamentally about - information processes
with two essential components: representations and a sequence of rules
for manipulation of the representations. Note that it has nothing to do
with electronics or physics. The machine that carries out such processes
does not need to know the meaning of the symbols and why the process
yields correct results. The machine only needs to follow the procedures
(a set of rules) blindly.

As an example, you can read about a mechanical computer (difference
engine) designed by
Charles Babbage that
can tabulate polynomial functions:

!A difference engine: computing the solution to a polynomial
function.jpg "A difference engine: computing the solution to a polynomial function"){width="600"}

#### Another Analogy

Richard
Feynman
used another similar analogy (file clerk) to explain how computers work
from the inside out in his Computer Heuristics Lecture (1 hour 15 mins):
<http://www.youtube.com/watch?v=EKWGGDXe5MA>

#### History

Now that we have learned computing is, in essence, a certain
manipulation of symbols. A computer\'s ability to perform amazing tasks
depends on its ability to manipulate symbols according to well defined
rules. In fact, digital computers only manipulate two symbols - zeros
and ones. The intelligence of computing lies in the design and
implementations of the rules/programs.

In the future, when talking about computer machinery, we will see
computers are constructed using such principles.

You may wonder where the ideas come from. Many people in history made
significant contributions to ideas of computing and computers. Gottfried
Leibniz (1646--1716), a German philosopher, is considered the first
person to dream of reducing reasoning to calculation and building a
machine capable of carrying out such calculations. He observed that in
arithmetic we represent abstract quantities using symbols and manipulate
the symbols to get useful results according to rules. He dreamed that we
could represent abstract ideas using symbols and reason with the ideas
according to the logics between the ideas via similar concrete symbol
manipulation as we do in arithmetic. Such manipulations give us correct
results not because whoever does the manipulation is intelligent but
because the rules of manipulation mirrors relationships between
quantities and logics between ideas.

Because of Leibniz's dream, now we have computer science and universal
machines called computers. A computer is fundamentally a physical device
that can manipulate symbols following very simple logic rules. Almost
all computers are electronic because it happens to be cheaper and easier
to build that way. Computer science is fundamentally about the
information process (dealing with abstract ideas) that takes place
through symbol manipulation, which follows a recipe (a set of rules).
Such recipes are also known as algorithms. No wonder so many computer
programming books are called cookbooks :) In computer science we study
how to represent information and how to design and apply algorithms to
get meaningful results. There are usually many ways to perform the same
task. Comparing algorithms for evaluation purposes is called algorithm
(complexity) analysis. Communicating an algorithm (recipe) to a computer
is called programming/software development. The languages we use for
such communication are called computer programming languages. The
artifacts of programming are computer programs or software. The
engineering disciplines we try to apply in the software development
process to produce quality software is called software engineering. So
computer science is more about problem solving than computers. Computing
science is probably a more appropriate name for this discipline.

### Practices of Computing

Principles are fundamental ideas that permeate all aspects of computing.
Practices are not principles but are very useful to identify because
they identify the central practices of computing professionals.
Practices, sometimes called \"know-hows\", define someone\'s skill set
and the level of competency: beginner, competent, and expert. The four
core practices of computing are identified in the Great Principles of
Computing project:[^2]

-   Programming (including multilingual programming practice)
-   Systems and systems thinking
-   Modeling, validating, testing, and measuring
-   Innovating

Programming is an integral part of computer science because it allows us
to explore abstract ideas in computer science in concrete ways. It is
also an exciting creative process, which brings a great deal of
satisfaction when we can make computers do useful things. In this course
we will program in a very high-level graphical programming environment
to explore ideas in computer science.

Donald Knuth regards programming like composition: well-written programs
are a pleasure for others or yourself to read. He believes that
programming is triply rewarding :

-   beautiful code (aesthetic)
-   do useful work (humanitarian)
-   get paid (economic)

Programming a computer is essentially teaching the computer how to do
things. As we mentioned previously computers are simple machines that
strictly follow orders. For a computer to do the right task the
instructions in our program must be correct and logical. Programs that
are executable on a computer are software - serving as the brain of the
computer. Software with errors is called buggy (see for the history of
this name) software. Testing software on an actual computer can help
catch most bugs in the software. Testing provides almost immediate
feedback to the quality of our programs so that we can fix bugs and
improve it. Because of this, we believe programming makes us better
thinkers and learners. We will see why it is hard to prove the
correctness of programs.

!A person interacts with a computer in a programming
activity.{width="600"}

## References

[^1]: Levesque, Hector,Thinking as
    Computation, Hector J. Levesque,

[^2]: Denning, Peter, The Great Principles
    of Computing,
    <http://denninginstitute.com/pjd/GP/GP-site/welcome.html>