PicoCTF Writeup – asm2
# Information:
CTF Name: PicoCTF
CTF Challenge: asm2
Challenge Category: Reverse Engineering
Challenge Points: 250
PicoCTF 2019.
# Challenge Description:
What does asm2(0xb,0x2e) return? Submit the flag as a hexadecimal value (starting with ‘0x’). NOTE: Your submission for this question will NOT be in the normal flag format. Source
Hint: assembly conditions
# Writeup
In this challenge, we have the source code of an assembly code snippet. The file name is test.S and below you can see the code snippet:
asm2:
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: sub esp,0x10
<+6>: mov eax,DWORD PTR [ebp+0xc]
<+9>: mov DWORD PTR [ebp-0x4],eax
<+12>: mov eax,DWORD PTR [ebp+0x8]
<+15>: mov DWORD PTR [ebp-0x8],eax
<+18>: jmp 0x509 <asm2+28>
<+20>: add DWORD PTR [ebp-0x4],0x1
<+24>: sub DWORD PTR [ebp-0x8],0xffffff80
<+28>: cmp DWORD PTR [ebp-0x8],0x63f3
<+35>: jle 0x501 <asm2+20>
<+37>: mov eax,DWORD PTR [ebp-0x4]
<+40>: leave
<+41>: ret
As I said this is assembly code, I already solved a similar challenge regarding assembly, it might be similar, let’s try it.
To understand the program I decided to go line by line.
<+0>: push ebp
<+1>: mov ebp,esp
We know that the asm1(0xb,0x2e) is being put into the stack. This happens due to these two first lines. The first line pushes asm2 into ebp and then this value is moved into esp.
After these two instructions, the current stack state is as such:
0xc | 0x2e | which is equal to saying: ebp+0xc |
---|---|---|
0x8 | 0xb | which is equal to saying: ebp+0x8 |
0x4 | ret | which is equal to saying: ebp+0x4 |
0x0 | ebp |
Knowing this helps us a lot. Now the next instruction:
<+3>: sub esp,0x10
In this instruction, we are simply allocating space for local variables, and the new stack is:
0xc | 0x2e | which is equal to saying: ebp+0xc |
---|---|---|
0x8 | 0xb | which is equal to saying: ebp+0x8 |
0x4 | ret | which is equal to saying: ebp+0x4 |
0x0 | ebp | |
-0x4 | local1 | which is equal to saying: ebp-0x4 |
-0x8 | local2 | which is equal to saying: ebp-0x8 |
-0xc | local3 | which is equal to saying: ebp-0xc |
-0x10 | local4 | which is equal to saying: ebp-0x10 |
Next we have:
<+6>: mov eax,DWORD PTR [ebp+0xc]
<+9>: mov DWORD PTR [ebp-0x4],eax
<+12>: mov eax,DWORD PTR [ebp+0x8]
<+15>: mov DWORD PTR [ebp-0x8],eax
<+18>: jmp 0x509 <asm2+28>
Let’s go over each instruction of this block of code one at a time:
- First, we are assigning to the eax register the value in ebp+0xc which is 0x2e, according to the stack.
- Next, we move the value in the eax register to ebp-0x4, which in our case is local1, making it equal to 0x2e.
- Afterward, we move the contents ebp+0x8 to eax, making it equal to 0xb.
- In line +15 we move the contents of the eax register to ebp-0x8, which is the local2 variable, making it equal to 0xb.
- Finally, we jump to asm2+28.
The current stack state is:
0xc | 0x2e | which is equal to saying: ebp+0xc |
---|---|---|
0x8 | 0xb | which is equal to saying: ebp+0x8 |
0x4 | ret | which is equal to saying: ebp+0x4 |
0x0 | ebp | |
-0x4 | 0x2e | which is equal to saying: ebp-0x4 |
-0x8 | 0xb | which is equal to saying: ebp-0x8 |
-0xc | local3 | which is equal to saying: ebp-0xc |
-0x10 | local4 | which is equal to saying: ebp-0x10 |
Let’s move on to the next instruction:
<+28>: cmp DWORD PTR [ebp-0x8],0x63f3
<+35>: jle 0x501 <asm2+20>
As you know, the previous instruction made us jump to asm2+28, which is a comparison, followed by a jump if less or equals to. In this case, we have a comparison between ebp-0x8, (which we know is local2 and it is equal to 0xb) and 0x63f3. We know that 0xb is less than 0x63f3 so we take the jump to asm2+20.
<+20>: add DWORD PTR [ebp-0x4],0x1
<+24>: sub DWORD PTR [ebp-0x8],0xffffff80
In line +20 we have an add instruction, we are adding 1 to ebp-0x4, making it 0x2f.
In line +24 we have a sub instruction, we are subtracting 0xffffff80 to ebp-0x8. This is 0x86 architecture so we have to truncate the result, to do so I simply performed this operation in python:
local2 = (local2 - 0xffffff80) & 0xffffffff
The new value for local2 is: 0x8b
Moving on to the next line we are once again in +28, and we know that 0x8b is less than 0x63f3, therefore we will take the jump was again. To facilitate the process I created a python 3 script that is a translation to python 3 of this assembly code, see below:
'''
Stack:
[ local4 ] <--- ebp-0x10
[ local3 ] <--- ebp-0xc
[ local2 ] <--- ebp-0x8
[ local1 ] <--- ebp-0x4
[ ebp ]
[ ret ] <--- ebp+0x4
[ arg1 ] <--- ebp+0x8
[ arg2 ] <--- ebp+0xc
'''
#We know that asm2 receives two arguments
def asm2(arg1, arg2):
#asm2:
#<+0>: push ebp
#<+1>: mov ebp,esp
#<+3>: sub esp,0x10
#<+6>: mov eax,DWORD PTR [ebp+0xc]
eax = arg2
#<+9>: mov DWORD PTR [ebp-0x4],eax
local1 = eax
#<+12>: mov eax,DWORD PTR [ebp+0x8]
eax = arg1
#<+15>: mov DWORD PTR [ebp-0x8],eax
local2 = eax
#<+18>: jmp 0x509 <asm2+28>
#<+20>: add DWORD PTR [ebp-0x4],0x1
#<+24>: sub DWORD PTR [ebp-0x8],0xffffff80
#<+28>: cmp DWORD PTR [ebp-0x8],0x63f3
#<+35>: jle 0x501 <asm2+20>
while(local2 <= 0x63f3):
local1 = (local1 + 1) & 0xffffffff #This truncates the result to 32 bits.
local2 = (local2 - 0xffffff80) & 0xffffffff #This truncates the result to 32 bits.
'''
It is necessary to truncate the restuls because in python does not have
buffer overflow but 0x86 can have so we have to truncate it.
'''
#<+37>: mov eax,DWORD PTR [ebp-0x4]
#<+40>: leave
#<+41>: ret
return hex(local1)
print(asm2(0xb, 0x2e))
To create the script I simply translated as I did before but to the python 3 syntax. After running the code we get the flag:
The Python 3 script source code can be found here.
Thank you very much for reading!
Cheers,
MRegra