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