* Andy Champ:
Alf,
I think you'll find that code has been in read-only segments since NT
3.1.
I think you're right (not that I'm entirely wrong, though!
).
A different segment selector is used for the code segment, and it appears to be
very read-only...
But it's like a door with a huge lock, placed in the middle of an open room. No
problem walking around that door instead of trying to walk through it. For as it
was, and still is in Windows XP at least, CS::blah (code segment offset blah)
and DS:blah (data segment offset blah) map to the same address.
So it's no problem executing code in the data segment, e.g. for the purpose of
infecting a machine via a buffer overrun.
What I should have written, instead of "having", was that Windows, or any OS,
should have /required/ code to be in read-only execute-only segment, i.e. not
mapping code and data segment to the same linear address range.
Code below -- sorry about rusty assembler skills! -- exemplifies executing
data as code, which as I remarked (on "trampoline") also has benign uses:
<code language="Microsoft MASM">
..386
..MODEL flat, stdcall
STD_OUTPUT_HANDLE EQU -11
GetStdHandle PROTO NEAR32 stdcall,
nStdHandle
WORD
WriteFile PROTO NEAR32 stdcall,
hFile
WORD, lpBuffer:NEAR32, nNumberOfBytesToWrite
WORD,
lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32
ExitProcess PROTO NEAR32 stdcall,
dwExitCode
WORD
..STACK 4096
..DATA
msg DB "Yay!", 13, 10
written DW 0
hStdOut DD 0
pureData DB 512 DUP (0) ; 1/2 KB array
..CODE
getN:
mov eax, 'D'
ret
nBytesCode EQU $ - getN
_start:
; Directly modify the code in the little function -- gah!
mov ebx, offset getN
;mov byte ptr [ebx+1], 'N' ; This would crash, it's read-only.
; Instead copy the little function to data buffer.
mov esi, offset getN
mov edi, offset pureData
mov ecx, nBytesCode
rep movsb
; Modify the copy.
mov byte ptr [pureData+1], 'N'
; Mess up the stack and call the copy.
push offset changeZeMessage ; return address
push offset pureData ; jump here (roundabout 'cause i'm rusty in
asm!)
ret ; Calling dynamically generated function in pure data
changeZeMessage:
mov byte ptr [msg], al ; Message now changed to "Nay!"
doTell:
INVOKE GetStdHandle,
STD_OUTPUT_HANDLE ; Standard output handle
mov [hStdOut], eax
INVOKE WriteFile,
hStdOut, ; File handle for screen
NEAR32 PTR msg, ; Address of string
LENGTHOF msg, ; Length of string
NEAR32 PTR written, ; Bytes written
0 ; Overlapped mode
INVOKE ExitProcess,
0 ; Result code for parent process
PUBLIC _start
END
</code>
<result>
C:\cppfn\test> ml /nologo /c /coff flat.asm
Assembling: flat.asm
C:\cppfn\test> link /nologo flat.obj kernel32.lib /entry:_start /subsystem:console
C:\cppfn\test> flat
Nay!
C:\cppfn\test> _
</result>
On a more modern computer, or perhaps in Vista (above was in XP Prof), I imagine
this program will simply crash.
But of old, in Windows NT family, no problem executing data as code, and that's
what I was thinking of -- but it sort of garbled on the way from now seldom
used sort of machine code level part of brain to keyboard... :-(
What has come in recently is DEP - no-execute data segments, which
is especially important for the stack.
Uhm, yes, sounds like it, except that I had the impression that this is at the
/page/ level, not at the segment level?
The code-separate-from-data may well have been designed for Harvard
architecture machines.
Yes.
I don't know the term "trampoline" outside gymnastics.
See e.g. <url:
http://en.wikipedia.org/wiki/Trampoline_(computers)>.
It's not complete article.
Basic machine code level trampoline in C++ programming is just like the
generated routine in program example above: it's dynamically generated machine
code that puts some hardwired value in function result register (here eax),
typically that's a pointer to a class type object, and jumps to some static
routine that in turn calls some given member function on that object. You
generate the trampoline and gives its address to a function that calls back on
the trampoline. And voilà, the call-back is forwarded to your object.
Cheers & thanks! & also hth.
,
- Alf