# 1. Installing Pin
* If your Linux system does not have Intel Pin installed. (If you have downloaded VirtualBox VM, it would have Pin installed, so skip this part).
* Download the https://yonghwi-kwon.github.io/class/softsec/pin/pin.sh and run it.
* Commands:
```shell
$ cd
$ wget https://yonghwi-kwon.github.io/class/softsec/pin/pin.sh
$ chmod +x pin.sh
$ ./pin.sh
```
1. `pin.sh` will download, unzip, and install the Pin.
2. After the installation, please close the terminal and open a new terminal to run the `pin`.
3. If it access the `pin` binary in your home folder, you can consider the installation is successful.
* Restart the shell (or terminal), and run `pin`
* If you see the following output, your setup is successful.
```shell
$ pin
E: [tid:2682] Missing application name
Pin: pin-3.28-98749-6643ecee5
Copyright 2002-2023 Intel Corporation.
Usage: pin [OPTION] [-t <tool> [<toolargs>]] -- <command line>
Use -help for a description of options
```
## 1.1. Compile a Pin Tool
* Move to the folder you extracted to the pin (where the `pin` binary exists).
* Move to `source/tools/MyPinTool`
```shell
$ cd source/tools/MyPinTool
```
* Run `make`
* Check whether the `obj-intel64` folder has `MyPinTool.so` file.
* If it has, your compilation was successful.
## 1.2. Running a program with a Pin tool
See the following examples to understand how to run a program with Pin and Pin tool.
Basically, you can run a program with a pin tool with the following command:
`pin -t YOUR-PIN-TOOL-SO-FILE -- COMMAND-THAT-RUNS-THE-PROGRAM`
Before you run, make sure that your pin tool's `.so` file's path is correct and accessible, as well as the command to the program.
* Run `ls` with a `icount.so` Pin tool
```shell
$ pwd
/home/classvm/pin/source/tools/SimpleExamples
$ ls obj-intel64/icount.so
obj-intel64/icount.so
$ pin -t obj-intel64/icount.so -- ls
```
* Run `ls -a` with a `icount.so` Pin tool
```shell
$ pin -t obj-intel64/icount.so -- ls -a
```
* Run `./hello` with a `MyPinTool.so` Pin tool
```shell
$ pin -t obj-intel64/MyPinTool.so -- ./hello
```
* Run `./hello arg` with a `MyPinTool.so` Pin tool
```shell
$ pin -t obj-intel64/MyPinTool.so -- ./hello arg
```
# 2. A simple Pintool (icount)
The below code is an example code of a Pintool.
```cpp=
VOID docount() { ins_count++; }
VOID Instruction(INS ins, VOID *v)
{
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
int main(int argc, char *argv[])
{
// Initialize PIN library. Print help message if -h(elp) is specified
// in the command line or the command line is invalid
if( PIN_Init(argc,argv) )
{
return Usage();
}
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction(Instruction, 0);
// Register function to be called when the application exits
PIN_AddFiniFunction(Fini, 0);
// Start the program, never returns
PIN_StartProgram();
return 0;
}
```
* Execution Timeline
* **Line 8:** `main()` function starts
* **Line 12**: `PIN_Init`
* **Line 18, 21**: Register callback functions `Instruction` and `Fini`.
* **Line 24**: Pin engine starts
> Pin engine starts to read instructions from the target binary and translates them.
* **Line 3**: For each instruction during the translation, `Instruction()` is called
* **Line 5**: Add a function call (`docount`) instruction to each translated instruction.
> Translated instructions are sent to the process to be executed
* **Line 1**: Before the original instruction is executed, `docount()` is executed.
# 3. Example: `lucky` program
You have a simple `lucky` program that compares your input with a random value. If you are super lucky you may get the right number.
### Source code
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char** argv)
{
if( argc < 2 ) {
printf("Usage: ./lucky [number]\n");
return 0;
}
srand(time(0));
int nLucky = rand() % 100;
int nYourNum = atoi(argv[1]);
if( nLucky == nYourNum ) {
printf("You got the right number! (Lucky #: %d, Your #: %d)\n",
nLucky, nYourNum);
} else {
printf("Next time... (Lucky #: %d, Your #: %d)\n",
nLucky, nYourNum);
}
return 0;
}
```
### Compile and Run
You can compile the program with the following command:
```shell
$ gcc -o lucky lucky.c
```
The below shows a few attempts to get the lucky number.
```shell
$ ./lucky 1
Next time... (Lucky #: 70, Your #: 1)
$ ./lucky 70
Next time... (Lucky #: 20, Your #: 70)
$ ./lucky 20
Next time... (Lucky #: 72, Your #: 20)
$ ./lucky 72
Next time... (Lucky #: 13, Your #: 72)
$ ./lucky 13
Next time... (Lucky #: 64, Your #: 13)
$ ./lucky 64
Next time... (Lucky #: 36, Your #: 64)
$
```
## 3.1. Execution Perturbation Strategy
Our goal is to make the program run the code `printf("You got the right number! ...)` at line 16. There can be multiple ways to perturb the execution to achieve this.
* **a.** Make the `rand()` return a constant value.
* **b.** Make the variable `nLucky` and `nYourNum` same.
* **c.** Change the control flow of the program at line 15.
### 3.1.a. `rand()` returning a constant
The lucky number is hard to guess as `rand()` returns a random value. What if we can make it always return `0`? This is what we are going to do.
#### Step 1. Obtaining Assembly Code
The following command (`objdump`) can obtain the disassembly code of the `lucky` binary.
```shell
$ objdump -S lucky > lucky.S
```
The below is a part of the `lucky.S` file.
```
00000000000011e9 <main>:
11e9: f3 0f 1e fa endbr64
11ed: 55 push %rbp
11ee: 48 89 e5 mov %rsp,%rbp
11f1: 48 83 ec 20 sub $0x20,%rsp
11f5: 89 7d ec mov %edi,-0x14(%rbp)
11f8: 48 89 75 e0 mov %rsi,-0x20(%rbp)
11fc: 83 7d ec 01 cmpl $0x1,-0x14(%rbp)
1200: 7f 19 jg 121b <main+0x32>
1202: 48 8d 05 ff 0d 00 00 lea 0xdff(%rip),%rax # 2008 <_IO_stdin_used+0x8>
1209: 48 89 c7 mov %rax,%rdi
120c: e8 8f fe ff ff call 10a0 <puts@plt>
1211: b8 00 00 00 00 mov $0x0,%eax
1216: e9 99 00 00 00 jmp 12b4 <main+0xcb>
121b: bf 00 00 00 00 mov $0x0,%edi
1220: e8 ab fe ff ff call 10d0 <time@plt>
1225: 89 c7 mov %eax,%edi
1227: e8 94 fe ff ff call 10c0 <srand@plt>
-------------------------------------------------------------------
122c: e8 bf fe ff ff call 10f0 <rand@plt>
-------------------------------------------------------------------
1231: 48 63 d0 movslq %eax,%rdx
1234: 48 69 d2 1f 85 eb 51 imul $0x51eb851f,%rdx,%rdx
123b: 48 c1 ea 20 shr $0x20,%rdx
123f: c1 fa 05 sar $0x5,%edx
1242: 89 c1 mov %eax,%ecx
1244: c1 f9 1f sar $0x1f,%ecx
```
* In x86, a function's return value is stored at the `eax` (`%eax`) register.
* To change the return value of `rand()`, we want to overwrite the `eax` register right before the `movslq` instruction at `0x1231`.
#### Step 2. The Initial Pin Tool
The below code will be your initial pin tool's code.
```cpp=
#include "pin.H"
#include "util.h"
CUtilImage pinutil;
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
Log("[%lx] %s\n", addr, strDis.c_str());
}
int main(int argc, char* argv[])
{
PIN_InitSymbols();
if (PIN_Init(argc, argv)) {
return 0;
}
//=========== util.h ==================
LogInit("log.txt");
pinutil.InstrumentImageLoad(0);
//=========================================
//==============================================
INS_AddInstrumentFunction(Instruction, 0);
//==============================================
//=======================================================
// Start the program, never returns
PIN_StartProgram();
return 0;
}
```
* Description on the key functions:
* main()
* It does a very simple job: (1) Initialize the pin tool with symbols (e.g., function/variable names) (Lines 15-16), (2) Load the pin utility code that is provided for you (Lines 21-22), and then (3) Register an instrumentation function for every instruction (Line 26).
* Lastly, `PIN_StartProgram()` (Line 31) will begin the execution of the target program.
* Instruction() (lines 6-11)
* It logs every instruction that is parsed.
* `INS_Address()` returns the runtime address of the instruction.
* `INS_Disassemble()` returns the instruction's disassembly in text.
* `Log()` is a printf like function provided by the `util.h` file. The file also contains various helper functions for the project.
#### Step 3. Executing the Pin tool
Run the pin tool with the below command.
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
```
You will get the following output.
```clike
[7f8b89e31290] mov rdi, rsp
[7f8b89e31293] call 0x7f8b89e32030
[7f8b89e32030] nop edx, edi
[7f8b89e32034] push rbp
[7f8b89e32035] mov rbp, rsp
[7f8b89e32038] push r15
[7f8b89e3203a] push r14
[7f8b89e3203c] push r13
[7f8b89e3203e] push r12
[7f8b89e32040] push rbx
...
```
This **does not look like instructions in the main** function. You can search those instructions (e.g., push r15) in the `lucky.S`, but you would not be able to find.
* **Why?** This is because those instructions are **not from the main executable file**. They are from **the OS's libraries**.
* [More details about this topic can be found in the appendix: **Pin-traces-every-user-mode-instructions** section below](#Pin-traces-every-user-mode-instructions).
#### Step 4. Finding the Source of the Instructions
To understand where those instructions are coming from, we can slightly improve `Instruction()` to print out the image file names that include the instructions.
* [If you wonder how the `GetImageName()` function is implemented, see the `IMG_AddInstrumentFunction to monitor module loading` section in the appendix.](#IMG_AddInstrumentFunction-to-monitor-module-loading)
```cpp=
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
}
```
With this, we can get the following log.
```clike
[/lib64/ld-linux-x86-64.so.2:7f59b3726290] mov rdi, rsp
[/lib64/ld-linux-x86-64.so.2:7f59b3726293] call 0x7f59b3727030
[/lib64/ld-linux-x86-64.so.2:7f59b3727030] nop edx, edi
[/lib64/ld-linux-x86-64.so.2:7f59b3727034] push rbp
[/lib64/ld-linux-x86-64.so.2:7f59b3727035] mov rbp, rsp
[/lib64/ld-linux-x86-64.so.2:7f59b3727038] push r15
[/lib64/ld-linux-x86-64.so.2:7f59b372703a] push r14
[/lib64/ld-linux-x86-64.so.2:7f59b372703c] push r13
[/lib64/ld-linux-x86-64.so.2:7f59b372703e] push r12
[/lib64/ld-linux-x86-64.so.2:7f59b3727040] push rbx
...
```
Now, we see those instructions are from **the OS's loader library** (`ld_linux-x86-64.so.2`).
If you scroll down the log file (or search `lucky`), you will see instructions from `lucky` as follows.
```clike
[/lib64/ld-linux-x86-64.so.2:7f59b37262d1] mov rsp, r13
[/lib64/ld-linux-x86-64.so.2:7f59b37262d4] jmp r12
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a100] nop edx, edi
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a104] xor ebp, ebp
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a106] mov r9, rdx
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a109] pop rsi
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a10a] mov rdx, rsp
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a10d] and rsp, 0xfffffffffffffff0
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a111] push rax
[/home/classvm/pin/source/tools/MyPinTool/lucky:55e866f9a112] push rsp
...
```
Great, but those are also **not** from the `main()` function of the program. If you try to search a few instructions (e.g., xor, mov, pop, ...) in the log, we can identify that they are from the `_start` function, which is the entrypoint of the program (i.e., the starting point of the program).
```clike
Disassembly of section .text:
0000000000001100 <_start>:
1100: f3 0f 1e fa endbr64
1104: 31 ed xor %ebp,%ebp
1106: 49 89 d1 mov %rdx,%r9
1109: 5e pop %rsi
110a: 48 89 e2 mov %rsp,%rdx
110d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1111: 50 push %rax
1112: 54 push %rsp
```
* **Static vs Dynamic Addresses** (Addresses in `lucky.S` vs in log)
* While we find the matching instructions, we see the addresses of the instructions from the **runtime log** and `lucky.S` are completely different: `55e866f9a100` vs `1100`.
* You can still make a mapping between them, but there is another problem. The runtime **address changes every time you run the program**.
For eample, run it again and I get the following address.
```clike
[/lib64/ld-linux-x86-64.so.2:7fef7c14c2d1] mov rsp, r13
[/lib64/ld-linux-x86-64.so.2:7fef7c14c2d4] jmp r12
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7100] nop edx, edi
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7104] xor ebp, ebp
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7106] mov r9, rdx
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7109] pop rsi
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e710a] mov rdx, rsp
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e710d] and rsp, 0xfffffffffffffff0
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7111] push rax
[/home/classvm/pin/source/tools/MyPinTool/lucky:55d34b7e7112] push rsp
```
It changed from `55e866f9a100` to `55d34b7e7100`. This is because of the ASLR (Address Space Layout Randomization). So, let's turn off the ASLR.
#### Step 5. Turning off ASLR
* Using the given `aslr.sh`, you can simply do `./aslr.sh off` (you need to provide the password).
```shell
$ ./aslr.sh off
[sudo] password for classvm:
0
```
* Now, without the ASLR, you will get a consistent address across runs (e.g., `555555555100`).
```clike
[/lib64/ld-linux-x86-64.so.2:7ffff7fe32d1] mov rsp, r13
[/lib64/ld-linux-x86-64.so.2:7ffff7fe32d4] jmp r12
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555100] nop edx, edi
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555104] xor ebp, ebp
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555106] mov r9, rdx
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555109] pop rsi
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555510a] mov rdx, rsp
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555510d] and rsp, 0xfffffffffffffff0
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555111] push rax
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555112] push rsp
```
#### Step 6. Finding the instructions of `main()`
To focus on the instructions of `main()`, you can leverage the provided `util.h`.
Specifically, `pinutil`'s `IsMainRoutine()` would return `1` if the given instruction address belongs to the `main()`. We then improve the `Instruction()` once again.
```cpp
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
if( pinutil.IsMainRoutine(addr) ) {
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
}
}
```
With this, we can get the below log.
```clike
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551e9] nop edx, edi
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551ed] push rbp
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551ee] mov rbp, rsp
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551f1] sub rsp, 0x20
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551f5] mov dword ptr [rbp-0x14], edi
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551f8] mov qword ptr [rbp-0x20], rsi
[/home/classvm/pin/source/tools/MyPinTool/lucky:5555555551fc] cmp dword ptr [rbp-0x14], 0x1
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555200] jnle 0x55555555521b
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555202] lea rax, ptr [rip+0xdff]
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555209] mov rdi, rax
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555520c] call 0x5555555550a0
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555521b] mov edi, 0x0
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555220] call 0x5555555550d0
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555225] mov edi, eax
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555522c] call 0x5555555550f0
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555231] movsxd rdx, eax
...
```
Remember that in `lucky.S`, the `main()` starts at `11e9`, and its runtime address is `5555555551e9`. The target instruction was at `122c`, which is in the log `55555555522c`.
| Address in `lucky.S` | Runtime Address |
| -------- | -------- |
| `11e9` | `5555555551e9` |
| `122c` | `55555555522c` |
* Simple address conversions can be done as follows:
* Runtime Address = `0x555555554000` + Address in `lucky.S`
* Address in `lucky.S` = Runtime Address - `0x555555554000`
Since we disabled the ASLR, the runtime addresses such as `55555555522c` would not be changing across runs. Hence, in this homework, we can just use the hardcoded address.
#### Step 7. Changing the Function's Return Value
* `55555555522c` is a `call` instruction. At this instruction, the function is not called.
* The next instruction is `555555555231`, which is executed after the function return. Hence, to change the function's return value, `555555555231` is the right place.
#### Step 8. Instrumenting Runtime Callback Function on Instructions
* `Instruction()` is executed while Pin is parsing the program. It can inspect the static instruction before its execution.
* To monitor/perturb runtime execution, we need to register a callback function on the instructions. Once registered, the callback function will be executed at runtime, so that you can modify the runtime execution.
* You can use `INS_InsertCall()` to register a callback function. The below shows an example:
* [If you want to know the details of using `INS_InsertCall` to read/write the register's value, see the `Tracing and changing register values` section in the appendix.](#Tracing-and-changing-register-values)
```cpp=
VOID InsCallback_RAX(ADDRINT ip, ADDRINT * regRAX);
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
if( pinutil.IsMainRoutine(addr) ) {
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
switch( addr ) {
case 0x555555555231:
{
INS_InsertCall( ins, IPOINT_BEFORE,
(AFUNPTR)InsCallback_RAX,
IARG_ADDRINT, addr,
IARG_REG_REFERENCE, REG_RAX,
IARG_END );
}
break;
}
}
}
VOID InsCallback_RAX(ADDRINT ip, ADDRINT * regRAX)
{
switch( ip ) {
case 0x555555555231:
Log("[InsCallback_RAX] %ld\n", *regRAX );
break;
}
}
```
Running the pin tool would result the following log.
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
Next time... (Lucky #: 37, Your #: 0)
```
* Log
```clike
...
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555262] mov rdi, rax
[/home/classvm/pin/source/tools/MyPinTool/lucky:555555555265] call 0x5555555550e0
[InsCallback_RAX] 1480480237
[/home/classvm/pin/source/tools/MyPinTool/lucky:55555555526a] mov dword ptr [rbp-0x4], eax
...
```
You can see the `rand()` returns `1480480237`, where `1480480237 % 100` is `37` which is shown on the terminal.
#### Step 9. Overwriting the RAX register (Function's Return Value)
You can overwrite the value as follows:
```cpp
VOID InsCallback_RAX(ADDRINT ip, ADDRINT * regRAX)
{
switch( ip ) {
case 0x555555555231:
Log("[InsCallback_RAX] %ld\n", *regRAX );
*regRAX = 0;
break;
}
}
```
With this change, you can run the program, and can see the following.
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
You got the right number! (Lucky #: 0, Your #: 0)
```
Now we changed the return value of `rand()` to `0`.
### 3.1.b. Make the `nLucky` and `nYourNum` same
This strategy will copy values from `nLucky` to `nYourNum` or vice versa, to make them have the same value.
We assume that you have followed all the previous steps in **3.1.a**, and do not repeat the steps.
#### Step 1. Identify Variable
The first step is to identify the variables' runtime addresses. In particular, we will identify instructions that write the variables, and obtain addresses of the variables from the instruction.
* See the following code from `lucky.S`
```clike
...
1265: e8 76 fe ff ff call 10e0 <atoi@plt>
126a: 89 45 fc mov %eax,-0x4(%rbp)
126d: 8b 45 f8 mov -0x8(%rbp),%eax
1270: 3b 45 fc cmp -0x4(%rbp),%eax
1273: 75 1e jne 1293 <main+0xaa>
1275: 8b 55 fc mov -0x4(%rbp),%edx
...
```
which is roughly from the below 2 lines of C code.
```cpp
int nYourNum = atoi(argv[1]);
if( nLucky == nYourNum ) {
```
We focus on `cmp` at `1270` that compares. It compares `%rbp-4` and `%rbp-8` which is loaded to `%eax` at `126d`. Seeing that `atoi()`'s return is stored to `%rbp-4` at `126a`, we can infer the following: `%rbp-4 = nYourNum` and `%rbp-8 = nLucky`.
#### Step 2. Obtaining Memory Addresses
With the above information, we can do the following
* At `126a`, identify the runtime address of `%rbp-4`.
* At `126d`, read `%rbp-8` and overwrite the value to `%rbp-4`.
The following code would do the job.
```cpp=
#include "pin.H"
#include "util.h"
CUtilImage pinutil;
VOID OnMemWriteAfter(VOID * ip, VOID * addr, UINT32 size);
VOID OnMemRead(VOID * ip, VOID * addr, UINT32 size);
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
if( pinutil.IsMainRoutine(addr) ) {
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
switch( addr ) {
case 0x55555555526a:
case 0x55555555526d:
{
UINT32 memOperands = INS_MemoryOperandCount(ins);
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
if (INS_MemoryOperandIsWritten(ins, memOp)) {
INS_InsertCall(
ins, IPOINT_AFTER, (AFUNPTR)OnMemWriteAfter,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_MEMORYWRITE_SIZE,
IARG_END);
}
if (INS_MemoryOperandIsRead(ins, memOp)) {
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)OnMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_MEMORYREAD_SIZE,
IARG_END);
}
}
}
break;
}
}
}
VOID OnMemWriteAfter(VOID * ip, VOID * addr, UINT32 size)
{
Log("[MEMWRITE(AFTER)] %p, memaddr: %p, size: %d\n", ip, addr, size);
LogData(addr, size);
}
VOID OnMemRead(VOID * ip, VOID * addr, UINT32 size)
{
Log("[MEMREAD] %p, memaddr: %p, size: %d\n", ip, addr, size);
LogData(addr, size);
}
int main(int argc, char* argv[])
{
PIN_InitSymbols();
if (PIN_Init(argc, argv)) {
return 0;
}
//=========== util.h ==================
LogInit("log.txt");
pinutil.InstrumentImageLoad(0);
//=========================================
//==============================================
INS_AddInstrumentFunction(Instruction, 0);
//==============================================
//=======================================================
// Start the program, never returns
PIN_StartProgram();
return 0;
}
```
* Code Description:
* `INS_MemoryOperandCount` returns how many memory read/write operations will be conducted by the instruction. It loops over all the operations.
* `INS_MemoryOperandIsWritten` returns `1` if a given memory operation is **write**.
* `INS_InsertCall` is used to attach a callback function on the instruction. It asks Pin to pass the memory address of the operation (`IARG_MEMORYOP_EA, memOp`) and the size of memory write (`IARG_MEMORYWRITE_SIZE`).
* `INS_MemoryOperandIsRead` returns `1` if a given memory operation is **read**.
* `INS_InsertCall` is used to attach a callback function on the instruction. It asks Pin to pass the memory address of the operation (`IARG_MEMORYOP_EA, memOp`) and the size of memory read (`IARG_MEMORYREAD_SIZE`).
* `OnMemWriteAfter` will be called after the instruction is done (i.e., after the memory is written) (due to `IPOINT_AFTER` in `InsertCall`).
* `OnMemRead` will be called before the instruction is done (i.e., before the memory is read) (due to `IPOINT_BEFORE` in `InsertCall`).
* [Regarding the Pin instrumentation APIs for the memory read/write operations, please see the `Tracing and modifying memory reads/writes` section in the appendix.](#Tracing-and-modifying-memory-readswrites)
* The log is as follows, when the lucky number is 34 (`0x22`).
```clike
[MEMWRITE(AFTER)] 0x55555555526a, memaddr: 0x7fffffffe11c, size: 4
00 00 00 00
[MEMREAD] 0x55555555526d, memaddr: 0x7fffffffe118, size: 4
22 00 00 00
```
#### Step 3. Copying the Memory Content
With the target instructions, we can extract the addresses and copy the value as follows.
```cpp=
int* g_pnLuckyNum;
int* g_pnYourNum;
VOID OnMemWriteAfter(VOID * ip, VOID * addr, UINT32 size)
{
Log("[MEMWRITE(AFTER)] %p, memaddr: %p, size: %d\n", ip, addr, size);
LogData(addr, size);
if( (VOID*)0x55555555526a == ip ) {
g_pnYourNum = (int*) addr;
}
}
VOID OnMemRead(VOID * ip, VOID * addr, UINT32 size)
{
Log("[MEMREAD] %p, memaddr: %p, size: %d\n", ip, addr, size);
LogData(addr, size);
if( (VOID*)0x55555555526d == ip ) {
g_pnLuckyNum = (int*) addr;
if( g_pnYourNum && g_pnLuckyNum ) {
*g_pnYourNum = *g_pnLuckyNum;
}
}
}
```
With this, we can run the program as follows.
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
You got the right number! (Lucky #: 74, Your #: 74)
```
Observe that the initial input provided is `0` but the lucky number is copied to the input.
### 3.1.c. Change the control flow
See the control flow of the `lucky.S`.
```c
int nYourNum = atoi(...);
1265: e8 76 fe ff ff call 10e0 <atoi@plt>
126a: 89 45 fc mov %eax,-0x4(%rbp)
if( nLucky == nYourNum ) {
126d: 8b 45 f8 mov -0x8(%rbp),%eax
1270: 3b 45 fc cmp -0x4(%rbp),%eax
1273: 75 1e jne 1293 <main+0xaa>
printf("You got the right number! (Lucky #: %d, Your #: %d)\n", nLucky, nYourNum);
1275: 8b 55 fc mov -0x4(%rbp),%edx
1278: 8b 45 f8 mov -0x8(%rbp),%eax
127b: 89 c6 mov %eax,%esi
127d: 48 8d 05 9c 0d 00 00 lea 0xd9c(%rip),%rax # 2020 <_IO_stdin_used+0x20>
1284: 48 89 c7 mov %rax,%rdi
1287: b8 00 00 00 00 mov $0x0,%eax
128c: e8 1f fe ff ff call 10b0 <printf@plt>
1291: eb 1c jmp 12af <main+0xc6>
printf("Next time... (Lucky #: %d, Your #: %d)\n", nLucky, nYourNum);
1293: 8b 55 fc mov -0x4(%rbp),%edx
1296: 8b 45 f8 mov -0x8(%rbp),%eax
1299: 89 c6 mov %eax,%esi
```
Observe that I put the C source code for assembly instructions.
* See `jne` at `1273`. `jne` means `jump if not equal`, where the `cmp` at `1270` compared the `nLucky` and `nYourNum`.
* If they **two are different**, it jumps to `1293`, meaning that the instructions from `1293` is for the `else` block.
* If they **two are identical**, it continues to execute the code at `1275`, which is the `true` branch block.
With the above analysis, we can try two methods.
* We can delete the `jne` instruction so that it will always go into the `true` branch.
* We can insert an unconditional jump (`jmp`) at `1270`.
#### Method 1: Delete `jne` instruction.
The below code will delete the `jne` instruction.
```cpp
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
if( pinutil.IsMainRoutine(addr) ) {
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
if( addr == 0x555555555273 ) {
INS_Delete(ins);
}
}
}
```
Running it will give the below output:
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
You got the right number! (Lucky #: 11, Your #: 0)
```
Observe that **the numbers are different**, but the program says it is the right number.
This way of forcing the control flow is not recommended in practice because if you force the control flow, without making sure that all the variables have valid values, would cause an execution with **invalid execution context**. It is possible that such an execution might be infeasible, hence analysis on the execution might be useless and misleading.
#### Method 2: Insert an unconditional jump
Similarly, the below code will insert an unconditional jump right before the `cmp` instruction (at `1270`) to jump to `1275`.
```cpp
VOID Instruction(INS ins, VOID *v)
{
ADDRINT addr = INS_Address(ins);
string strDis = INS_Disassemble(ins);
if( pinutil.IsMainRoutine(addr) ) {
Log("[%s:%lx] %s\n", pinutil.GetImageName(addr), addr, strDis.c_str());
if( addr == 0x555555555270 ) {
INS_InsertDirectJump( ins, IPOINT_BEFORE, 0x555555555275 );
}
}
}
```
Running the pin tool gives you the following output:
```shell
$ pin -t ./obj-intel64/MyPinTool.so -- ./lucky 0
You got the right number! (Lucky #: 23, Your #: 0)
```
# Appendix
## Pin traces every user mode instructions
Pin traces every instruction executed (in the user mode), including instructions from libraries and operating systems. In many cases, particularly for malware analysis, we often focus on the application's code but not library/OS code. Since you know what library/OS functions are doing, we don't need to trace. For example, once you see an application call `strcpy(s1, s2)`, you know it will copy a string `s2` to `s1`, without looking at the individual instructions in `strcpy()`.
The below logs show an example of the `icount` pintool, counting the number of instructions executed. It includes all the instructions executed including those in the library/OS functions.
```shell
$ pin -t obj-intel64/icount.so -- target/sample
...
Count 193790
```
Here is a few examples of what it traced
```shell
7ffff7fd0100 (/lib64/ld-linux-x86-64.so.2 + 1100) mov rdi, rsp
7ffff7fd0103 (/lib64/ld-linux-x86-64.so.2 + 1103) call 0x7ffff7fd0df0
7ffff7fd0df0 (/lib64/ld-linux-x86-64.so.2 + 1df0) nop edx, edi
7ffff7fd0df4 (/lib64/ld-linux-x86-64.so.2 + 1df4) push rbp
7ffff7fd0df5 (/lib64/ld-linux-x86-64.so.2 + 1df5) mov rbp, rsp
7ffff7fd0df8 (/lib64/ld-linux-x86-64.so.2 + 1df8) push r15
7ffff7fd0dfa (/lib64/ld-linux-x86-64.so.2 + 1dfa) mov r15, rdi
7ffff7fd0dfd (/lib64/ld-linux-x86-64.so.2 + 1dfd) push r14
7ffff7fd0dff (/lib64/ld-linux-x86-64.so.2 + 1dff) push r13
7ffff7fd0e01 (/lib64/ld-linux-x86-64.so.2 + 1e01) push r12
7ffff7fd0e03 (/lib64/ld-linux-x86-64.so.2 + 1e03) push rbx
7ffff7fd0e04 (/lib64/ld-linux-x86-64.so.2 + 1e04) sub rsp, 0x38
...
more 12,000 instructions omitted
...
7ffff7fd013a (/lib64/ld-linux-x86-64.so.2 + 113a) lea rdx, ptr [rip+0x10c0f]
7ffff7fd0141 (/lib64/ld-linux-x86-64.so.2 + 1141) mov rsp, r13
7ffff7fd0144 (/lib64/ld-linux-x86-64.so.2 + 1144) jmp r12
5555555550c0 (.../target/sample + 10c0) nop edx, edi
5555555550c4 (.../target/sample + 10c4) xor ebp, ebp
5555555550c6 (.../target/sample + 10c6) mov r9, rdx
5555555550c9 (.../target/sample + 10c9) pop rsi
5555555550ca (.../target/sample + 10ca) mov rdx, rsp
5555555550cd (.../target/sample + 10cd) and rsp, 0xfffffffffffffff0
5555555550d1 (.../target/sample + 10d1) push rax
5555555550d2 (.../target/sample + 10d2) push rsp
5555555550d3 (.../target/sample + 10d3) lea r8, ptr [rip+0x206]
5555555550da (.../target/sample + 10da) lea rcx, ptr [rip+0x18f]
```
Then, we modify the pintool to only count the instructions executed in the `main` module (the `sample` binary).
```shell
$ pin -t obj-intel64/icount.so -- target/sample
...
Count 134
```
### IMG_AddInstrumentFunction to monitor module loading
From the simple pintool program, we call an additional instrumentation function: `IMG_AddInstrumentFunction`.
Observe the line 14. It registers a new function `ImageLoad` to be called when there are any new image (e.g., library) is loaded.
```cpp=
int main(int argc, char* argv[])
{
if (PIN_Init(argc, argv))
{
return Usage();
}
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Register ImageLoad to be called when an image is loaded
IMG_AddInstrumentFunction(ImageLoad, 0);
// Never returns
PIN_StartProgram();
return 0;
}
```
Then, we add the `ImageLoad` function as follows.
```cpp
ADDRINT g_addrLow, g_addrHigh;
BOOL g_bMainExecLoaded = FALSE;
VOID ImageLoad(IMG img, VOID *v)
{
if( IMG_IsMainExecutable(img) ) {
g_addrLow = IMG_LowAddress(img);
g_addrHigh = IMG_HighAddress(img);
// Use the above addresses to prune out non-interesting instructions.
g_bMainExecLoaded = TRUE;
}
}
```
In this case (also in most cases), we are interested in the main module's code, which is essentially the code of the binary (`sample` in this case).
`IMG_IsMainExecutable()` returns `true` if the given `img` is a main executable file (i.e., the main binary file you ran).
Then, we use `IMG_LowAddress()` and `IMG_HighAddress()` to obtain the base address (lower bound) and the highest address that belongs to the module (upper bound).
Any instructions fall into the address range (`g_addrLow` ~ `g_addrHigh`) means that they belong the main executable.
Then now we use the information to filter out the instructions.
```cpp=
VOID Instruction(INS ins, VOID* v)
{
string strInst = INS_Disassemble(ins);
ADDRINT addr = INS_Address(ins);
if( g_bMainExecLoaded ) {
if( g_addrLow <= addr && addr <= g_addrHigh ) {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
}
```
Thanks to the lines 6-7, now we will only instrument instructions that belong to the main module.
## Tracing and modifying memory reads/writes
Adding the following code will let you trace memory reads/writes.
```cpp=
VOID RecordMemWriteBefore(VOID * ip, VOID * addr, UINT32 size)
{
printf("[MEMWRITE(BEFORE)] %p, memaddr: %p, size: %d\n", ip, addr, size);
unsigned char* p = (unsigned char*)addr;
for( unsigned int i = 0; i < size; i++ ) {
printf("%02x ", (unsigned char)*p);
p++;
}
printf("\n");
}
VOID RecordMemRead(VOID * ip, VOID * addr, UINT32 size)
{
printf("[MEMREAD] %p, memaddr: %p, size: %d\n", ip, addr, size);
unsigned char* p = (unsigned char*)addr;
for( unsigned int i = 0; i < size; i++ ) {
printf("%02x ", (unsigned char)*p);
p++;
}
printf("\n");
}
VOID Instruction(INS ins, VOID* v)
{
...
UINT32 memOperands = INS_MemoryOperandCount(ins);
// Iterate over each memory operand of the instruction.
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_MEMORYREAD_SIZE,
IARG_END);
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWriteBefore,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_MEMORYWRITE_SIZE,
IARG_END);
}
}
}
```
### INS_MemoryOperandCount
First, `INS_MemoryOperandCount` returns a number of memory operations for a given instruction `ins`. For example, if an instruction reads a single memory once, it returns 1. If an instructions reads a value from memory and writes the value into another memory address, there are two memory operations: one read and one write. In this case, `INS_MemoryOperandCount` returns 2.
### INS_MemoryOperandIsRead and INS_MemoryOperandIsWritten
We then loop through the memory operations (i.e., memory operands). For each operand, `INS_MemoryOperandIsRead` returns true if the current memory operand is for memory read (observe that we pass `memOp` which is essentially an index of memory operands). Similarly, `INS_MemoryOperandIsWritten` returns true if the operand is for memory write.
### INS_InsertCall
Once we identify whether a memory operand is for read or write, we instrument accordingly using `INS_InsertCall`. Observe the arguments of the function.
```cpp=
INS_InsertCall(
ins,
IPOINT_BEFORE,
(AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_MEMORYREAD_SIZE,
IARG_END);
```
* `ins`
* the target instruction to be instrumented
* `IPOINT_BEFORE`
* the instrumentation (i.e., inserting a callback function) before the target instruction. There can be IPOINT_AFTER, but there are many instructions you can't use IPOINT_AFTER option. [Details are explained below (Link).](#IPOINT_BEFORE-vs-IPOINT_AFTER)
* `RecordMemRead`
* callback function name. This funciton will be instrumented to `ins`.
* `IARG_INST_PTR`
* The first function argument of the `RecordMemRead` callback function.
* `IARG_MEMORYOP_EA, memOp`
* The two arguments indicate that the second argument of the function `RecordMemRead` will be the effective address (EA) of the memory operand. Essentially, memory address that will be read will be passed to `RecordMemRead`
* `IARG_MEMORYREAD_SIZE`
* The third argument of `RecordMemRead` is the size of memory read.
* `IARG_END`
* It specifies that the end of the argument definition of `RecordMemRead` function.
To this end, we instrument the following function
```cpp=
VOID RecordMemRead(VOID * ip, VOID * addr, UINT32 size)
{
...
}
```
Arguments are defined at the `INS_InsertCall`
* `ip` is defined by `IARG_INST_PTR`
* `addr` is defined by `IARG_MEMORYOP_EA, memOp`
* `size` is defined by `IARG_MEMORYREAD_SIZE`
`RecordMemWriteBefore` follows the same method.
### IPOINT_BEFORE vs IPOINT_AFTER
The above code essentially instrument the function call to `RecordMemRead` and `RecordMemWriteBefore` at the target instruction, precisely before (IPOINT_BEFORE) the instruction.
Assume that we have the following four instructions, and we want to instrument the instruction at line 2.
```nasm=
mov rsi, rax
mov rax, qword ptr [rip+0x10] ; <==== target
call 0x7ffff7fd0df0
mov qword ptr [rax+0x20], rax
```
`IPOINT_BEFORE` means it will instrument before the target instruction.
```nasm=
mov rsi, rax
call RecordMemRead ; <==== instrumentation
mov rax, qword ptr [rip+0x2ea5] ; <==== target
call 0x7ffff7fd0df0
mov qword ptr [rax+0x20], rax
```
If you use `IPOINT_AFTER`, the instrumentation will look like the below code.
```nasm=
mov rsi, rax
mov rax, qword ptr [rip+0x2ea5] ; <==== target
call RecordMemRead ; <==== instrumentation
call 0x7ffff7fd0df0
mov qword ptr [rax+0x20], rax
```
#### A. Modifying the memory
Modifying a memory content in `RecordMemRead` and `RecordMemWriteBefore` are simple. You have the memory address at `addr`. You just write a value at `addr`. For instance, you can simply use the `*` operator to do, or can do via `memcpy`.
#### B. Before or After: Changing After Read
Observe that modifying the memory after the operation would not be effective. For example, in the below example, changing a value in `RecordMemRead` after the `mov` instruction would not affect `rax` in the `mov` instruction.
```nasm
mov rax, qword ptr [rax+0x2ea5] ; <==== target
call RecordMemRead ; <==== instrumentation
```
#### C. Before or After: Changing Before Write
Similarly, changing a value at a memory address before its actual memory write operation, will be wasted, because it will be overwritten.
```nasm
call RecordMemWriteBefore ; <==== instrumentation
mov qword ptr [rax+0x2ea5], rax ; <==== target
```
For instance, writing a value at `[rax+0x2ea5]` at `RecordMemWriteBefore` is essentially useless because it will be immeidately overwritten by the next instruction.
To effectively achieve this, you want to do it after the instruction. To do so, you may want to use `IPOINT_AFTER` or doing it before the next instruction.
#### D. IPOINT_AFTER is not supported for some instructions
Instructions that change the control flow of the program, `IPOINT_AFTER` is not supported. For example, if you want to instrument the `call` instruction with `IPOINT_AFTER`, the pin framework will cause an error.
```nasm=
mov rsi, rax
mov rax, qword ptr [rip+0x2ea5]
call 0x7ffff7fd0df0 ; <==== target
mov qword ptr [rax+0x20], rax
```
This is because, even if we instrument it after the `call` (line 3) as shown below, the instrumented code (line 4) will not be executed right after the line 3. It will be executed **after the the function called at line 3 returns**.
```nasm=
mov rsi, rax
mov rax, qword ptr [rip+0x2ea5]
call 0x7ffff7fd0df0 ; <==== target
call RecordMemWriteBefore ; <==== instrumentation
mov qword ptr [rax+0x20], rax
```
Of course, except for those corner cases, `IPOINT_AFTER` is a very useful mechanism. However, due to this limitation, I do not prefer it. Instead, I prefer to instrument all the instruction and do the required operation on the next instruction (you can check what was the last instruction executed to do this).
#### E. Example Code
Please see the provided file's sample pintool's code (particularly in `RecordMemWriteBefore` and `EveryInst`).
## Tracing and changing register values
To trace and change the registers' values, we can add **references of registers in the instrumentation function**. For example,
```cpp
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)EveryInst, IARG_INST_PTR,
IARG_REG_REFERENCE, REG_RAX,
IARG_REG_REFERENCE, REG_RBX,
IARG_REG_REFERENCE, REG_RCX,
IARG_REG_REFERENCE, REG_RDX,
IARG_END);
```
Observe `IARG_REG_REFERENCE, REG_RAX`, `IARG_REG_REFERENCE, REG_RBX`, `IARG_REG_REFERENCE, REG_RCX`, and `IARG_REG_REFERENCE, REG_RDX`.
They are essentially adding four arguments to the function `EveryInst`, resulting in the following function definition.
```cpp=
VOID EveryInst(ADDRINT ip,
ADDRINT * regRAX,
ADDRINT * regRBX,
ADDRINT * regRCX,
ADDRINT * regRDX)
{
printf("%lx\n", *regRAX); // read value
...
*regRAX = 0xdeadbeef; // new value
...
}
```
`regRAX`, `regRBX`, `regRCX`, and `regRDX` are references meaning that you can read and write the values inside them.
A very simple example of **read** is shown at line 7, while **writing** a new value is shown at line 9.
### Example: Changing return value of time()
The `sample` program (used in the above as well) is shown in the below.
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void main(int argc, char** argv)
{
int n = 0;
if( argc >= 2) n = atoi(argv[1]);
else n = (int)((int)time(0) % 500);
printf("n = %d\n", n);
if( n == 215 ) {
printf("1st\n");
} else if( n == 316) {
printf("2nd\n");
} else {
printf("Nothing\n");
}
}
```
In this example, we want to change a return value of `time()` at line 9.
The below is showing a result of `objdump -S sample`
```nasm=
...
00000000000011a9 <main>:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void main(int argc, char** argv)
{
11a9: f3 0f 1e fa endbr64
11ad: 55 push %rbp
11ae: 48 89 e5 mov %rsp,%rbp
11b1: 48 83 ec 20 sub $0x20,%rsp
11b5: 89 7d ec mov %edi,-0x14(%rbp)
11b8: 48 89 75 e0 mov %rsi,-0x20(%rbp)
int n = 0;
11bc: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
if( argc >= 2) n = atoi(argv[1]);
11c3: 83 7d ec 01 cmpl $0x1,-0x14(%rbp)
11c7: 7e 18 jle 11e1 <main+0x38>
11c9: 48 8b 45 e0 mov -0x20(%rbp),%rax
11cd: 48 83 c0 08 add $0x8,%rax
11d1: 48 8b 00 mov (%rax),%rax
11d4: 48 89 c7 mov %rax,%rdi
11d7: e8 d4 fe ff ff callq 10b0 <atoi@plt>
11dc: 89 45 fc mov %eax,-0x4(%rbp)
11df: eb 33 jmp 1214 <main+0x6b>
else n = (int)((int)time(0) % 500);
11e1: bf 00 00 00 00 mov $0x0,%edi
11e6: e8 b5 fe ff ff callq 10a0 <time@plt>
11eb: 48 63 d0 movslq %eax,%rdx
11ee: 48 69 d2 d3 4d 62 10 imul $0x10624dd3,%rdx,%rdx
11f5: 48 c1 ea 20 shr $0x20,%rdx
11f9: 89 d1 mov %edx,%ecx
11fb: c1 f9 05 sar $0x5,%ecx
11fe: 99 cltd
11ff: 29 d1 sub %edx,%ecx
1201: 89 ca mov %ecx,%edx
1203: 89 55 fc mov %edx,-0x4(%rbp)
1206: 8b 55 fc mov -0x4(%rbp),%edx
1209: 69 d2 f4 01 00 00 imul $0x1f4,%edx,%edx
120f: 29 d0 sub %edx,%eax
1211: 89 45 fc mov %eax,-0x4(%rbp)
printf("n = %d\n", n);
1214: 8b 45 fc mov -0x4(%rbp),%eax
1217: 89 c6 mov %eax,%esi
1219: 48 8d 3d e4 0d 00 00 lea 0xde4(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
1220: b8 00 00 00 00 mov $0x0,%eax
1225: e8 66 fe ff ff callq 1090 <printf@plt>
if( n == 215 ) {
122a: 81 7d fc d7 00 00 00 cmpl $0xd7,-0x4(%rbp)
1231: 75 0e jne 1241 <main+0x98>
printf("1st\n");
1233: 48 8d 3d d2 0d 00 00 lea 0xdd2(%rip),%rdi # 200c <_IO_stdin_used+0xc>
123a: e8 41 fe ff ff callq 1080 <puts@plt>
} else if( n == 316) {
printf("2nd\n");
} else {
printf("Nothing\n");
}
123f: eb 23 jmp 1264 <main+0xbb>
} else if( n == 316) {
1241: 81 7d fc 3c 01 00 00 cmpl $0x13c,-0x4(%rbp)
1248: 75 0e jne 1258 <main+0xaf>
printf("2nd\n");
124a: 48 8d 3d bf 0d 00 00 lea 0xdbf(%rip),%rdi # 2010 <_IO_stdin_used+0x10>
1251: e8 2a fe ff ff callq 1080 <puts@plt>
1256: eb 0c jmp 1264 <main+0xbb>
printf("Nothing\n");
1258: 48 8d 3d b5 0d 00 00 lea 0xdb5(%rip),%rdi # 2014 <_IO_stdin_used+0x14>
125f: e8 1c fe ff ff callq 1080 <puts@plt>
1264: 90 nop
1265: c9 leaveq
1266: c3 retq
1267: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
126e: 00 00
...
```
Observe that it is at line 30 (@11eb).
```nasm
11e6: e8 b5 fe ff ff callq 10a0 <time@plt>
11eb: 48 63 d0 movslq %eax,%rdx ; <====
```
Before this `movslq` (`mov`, slq means the data size of the `mov` instruction), we want to change the value of `rax` (or `eax`) register's value.
The below code is for that purpose:
```cpp
VOID EveryInst(ADDRINT ip, ADDRINT * regRAX, ADDRINT * regRBX, ADDRINT * regRCX, ADDRINT * regRDX)
{
/*
modifying rax register after time()
11e6: e8 b5 fe ff ff callq 10a0 <time@plt>
11eb: 48 63 d0 movslq %eax,%rdx
*/
if( ip == 0x5555555551eb ) {
// assign a new value 316.
*regRAX = 316;
}
}
```
This forces the return value of `time()` is always 316.