3. Simple Lambdas
Github Repo with Tools and Files
Track3_1¶
This sample aims to show what it looks like in assembly when variable is initialised as a pointer and as an actual value.
Additionally, it aims to show us how to manipulate types in IDA to make the output look like the source code that we are reversing.
Track3_1.c¶
In track3_1, we are given the following C file, which contains 2 structs, SimpleContext and SimpleLambda. As can be seen below, SimpleLambda will be making use of the SimpleContext and in the main function, we will simply be passing values into the
struct SimpleContext{
int Arg1;
int Arg2;
};
struct SimpleLambda{
void *pCallback;
short Priority;
struct SimpleContext *pContext
};
int main(){
struct SimpleContext c;
c.Arg1 = 10;
c.Arg2 = 12;
struct SimpleLambda l;
l.priority =1;
}
Track3_1.exe¶
In the case of Track3_1.exe, the .pdb file exists, and hence we can find the structure of SimpleContext and SimpleLambda in local types (Shift + F1).
However, at this stage, looking at the disassembly below,
We simply just need to go to the "local types" window select the types we want, in this case SimpleContext and SimpleLambda then hit enter to import the structures.
And as can be seen below, the structs have been imported to our structure window.
We then go to the stack where the variables are stored (double-click on the variable) and then alt+q to convert the variables to the structure we want it to be.
As can be seen below, Arg1 and Arg2 is labeled on the disassembler view.
We also notice an interesting phenomena, where for Priority variable, we see the following instructions:
mov eax,1
mov [rbp+120h+var_F0], ax
For this we will need to reference #Track3_1.c, we know that Priority variable is declared as a short Priority which is 16bit or 2 bytes. EAX is 4 bytes or 32bits, and in this case, the initial value of Priority is transmitted to the EAX register first before 2 bytes from EAX, AX is being copied to the stack.
Quiz¶
Q1. Please find the size of Arg1 field of SimpleContext Structure. How many bytes is it?
Ans : 4
Q2. To initialize the Priority field of SimpleLambda structure the compiler firstly puts 1 in EAX general purpose register, but then takes only AX part of it. Why?
Ans : Priority field has short data type - two bytes long. The AX has the same length and compiler used this part of the register in instruction
Q3. You could easily find the initial values for Arg1 and Arg2 on your program’s stack. Could you do the same for the initial value for Priority?
Ans : Yes, the initial value for Priority 1 the compiler decided to transmit through the general purpose register, not the stack, but anyway the resulting local var is on a stack
Track3_2¶
This track aims to show how it looks like in assembly when there is a function call in assembly and also that pointer can point to pointers and pointing can be nested.
Track3_2.c¶
struct SimpleContext{
int Arg1;
int Arg2;
};
struct SimpleLambda{
void *pCallback;
short Priority;
struct SimpleContext *pContext
};
int SimpleCallback(struct SimpleContext *Context){
Context->Arg1 = 14;
return 0;
}
int main(){
struct SimpleContext c;
c.Arg1 = 10;
c.Arg2 = 12;
struct SimpleLambda l;
l.pCallback = simpleCallback;
l.priority =1;
l.pContext = &c;
}
Track3_2.exe¶
We have the following assembly.
We follow what we did for the first binary, where we import local types SimpleContext and SimpleLambda. However, this time we will attempt to utilise the local type SimpleLambda. To do so, we will need to identify Priority variable as we already did above, and follow it in the stack.
As can be seen above, the stack structure closely resembles that of the one in SimpleLambda. (We can refer to our source code too).
Therefore, we can guess that var_F8 is likely the pointer to callback variable. As such, we can apply the structure starting from var_F8.
Shown above is the disassembly after all the renaming and we can see above that after adding all the structures, and renaming, the disassembly is resembles the actual code.
Quiz¶
Q1. Find the real start address of SimpleCallback function in track3_2.exe. Type your answer in the 0xXXXXXXXXX format without the initial zeros.
Ans : 0x140011770
Q2. Which fields of SimpleLambda structure are pointers?
Ans : pCallback and pContext fields are pointers, they contain addresses
Q3. The pContext field in SimpleLambda structure is the pointer to SimpleContext custom data type. Could SimpleContext in its turn contain some fields with pointers?
Ans : Yes, sure, C allows pointers to pointers to pointers and so on, it’s not a problem
Track3_3¶
Track3_3 attempts to show us the difference in disassembly when "shallow copying" is done vs "deep copying". Shallow copying is seen where variable l is directly assigned to variable l2 and deep copying is seen in the later part of the code where memcpy() is used to copy the value of l to l2.
In the case of shallow copying, we see that what was copied was simply the address/pointers of the values(values in l2 are simply pointed from l) which might not be ideal whereas for "deep copying" the address/pointers are not copied but instead different from each other. The values are stored in different addresses, even though they are the same value.
Track3_3.c¶
struct SimpleContext{
int Arg1;
int Arg2;
};
struct SimpleLambda{
void *pCallback;
short Priority;
struct SimpleContext *pContext
};
int SimpleCallback(struct SimpleContext *Context){
Context->Arg1 = 14;
return 0;
}
int main(){
struct SimpleContext c;
c.Arg1 = 10;
c.Arg2 = 12;
struct SimpleLambda l;
l.pCallback = SimpleCallback;
l.Priority = 1;
l.pContext = &c;
struct SimpleLambda l2 = l;
printf("new lambda priority val: %d and address: 0x%x\n", l2.Priority, &l2.priority);
printf("old lambda priority val: %d and address: 0x%x\n", l.Priority, &l.priority);
printf("new lambda context address: 0x%x\n", l2.pContext);
printf("old lambda context address: 0x%x\n", l.pContext);
l2.pContext = malloc(sizeof(struct SimpleContext));
memcpy(l2.pContext,l.pContext,sizeof(struct SimpleContext));
printf("new lambda context address: 0x%x\n", l2.pContext);
printf("old lambda context address: 0x%x\n", l.pContext);
printf("new lambda context arg1: %d\n", l2.pContext->Arg1);
}
Track3_3.exe¶
Below is the function renamed initially, with the knowledge we gained till track3_2.
Shallow Copy


As can be seen before rep movsb, the function looks exactly, like what is happening at the source code. Another thing we can notice here, when renaming are instructions :
mov rdi, rax
mov rsi, rcx
rep movsb
rdi is the destination index and rsi, source index. Before rep movsb which means copying of bytes repeatedly from source to destination is executed.
We trace the register rcx since the value in it is moved into rsi and we see above that the address of simpleLambda is loaded into rcx which is then loaded into rsi.
rdi is loaded from rax which is loaded from var_F8 and 0x18 (hex) is moved into ecx before rep movsb is executed. From here it seems like ecx is being used as a counter. The above operation is the copying of values in simpleLambda l to simpleLambda l2.
Therefore, we can deduce that var_F8 is the address for the new lambda.
Deep Copy
In deep copy below,

The disassembly below is a nicer representation after the renaming and also going to the stack and giving l2 the simpleLambda structure.
Shallow Copy

Deep Copy

Quiz¶
Q1. Inside the binary find the copying of a SimpleLambda structure. How many bytes will be taken to the new structure?
Ans : 24
Q2. Would such a default way of C copying pose some possible problems to us?
Ans : Yes, the value fields would be good, but all the pointer fields would still reference the same memory addresses. So in this case changing would affect both structures - original and copied one
Q3. Please locate in the resulting executable the malloc() call you added for deep copying. How many bytes does it allocate for SimpleContext and why?
Ans : 8 bytes, you provided sizeof(struct SimpleContext) as argument and SimpleContext contains just 2 fields 4 bytes each