Log in

View Full Version : RCE exercise for beginners


niaren
December 27th, 2010, 17:30
Beginners will maybe find this difficult and I assume experienced reversers find this simple and uninteresting.
Attached is a small exe. The exe is very simple it prints one line to the console. The exe has been edited in a hex editor in such a way that you will see, if you open it in your favorite disassembler, the code obfuscated right at the entry point.

Problem 1: Why does the code appear obfuscated in a disassembler (only tested with IDA)? what is the idea?
Problem 2: How does it work?

To be honest, I don't think I understand completely every detail of how it works myself

Darkelf
December 27th, 2010, 20:38
Hmmm,

I tried really hard, but I can't see any obfuscation.
The code is short (well the function to get the length of the string could be really shorter), simple and there is nothing hidden in the exe.
I least I don't see anything like that.
This is what I see:

Code:

00011000 PUSH hello.00013000 ; ASCII "Hey, this actually works."
00011005 CALL hello.00011014
0001100A PUSH 0
0001100C CALL <JMP.&kernel32.ExitProcess>
00011011 INT3
00011012 INT3
00011013 INT3
00011014 PUSH EBP
00011015 MOV EBP,ESP
00011017 ADD ESP,-0C
0001101A PUSH -0B
0001101C CALL <JMP.&kernel32.GetStdHandle>
00011021 MOV DWORD PTR SS:[EBP-4],EAX
00011024 PUSH DWORD PTR SS:[EBP+8] ; hello.<ModuleEntryPoint>
00011027 CALL hello.00011050 ; call to a function that returns the length of the string
0001102C MOV DWORD PTR SS:[EBP-C],EAX
0001102F PUSH 0 ; structure (not needed here - hence 0)
00011031 LEA EAX,DWORD PTR SS:[EBP-8]
00011034 PUSH EAX ; buffer
00011035 PUSH DWORD PTR SS:[EBP-C] ; length of string
00011038 PUSH DWORD PTR SS:[EBP+8] ; the string
0001103B PUSH DWORD PTR SS:[EBP-4] ; handle for stdout
0001103E CALL <JMP.&kernel32.WriteFile>
00011043 MOV EAX,DWORD PTR SS:[EBP-8] ;bytes written
00011046 LEAVE
00011047 RETN 4
0001104A INT3
0001104B INT3
0001104C INT3
0001104D INT3
0001104E INT3
0001104F INT3
00011050 MOV EAX,DWORD PTR SS:[ESP+4] ; ntdll.7C920228
00011054 LEA EDX,DWORD PTR DS:[EAX+3]
00011057 PUSH EBP
00011058 PUSH EDI ; ntdll.7C920228
00011059 MOV EBP,80808080
0001105E MOV EDI,DWORD PTR DS:[EAX]
00011060 ADD EAX,4
00011063 LEA ECX,DWORD PTR DS:[EDI+FEFEFEFF]
00011069 NOT EDI ; ntdll.7C920228
0001106B AND ECX,EDI ; ntdll.7C920228
0001106D AND ECX,EBP
0001106F JNZ SHORT hello.000110AA
00011071 MOV EDI,DWORD PTR DS:[EAX]
00011073 ADD EAX,4
00011076 LEA ECX,DWORD PTR DS:[EDI+FEFEFEFF]
0001107C NOT EDI ; ntdll.7C920228
0001107E AND ECX,EDI ; ntdll.7C920228
00011080 AND ECX,EBP
00011082 JNZ SHORT hello.000110AA
00011084 MOV EDI,DWORD PTR DS:[EAX]
00011086 ADD EAX,4
00011089 LEA ECX,DWORD PTR DS:[EDI+FEFEFEFF]
0001108F NOT EDI ; ntdll.7C920228
00011091 AND ECX,EDI ; ntdll.7C920228
00011093 AND ECX,EBP
00011095 JNZ SHORT hello.000110AA
00011097 MOV EDI,DWORD PTR DS:[EAX]
00011099 ADD EAX,4
0001109C LEA ECX,DWORD PTR DS:[EDI+FEFEFEFF]
000110A2 NOT EDI ; ntdll.7C920228
000110A4 AND ECX,EDI ; ntdll.7C920228
000110A6 AND ECX,EBP
000110A8 JE SHORT hello.0001105E
000110AA TEST ECX,8080
000110B0 JNZ SHORT hello.000110B8
000110B2 SHR ECX,10
000110B5 ADD EAX,2
000110B8 SHL CL,1
000110BA SBB EAX,EDX ; ntdll.KiFastSystemCallRet
000110BC POP EDI ; kernel32.7C817077
000110BD POP EBP ; kernel32.7C817077
000110BE RETN 4
000110C1 INT3
000110C2 JMP NEAR DWORD PTR DS:[<&kernel32.ExitProcess>] ; kernel32.ExitProcess
000110C8 JMP NEAR DWORD PTR DS:[<&kernel32.GetStdHandle>] ; kernel32.GetStdHandle
000110CE JMP NEAR DWORD PTR DS:[<&kernel32.WriteFile>] ; kernel32.WriteFile


