4

I have encountered a quite obscure 32-bit ELF file (that is a crackme) and I still cannot figure out how can it execute. First, beside some "understandable" property that it has not any section:

# readelf --sections SimpleVM

There are no sections in this file.

Considering the segments:

# readelf --segments SimpleVM

Elf file type is EXEC (Executable file)
Entry point 0xc023dc
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00c01000 0x00c01000 0x013c7 0x013c7 RWE 0x1000
  LOAD           0x00019c 0x0804b19c 0x0804b19c 0x00000 0x00000 RW  0x1000

I observe that the first segments LOAD has size 0x13c7 bytes, and mapped into memory at 0xc01000; the second one is not important because its size is zero. But, the entry point of the ELF file is at 0xc023dc, that means outside any segment LOAD!!!

I use also IDA 6.8 (evaluation ver.) to load this file, and IDA says that the entry point is illegal.

Since the program has no INTERP segment, the first executed instruction must be at 0xc023dc. But this address is outside any "reliably" mapped data, we cannot sure which instruction will be executed. I think that this ELF should have some random behaviors (e.g. it should be crashed usually), but it is not, it executes normally, without any crash.

So my question is: how can this happen?

NB1. In case of someone wants to look at this file, I give the link here, but please do not give directly the solution. I want to handle it myself.

NB2. Using a Pintool to trace out what happens, I find the OEP of the program is at 0xc01dfa since its trace is:

0xc023dc  mov dword ptr [0xc01bf0], 0x252e8 <=== modify address 0xc01bf0
0xc023e6  jmp 0xc01bf0
0xc01bf0  call 0xc01e47                     <=== modified instruction
0xc01e47  pop ebp                           <=== OEP
0xc01e48  call 0xc01dfa
0xc01dfa  pop esi
0xc01dfb  lea eax, ptr [ebp-0x9]
0xc01dfe  mov edi, dword ptr [eax]
0xc01e00  sub eax, edi
0xc01e02  mov edx, eax
0xc01e04  add eax, dword ptr [eax+0x48]
0xc01e07  add eax, 0xfff
0xc01e0c  and eax, 0xfffff000
0xc01e11  push 0x1
0xc01e13  push eax
....

But I still cannot understand why the instruction at 0xc023dc is always mov dword ptr [0xc01bf0], 0x252e8 (so the binary is somehow "self-modified")

  • 1
    File/memory mappings are always multiples of the page size, on x86 this is usually 4k. The mapping length here, 0x13c7 will be rounded up to a multiple of the page size meaning that 0x2000 bytes will be mapped. If you look the raw file at offset 0x13dc you should find these 'extra' instructions. – Ian Cook Sep 18 '15 at 6:45
  • Thanks a lot @IanCook, this is exactly what you say. Indeed, at 0x13dc is the opcode (10 bytes) of the first instruction. I do not know that the mapping size should be rounded up to page size. – Ta Thanh Dinh Sep 18 '15 at 7:47
  • ...then the OS will copy more data from LOAD segments regardless of theirs size in the header. Would you mind let your comment as an answer? – Ta Thanh Dinh Sep 18 '15 at 7:55
5

File/memory mappings are always multiples of the page size, on x86 this is usually 4k. The mapping length here, 0x13c7 will be rounded up to a multiple of the page size meaning that 0x2000 bytes will be mapped. If you look the raw file at offset 0x13dc you should find these 'extra' instructions. The rounding up to page size is necessary because the memory manager and processor page tables work on 4k granularity to reduce the overhead of memory management.

There is self-modification going on too. It is the write to memory from the instruction at 0xc023dc which creates the CALL (0xE8) instruction you see at address 0xC01BF0. This won't be in the raw file. The write to the code is possible because the code is, unusually, mapped with write (W) access in the program header.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.