Does this mean that all segments share the same space, and that the program's code and data are all together in one big happy segment?
If so, then assuming I have space set aside for it, it also means that I should be able to put new machine code in that space at run time, and then run it, right?
Hi CCurl,
Yes, code and data are sharing the same space. From \masm32\help\asmintro.chm :
Quote
Flat memory Model
A program written in native 32 bit Windows format is created in what is called FLAT memory model which has a single segment that contains both code and data. The programs must be run on a 386 or higher processor.
Differing from earlier 16 bit code that used combined segment and offset addressing with a 64k segment limit, FLAT memory model works only in offsets and has a range of 4 gigabytes. This makes assembler easier to write and the code is generally a lot faster.
All segment registers are automatically set to the same value with this memory model and this means that segment / offset addressing must NOT be used in 32 bit programs that run in 32 bit Windows.
For programmers who have written code in DOS, a 32 bit Windows PE executable file is similar in some respects to a dos COM file, they have a single segment that can contain both code and data and they both work directly in offsets, neither use Segment / Offset addressing.
The defaults in flat-model programs are NEAR code addressing and NEAR data addressing within the range of 4 gigabytes.
The FS and GS segment registers are not normally used in application programs but are used in some instances by the operating system.
You must be careful while modifying your executable ( = put new machine code at run time ) This can trigger the antivirus software and this type of coding is more difficult to maintain.
So I did a little test ...
I created a codeBuffer BYTE 100 (0) and filled it in with some simple machine code to MOV a number (0) into EDX (ba 00 00 00 00), with a RET (c3) at the end.
Then I loaded the address of that buffer into EBX and executed CALL EBX (ff d3).
0xC0000005: Access violation.
sigh
Keep in mind that as of Win XP, Microsoft introduced DEP (Data Execution Prevention) to restrict certain types of exploits. You need to allocate memory and specify that it is read/write/execute to do what you are after.
deleted
This works on Win7-64:
include \masm32\include\masm32rt.inc
.code
destination db "Surprise.." ; the MessageBox code will go here
start:
mov edi, offset destination
push edi
mov esi, offset MyMB
.Repeat
movsb
.Until esi>=MyMB_end
pop edi
int 3 ; for the debugger
call edi
exit
MyMB:
mov esi, MessageBox
push MB_OK
push chr$("Copied:")
push chr$("Hello World")
push 0
call esi ; MsgBox 0, "Hello World", "Copied:", MB_OK
retn
MyMB_end:
end start
The linker commandline must contain /SECTION:.text,rwe
In case you are using RichMasm, insert this line under "end start", then hit F6:
OPT_DebugL /SECTION:.text,RWE
Otherwise, use a batch file.
P.S.: Before & after:
destination 53 db 53 ; CHAR 'S'
00401001 75 db 75 ; CHAR 'u'
00401002 72 db 72 ; CHAR 'r'
00401003 70 db 70 ; CHAR 'p'
00401004 72 db 72 ; CHAR 'r'
00401005 69 db 69 ; CHAR 'i'
00401006 73 db 73 ; CHAR 's'
00401007 65 db 65 ; CHAR 'e'
00401008 2E db 2E ; CHAR '.'
00401009 2E db 2E ; CHAR '.'
<ModuleEntryPoint> /$ BF 00104000 mov edi, destination ; ASCII "Surprise..¿"
0040100F |. 57 push edi
00401010 |. BE 28104000 mov esi, 00401028
00401015 |> A4 movsb
00401016 |. 81FE 3E104000 cmp esi, MessageBoxA ; jmp to user32.MessageBoxA
0040101C |.^ 72 F7 jb short 00401015
0040101E |. 5F pop edi
0040101F |. FFD7 call near edi
destination BE 40104000 mov esi, MessageBoxA ; jmp to user32.MessageBoxA
00401005 6A 00 push 0
00401007 68 00304000 push offset ??0019 ; ASCII "Copied:"
0040100C 68 08304000 push offset ??001A ; ASCII "Hello World"
00401011 6A 00 push 0
00401013 FFD6 call near esi
00401015 C3 retn
00401016 81FE 3F104000 cmp esi, 0040103F
0040101C ^ 72 F7 jb short 00401015
0040101E 5F pop edi
0040101F CC int3
00401020 FFD7 call near edi ; NewMasm3.destination
00401022 6A 00 push 0
00401024 E8 1D000000 call ExitProcess ; jmp to kernel32.ExitProcess
00401029 BE 40104000 mov esi, MessageBoxA ; jmp to user32.MessageBoxA
0040102E 6A 00 push 0
00401030 68 00304000 push offset ??0019 ; ASCII "Copied:"
00401035 68 08304000 push offset ??001A ; ASCII "Hello World"
0040103A 6A 00 push 0
0040103C FFD6 call near esi
MessageBoxA C3 retn
You might have a closer look at address 00401015 :biggrin:
An even weirder example is attached. Open it in Olly, hit F8 until VirtualProtect is passed, then continue with F7 (single step). From time to time, select a few lines and hit Ctrl A to force Olly to update the disassembly.
again, i would avoid special build command lines
use VirtualProtect - we covered this in one of your first threads....
http://masm32.com/board/index.php?topic=4664.msg50217#msg50217 (http://masm32.com/board/index.php?topic=4664.msg50217#msg50217)
http://masm32.com/board/index.php?topic=4664.msg50251#msg50251 (http://masm32.com/board/index.php?topic=4664.msg50251#msg50251)
Sorry about seemingly asking the same question again, but it felt a little different to me ... in the other thread, the question was more centered around putting machine code into dynamically allocated memory, whereas dynamic memory allocation wasn't part of this question. But at the end of the day, is sounds like the issue, and the way to address it, are the same.
I've only been at this a couple of weeks, so I'm still green about alot of things. Thanks for your patience.
Quote from: dedndave on October 29, 2015, 12:44:40 PM
again, i would avoid special build command lines
use VirtualProtect
Thats' what I did - no special command line needed:
Quote from: jj2007 on October 29, 2015, 07:52:09 AMAn even weirder example is attached. Open it in Olly, hit F8 until VirtualProtect is passed, then continue with F7 (single step). From time to time, select a few lines and hit Ctrl A to force Olly to update the disassembly.
no problem - i just thought maybe you had forgotten the first thread - lol
OK ... so ...
memorySize EQU 1024*1024*8
call GetProcessHeap
invoke HeapAlloc, eax, 0, memorySize
mov ebx, eax
invoke VirtualProtect, ebx, memorySize, PAGE_EXECUTE_READWRITE, edx
GetProcessHeap sets EAX to a non-ZERO value, presumably the heap handle.
HeapAlloc sets EAX to a different non-ZERO value, presumably the pointer to the memory it allocated.
VirtualProtect sets EAX (and EDX) to ZERO, which the documentation makes me think it probably failed.
- it also sets ECX to an apparently random number.
Please advise.
I advise you to post full code with headers, so that we can run it with a debugger.
there are a few points here, to be aware of
call GetProcessHeap
invoke HeapAlloc, eax, 0, memorySize
mov ebx, eax
invoke VirtualProtect, ebx, memorySize, PAGE_EXECUTE_READWRITE, edx
first, when you call GetProcessHeap, save the results for later
i generally do this as part of program initializaion (i use a global dword labeled hHeap, in the .DATA? section)
even if you don't allocate other blocks, you will need it to free the block :P
second, is the last argument for the VirtualProtect call
if you want to save the previous attribute for the memory block, it should be a pointer to the variable that receives it
otherwise, it should be NULL (NULL EQU 0 is in windows.inc)
the contents of EDX at that time are undefined
i don't really see a need for it, as i don't see a rule that says it has to be restored prior to freeing the block
finally, the use of the EBX register is a little unknown for us - we don't know the context of the code
this is why Jochen mentioned posting the entire code, so we can get the context
if it's inside a PROC, where EBX has been preserved, it's ok
if it's in the main procedure, it's ok
as an added note, i assume you save the block address in a global :biggrin:
Unfortunately, I did it last night at home, and I am at work now.
I just tried installing MASM on my work PC, and it is giving me that "You appeared to have cancelled the installation." message ... the other thread I just started. I was going to re-do it here at work and post the entire ASM file, but if I can't get MASM to install, I'm kind of stuck.
But that is essentially the entire contents of the program, save the call to ExitProcess. I made a totally barebones project to try out the calls, and that is all there is in the main proc.
HeapAlloc has a much lower granularity (8 bytes?) than VirtualAlloc (4K page) but using VirtualProtect will change the attributes of a whole page, not just a small range within a page. The remarks for VirtualProtect warn against using HeapAlloc.
QuoteIt is best to avoid using VirtualProtect to change page protections on memory blocks allocated by GlobalAlloc, HeapAlloc, or LocalAlloc, because multiple memory blocks can exist on a single page. The heap manager assumes that all pages in the heap grant at least read and write access.
Something else to look out for
QuoteWhen protecting a region that will be executable, the calling program bears responsibility for ensuring cache coherency via an appropriate call to FlushInstructionCache once the code has been set in place. Otherwise attempts to execute code out of the newly executable region may produce unpredictable results.
I was able to get something to build and run without installing MASM.
In this, VirtualAlloc returns EAX=0, so of course VirtualProtect will also not be successful.
; TEST program
; ---------------------------------------------------------------------------------------------------------
.686P
.model flat, stdcall
.stack 4096
; ---------------------------------------------------------------------------------------------------------
; Constants
memorySize EQU 1024*8
PAGE_READWRITE EQU 0004h
PAGE_EXECUTE EQU 0010h
PAGE_EXECUTE_READWRITE EQU 0040h
MEM_COMMIT EQU 010000h
; ---------------------------------------------------------------------------------------------------------
.data
hHeap DWORD ?
theMem DWORD ?
; ---------------------------------------------------------------------------------------------------------
.code
ExitProcess PROTO, dwExitCode:dword
GetProcessHeap PROTO ; Get the current process heap handle
HeapAlloc PROTO,
hHeap:DWORD, ; handle to private heap block
dwFlags:DWORD, ; heap allocation control flags
dwBytes:DWORD ; number of bytes to allocate
HeapFree PROTO,
hHeap:DWORD, ; handle to heap with memory block
dwFlags:DWORD, ; heap free options
lpMem:DWORD ; pointer to block to be freed
VirtualProtect PROTO,
pMem:DWORD, ; pointer to the memory
dwSize:DWORD, ; size of area to protect
fNewProtect:DWORD, ; new protection
pOldProtect:DWORD ; pointer to storage for old protection value
VirtualAlloc PROTO,
pMem:DWORD, ; optional, NULL lets the system figure it out
dwSize:DWORD, ; requested size
fAllocType:DWORD, ; allocation type
fProtect:DWORD ; protection flags
; ---------------------------------------------------------------------------------------------------------
main proc
; call GetProcessHeap
; mov hHeap, eax
; invoke HeapAlloc, hHeap, 0, memorySize
; invoke HeapFree, hHeap, 0, theMem
invoke VirtualAlloc, 0, memorySize, MEM_COMMIT, PAGE_READWRITE
mov theMem, eax
invoke VirtualProtect, theMem, memorySize, PAGE_EXECUTE, 0
invoke ExitProcess,0
main endp
end main
; ---------------------------------------------------------------------------------------------------------
when you set the attribute for VirtualProtect, use PAGE_EXECUTE_READWRITE so that you can write the code in there
PAGE_EXECUTE_READWRITE EQU 40h
VirtualAlloc will probably handle that attribute constant, so that you don't have to use VirtualProtect
also - we typically list PROTO's first in the source - they don't need to be in an opened section
New ASM file ... do you get a value back in EAX from your VirtualAlloc call? Mine comes back with ZERO.
; TEST program
; ---------------------------------------------------------------------------------------------------------
.686P
.model flat, stdcall
.stack 4096
; ---------------------------------------------------------------------------------------------------------
ExitProcess PROTO,
dwExitCode:DWORD
GetProcessHeap PROTO ; Get the current process heap handle
HeapAlloc PROTO,
hHeap:DWORD, ; handle to private heap block
dwFlags:DWORD, ; heap allocation control flags
dwBytes:DWORD ; number of bytes to allocate
HeapFree PROTO,
hHeap:DWORD, ; handle to heap with memory block
dwFlags:DWORD, ; heap free options
lpMem:DWORD ; pointer to block to be freed
VirtualProtect PROTO,
pMem:DWORD, ; pointer to the memory
dwSize:DWORD, ; size of area to protect
fNewProtect:DWORD, ; new protection
pOldProtect:DWORD ; pointer to storage for old protection value
VirtualAlloc PROTO,
pMem:DWORD, ; optional, NULL lets the system figure it out
dwSize:DWORD, ; requested size
fAllocType:DWORD, ; allocation type
fProtect:DWORD ; protection flags
; ---------------------------------------------------------------------------------------------------------
; Constants
memorySize EQU 1024*8
PAGE_READWRITE EQU 0004h
PAGE_EXECUTE EQU 0010h
PAGE_EXECUTE_READWRITE EQU 0040h
MEM_COMMIT EQU 010000h
; ---------------------------------------------------------------------------------------------------------
.data
hHeap DWORD ?
theMem DWORD ?
; ---------------------------------------------------------------------------------------------------------
.code
main proc
; call GetProcessHeap
; mov hHeap, eax
; invoke HeapAlloc, hHeap, 0, memorySize
; invoke HeapFree, hHeap, 0, theMem
invoke VirtualAlloc, 0, memorySize, MEM_COMMIT, PAGE_EXECUTE_READWRITE
mov theMem, eax
invoke VirtualProtect, theMem, memorySize, PAGE_EXECUTE_READWRITE, 0
invoke ExitProcess,0
main endp
end main
; ---------------------------------------------------------------------------------------------------------
VirtualAlloc returns the address of the allocated block
if it returns 0, it means there is an error; call GetLastError to get the error code
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887%28v=vs.85%29.aspx)
QuoteTo reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.
Attempting to commit a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL lpAddress fails unless the entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS.
in ASM, we use the OR operator
MEM_COMMIT or MEM_RESERVE
by the way, 8KB allocation is very small
you could put that much (and more) in the .DATA? section, easily
Whoops ... one too many ZEROs on the MEM_COMMIT constant. Now I get a non-ZERO number back from VirtualAlloc.
Yeah, 8K is just for the test, trying 8MB now.
It WORKS!!!
; TEST program
; ---------------------------------------------------------------------------------------------------------
.686P
.model flat, stdcall
.stack 4096
; ---------------------------------------------------------------------------------------------------------
ExitProcess PROTO,
dwExitCode:DWORD
GetLastError PROTO
VirtualAlloc PROTO,
pMem:DWORD, ; optional, NULL lets the system figure it out
dwSize:DWORD, ; requested size
fAllocType:DWORD, ; allocation type
fProtect:DWORD ; protection flags
; ---------------------------------------------------------------------------------------------------------
; Constants
memorySize EQU 1024*1024*8
PAGE_READWRITE EQU 0004h
PAGE_EXECUTE EQU 0010h
PAGE_EXECUTE_READWRITE EQU 0040h
MEM_COMMIT EQU 01000h
MEM_RESERVE EQU 02000h
; ---------------------------------------------------------------------------------------------------------
.data
hHeap DWORD ?
theMem DWORD ?
; ---------------------------------------------------------------------------------------------------------
.code
main proc
invoke VirtualAlloc, 0, memorySize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE
mov theMem, eax
test eax, eax
jnz doTest
call GetLastError
jmp allDone
doTest:
mov ebp, theMem
inc ebp
mov byte ptr [ebp], 00bah
mov DWORD ptr [ebp][1], 11223344h
mov byte ptr [ebp][5], 00c3h
call ebp
; EDX should get 11223344h in it
allDone:
invoke ExitProcess,0
ret
main endp
end main
; ---------------------------------------------------------------------------------------------------------