I hope I didn't miss the point.

Regards
darkelf

Kayaker
December 28th, 2010, 03:45
For me it looks like this in Olly. I don't know why that first call doesn't resolve like you got, Darkelf.

Code:

00011000 > $ 68 00300100 PUSH hello2.00013000 ; ASCII "Hey, this actually works."
00011005 E8 DB E8
00011006 . 0A000000 DD 0000000A

0001100A . 6A 00 PUSH 0 ; /ExitCode = 0
0001100C . E8 B1000000 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess



In IDA:

Code:

.text:B9961000 start:
.text:B9961000 68 00 30 01 00 push 13000h
.text:B9961005 E8 0A 00 95 B9 call near ptr 732B1014h
.text:B996100A 6A 00 push 0
.text:B996100C E8 B1 00 00 00 call ExitProcess



This is kind of interesting, not the "obfuscation" per se (a literal interpretation by the disassembler of E8 - Call near, relative, displacement relative to next instruction), but how the base relocations are implemented by the loader.

Unlike the more usual occurence where the preferred load address is lower than the actual (relocated) load address, this is the opposite situation. So the base relocation "delta" value is apparently subtracted rather than added to the offset to get the correct displacement.

Here is an article describing how to force a relocation by setting the preferred base address to any invalid user-mode address (as niaren has done

http://uninformed.org/index.cgi?v=6&a=3&p=2


I think this is a good little project to explore the workings of the Relocation Table, just going to leave it at that for now.

Darkelf
December 28th, 2010, 04:50
Quote:
[Originally Posted by Kayaker;88732]For me it looks like this in Olly. I don't know why that first call doesn't resolve like you got, Darkelf.

Code:

00011000 > $ 68 00300100 PUSH hello2.00013000 ; ASCII "Hey, this actually works."
00011005 E8 DB E8
00011006 . 0A000000 DD 0000000A

0001100A . 6A 00 PUSH 0 ; /ExitCode = 0
0001100C . E8 B1000000 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess



Oops, actually it looked that way in my Olly too.
I didn't count that as obfuscation since I come across this in Olly once in a while. So I almost automatically marked that area, did a right-click->binary copy->right-click again->binary paste - and all was well. I think it's quite common in Olly that code looks like this, especially when dealing with packed programms, but also with "normal" applications from time to time. I'm sorry if I confused anyone.
Thank you, Kayaker for the link. It looks like a good reading and I really need to learn more on this topic.

Regards
darkelf

aqrit
December 28th, 2010, 12:33
to de-obfuscation this with olly use the "Remove analysis from selection" option

niaren
December 29th, 2010, 16:21
Thanks Darkelf, Kayaker and aqrit for participating in this 'exercise' and for showing some interest. I hope you had some fun and learned something. At least I did

Kayaker, it is fascinating to witness how much you can squeeze out of an exe when just a few bits have been flipped As you already have guessed you hit the bulls eye with that link. Couldn't be more right on.

I don't think more people will post here so let me then end this by explaining, using my own words, how this relocation trick works and also outline how that exe was modified from the original.

I was searching for info about relocations when I found the paper Kayaker references above. I decided to experiment a little by going through the accompanying code and use it to modify my own exe. I recommend doing this because there is quite much to read between the lines in that paper which will be clear when trying out the trick on a real exe. Anyway, I thought that a good way to share this would be to make a very simple exe file with a few 'obfuscated' places in and post it here.

If an exe file has a preferred image base address in the high end of the memory map, then the windows loader relocates the exe file to an address in the low end of the memory map. The trick works because it is possible to predict that the image will be relocated to address 0x00010000 just as you see in the Olly dumps above. If a different address is chosen for some reason then the exe will crash. Just to repeat what Kayaker already have mentioned then the loader computes a Delta value as the difference between expected image base address and the preferred image base address. The Delta value is added to addresses in the image that are absolute because those addresses are not correct after the image is relocated. Those places are specified in the .reloc section. It is then possible to take advantage of the fact that we know that the loader will add the Delta value to the places specified in the .reloc section. This can be used to make the image look obfuscated when the exe is loaded at its preferred image base address and this is what you see in IDA.

So if
- an exe file has a preferred image base address in the high end of the memory map (trick does not work on dlls because it is hard to predict the expected image base address)
- the exe file looks like garbage when opened in a disassembler
- the exe looks fine when opened in a debugger
- and there is no unpacking stub to see

Then there is a pretty good chance that the relocation trick is used to 'pack' the file. If you want to see the trick used to its full potential then just open the challenge.exe file that comes with the paper

The hello.exe file comes from the masm32 tutorials. It is the first console tutorial (demo1). First the exe was modified using the code from the paper to rebase the code at a high end preferred image base address (I believe this could also have been achieve using some linker option). The exe file has 4 addresses that needs to be updated with the Delta value after relocation. This can most easily been seen by doing dumpbin /relocation hello.exe at the prompt.
Dump of file hello.exe

File Type: EXECUTABLE IMAGE

BASE RELOCATIONS #4
1000 RVA, 10 SizeOfBlock
1 HIGHLOW
C4 HIGHLOW
CA HIGHLOW
D0 HIGHLOW

Summary

1000 .data
1000 .rdata
1000 .reloc
1000 .text

As you can see RVA 1001 is going to be updated with the Delta value, this what 1 HIGHLOW means. Hello.exe was then modified by
- changing "1 HIGHLOW" to "6 HIGHLOW"
- changing manually the dword at RVA 1001 to 13000 which we know will be correct after relocation.
- changing dword at RVA 1006 by subtracting the Delta value, the loader will add it back again

Quote:

Darkelf:
Thank you, Kayaker for the link. It looks like a good reading and I really need to learn more on this topic.

If you're interested I have an idea for another mini-project based on the same paper (my middle name is 'lets-do-a-mini-project' ). It seems that relocations can be used to manipulate control flow of an exe. Just look at section A.6 in the paper. It is possible to overwrite the return address of the loader. Because I'm having my hands full already in another mini-project you would have to take the initiative. I would very much like to participate and support you the best I can

evaluator
December 31st, 2010, 16:38
letz 5% "ruin" your wishes: W9x system will relocate exefile to 0400000.

new tip: you can make more then one relocator for same offset.
do work on this new :P-roject

evaluator
December 31st, 2010, 16:41
more wierd puzzle for you:
can you duplicate PE-header as one of PE-section?!?!

niaren
January 1st, 2011, 15:06
Yes, there might be portability issues with this trick. If I remember correctly the author of paper mentions this.

Quote:
[Originally Posted by evaluator;88778]more wierd puzzle for you:
can you duplicate PE-header as one of PE-section?!?!


Do you think it's possible?

evaluator
January 1st, 2011, 15:25
yes. i'm not giving impossible missions.

what did you tried?

niaren
January 1st, 2011, 16:00
Quote:
[Originally Posted by evaluator;88778]more wierd puzzle for you:
can you duplicate PE-header as one of PE-section?!?!


Using my own words, is this what you mean:

Modify the relocation section part only of an exe such that when opened in IDA the PE header appears as being a section of the exe?

Can you make it more precise?

evaluator
January 2nd, 2011, 15:21
i mean: duplicate MZPE-header as one of PE-section. (not related to RELOCs).

BanMe
January 2nd, 2011, 16:09
is this at compile time??

evaluator
January 3rd, 2011, 03:09
no, manual.
add section & try..

niaren
January 3rd, 2011, 16:13

http://i51.tinypic.com/2d1kymu.jpg

http://i53.tinypic.com/15yzwqo.jpg



2390

2391



You like? 



PS





import sys

import pefile



pe = pefile.PE('watermark1.exe')

pe.add_section(&quot;PEHeader&quot;,pe.header,0xC0000040)

pe.write(filename='watermark1_after.exe')



evaluator
January 4th, 2011, 14:52
no.
did you truly not understood me?

in PE-header
add new section OR modify any existing(for example RELOC),
SO this PE-header will mapped in memory like DATA/CODE..

niaren
January 4th, 2011, 16:35
Seriously, I'm still not sure what you mean

I'll try again, is this what you mean

hello.exe
Code:

name voffset vsize roffset rsize
('.text' , '0x1000', '0xd4', '0x400', '0x200')
('.rdata', '0x2000', '0x80', '0x600', '0x200')
('.data' , '0x3000', '0x20', '0x800', '0x200')
('.reloc', '0x4000', '0x26', '0xa00', '0x200')


hello_weird.exe
Code:

name voffset vsize roffset rsize
('.text' , '0x1000', '0xd4', '0x400', '0x200')
('.rdata' , '0x2000', '0x80', '0x600', '0x200')
('.data' , '0x3000', '0x20', '0x800', '0x200')
('.reloc' , '0x4000', '0x26', '0xa00', '0x200')
('PEHeader', '0x5000', '0x400', '0x0' , '0x400')
23922393

evaluator
January 5th, 2011, 08:48
yes, that's what i mean.
now hurry & solve, or Santa will leave US

niaren
January 5th, 2011, 14:55
The problem related to Santa is already solved. He doesn't live in the US
I think I begin to understand the other problem now. Despite of the specification in the last section header

('PEHeader', '0x5000', '0x400', '0x0' , '0x400')

The header does not seem to be mapped into memory. Is this the problem?
If this is the problem is this then a solution

2394

evaluator
January 5th, 2011, 15:06
GOOD SOLVE!

BTW1: raw offset can be also 1
BTW2: "US" also means can "us"

so, Happi NY! (nu yorCk?!)

niaren
January 5th, 2011, 15:19
Happy NY to you too and thanks for proposing the puzzle.

Can we conclude that a raw offset of 0 is not allowed in the sense that the section doesn't get mapped into memory, however, the loader (windows) doesn't complain about it either. Any raw offset in the range 1 to FileAlignment-1 seems to be truncated (rounded downwards) to 0!

evaluator
January 6th, 2011, 08:54
yah, recently i realized:
probably i can not know what is CORRECT PE-file.

so RAW-offset & RAW-size both can be unaligned.
i meet such thing in malware.

niaren
January 6th, 2011, 16:18
It is the same with relocation blocks, the above paper says that they don't have to be aligned.

It is probably just a technicality but the system 'silently' rejects a raw offset of 0 when specified directly in the section header. However, it has no problem using a raw offset of 0 internally!?