I suppose many of us know this structure:
IMAGE_OPTIONAL_HEADER32 STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode DWORD ?
SizeOfInitializedData DWORD ?
SizeOfUninitializedData DWORD ?
AddressOfEntryPoint DWORD ?
BaseOfCode DWORD ?
BaseOfData DWORD ?
ImageBase DWORD ?
...
Below a screenshot from PeView. What I don't understand are the entries AddressOfEntryPoint and BaseOfCode.
When looking at the exe through the eyes of Olly, it appears that the codebase is 401000h, not 1A6000h.
The entry point is at 5B6410h, not 1B6410h.
Adding ImageBase+SizeOfInitializedData, I get 401000h, the point where my code really starts.
What am I missing?
Quote from: jj2007 on June 18, 2017, 07:48:25 PM
What am I missing?
May be someone will be more comprehensive, I will only add these points:
1) ImageBase may not be where the image actually loads in memory. Odds are it will not be with ALSR.
2) BaseOfCode is beginning of code section relative to ImageBase
3) AddressOfEntryPoint - Starting address when execution begins.
Gary Nebbett, (remember his book?) posted a nice application of these concepts here (https://groups.google.com/forum/#!msg/comp.os.ms-windows.programmer.win32/Md3GKPc279A/Ax3bYgXhpD8J)
Finding the Entry Point (EP) in memory and disk (http://en.redinskala.com/finding-the-ep/)
Thanks, José and Erol :icon14:
It turns out that codebase is "wrong" because the exe is UPX-ed. So it really starts at the exotic address 5B6410h, but only to decompress itself. Afterwards, codebase is 401000h, i.e. the usual 400000h plus the initialised data. Hmpffff ::)
Quote from: jj2007 on June 18, 2017, 08:30:08 PM
Thanks, José and Erol :icon14:
It turns out that codebase is "wrong" because the exe is UPX-ed. So it really starts at the exotic address 5B6410h, but only to decompress itself. Afterwards, codebase is 401000h, i.e. the usual 400000h plus the initialised data. Hmpffff ::)
Not usual unless you disabled ALSR in the Registry or the program was built without dynamic codebase. In your case, probably UPX always creates space at that address to uncompress.
Right. Now I stumbled over an issue that gives me a real headache:
Dump of file ImageBaseBuglink614.exe
PE signature found
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
14C machine (i386)
3 number of sections
5946727A time date stamp Sun Jun 18 14:30:50 2017
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
10F characteristics
Relocations stripped
Executable
Line numbers stripped
Symbols stripped
32 bit word machine
OPTIONAL HEADER VALUES
10B magic #
5.12 linker version
5400 size of code
B3600 size of initialized data
0 size of uninitialized data
1000 RVA of entry point
1000 base of code
7000 base of data
400000 image base
1000 section alignment
200 file alignment
4.00 operating system version
0.00 image version
4.00 subsystem version
0 Win32 version
BB000 size of image
400 size of headers
0 checksum
3 subsystem (Windows CUI)
0 DLL characteristics
100000 size of stack reserve
1000 size of stack commit
100000 size of heap reserve
1000 size of heap commit
Everything is perfect except B3600 size of initialized data. SizeOfImage is 765952 dec, also ridiculously high.
Can't be. The exe is 27648 bytes only ::)
Now this is cute: After a lot of googling I found a book (https://books.google.it/books?id=nJ7VAwAAQBAJ&lpg=PA530&ots=d99rDnMhHz&dq=image_optional_header%20%22SizeOfInitializedData%22&pg=PA531#v=onepage&q=image_optional_header%20%22SizeOfInitializedData%22&f=true) written by a certain Kaspersky...
Note the "including senseless" ::)
Size of initialized data is frequently wrong. The file will load with any value you put there. Sizeofimage is a consequence.
QuoteKaspersky
The virus guy ;)
Quote from: aw27 on June 18, 2017, 11:54:57 PM
Size of initialized data is frequently wrong. The file will load with any value you put there. Sizeofimage is a consequence.
Yep. Fortunately some other values are valid ;)
And I managed to translate them into a new macro, @MbRet, see attachment:
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
CodeBelow PROTO :DWORD, :DWORD, :DWORD
CodeAbove proc uses edi hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar:DWORD, LocBuffer[260]:BYTE
ClearLocals
push esi ; esi to stack
lea esi, LocBuffer
nop
push eax ; ouch, we are not good at counting pushes and pops!
pop esi ; restore the stack
@MbRet
CodeAbove endp
Has2Args proc uses esi edi ebx mode, arg2
; no locals here
push eax
mov ecx, mode
pop eax
push eax ; one push too much
@MbRet
Has2Args endp
Has3Args proc uses ecx mode, arg2, arg3
push eax
mov ecx, mode
pop eax
pop eax ; one pop too much
@MbRet
Has3Args endp
Init invoke CodeAbove, 11111111h, 22222222h, 33333333h, 44444444h
invoke CodeBelow, 11111111h, 22222222h, 33333333h
invoke Has3Args, 12345678h, 22222222h, 33333333h
invoke Has2Args, 12345678h, 22222222h
Inkey "ok?"
Exit
CodeBelow proc uses esi edi ebx ecx edx eax arg1, arg2, arg3
LOCAL MyLocVar:DWORD
LOCAL LocBuffer[40h]:BYTE
ClearLocals ; optional: zero-initialise local variables (not with uses in proc head)
lea esi, LocBuffer ; whatever
pop eax ; ouch, again we are not good at counting pushes and pops!
@MbRet
CodeBelow endp
EndOfCode
Output:stack error in line 11, (push-pop)=-1
stack error in line 43, (push-pop)=1
line 27: retaddr 12345678 is above code segment
@MbRet generates just an ordinary
ret for usedeb=0. So for testing, set usedeb=1 and see what happens. I have applied it to RichMasm, over 130
rets are now active observers of stack corruption, at least in my debug version :P
Warning: Do not use for Windows callback functions. It won't crash, but no warnings either. Own procedures without a stack frame will issue a warning and crash directly afterwards, as expected.
May be I should have checked better, probably is part of the MasmBasic already, but I could not find the new macro. :shock:
Yes it is, version 19 June (http://masm32.com/board/index.php?topic=94.0). If you are really curious, I grant you an exclusive personal license to put an int 3 before the @MbRet in the source attached above :P
Quote from: jj2007 on June 19, 2017, 05:17:31 PM
I grant you an exclusive personal license to put an int 3 before the @MbRet in the source attached above :P
Oh, thank you I owe you a beer. :badgrin:
Red wine, please :biggrin:
Note that you need a console. RichMasm auto-detects a console program, but if you have a message loop etc, you can force it with OPT_Susy Console somewhere in the source.
If the push count is higher than the pop count, you may see this output:
line 1573: retaddr 0018FE48 is a local address (too many pushes?), below code segment
If the pop count is higher than the push count, you will probably see the first argument that you passed to the proc, in hex notation:
line 1573: retaddr 00000005 is below code segment
Finally, for checking a large source,
- make a backup
- load it in RichMasm
- press Ctrl F
- check the Case and FW boxes (=full word)
- type ret**endp (this finds all occurrences of ret <crlf> endp)
- warning: if you have the habit of using ret 3*DWORD, it won't work; retn 3*DWORD will be OK because of the FW
- hit Alt R (like Replace)
- type @MbRet in the replace box
- select the matches you want to replace
- put the cursor in the replace box and hit Return
Afterwards, try if it still builds (it should). Then put useMbRet=1 near the top of the source (this activates the macro), and run your program. It is a runtime check, so only procs that are actually in use may trigger error messages.
Go and play with an extra push or pop and an int 3 before the @MbRet ;)
Remember that with useMbRet=0, no extra code will be generated - just the usual ret before the endp.
Thank you so much! :t