i was just wondering about the registers and what is typically in them at startup. i remember seeing in another thread that windows XP machines usually have eax = 0 and ebx containing the address of the PEF (?) (whatever that is). is there any significance to the other values? (like "edx contains the address to the calling program")
this eventually leads to my next question, are those register values necessary for a program to function properly. i am trying to write a little "program supplement" thing that will get the arguments passed to the program, process them, and have it available to the user program (kind of like what c/c++ programs do with the argc/argv values). should i save the registers at the beginning when i process the arguments and restore them for the "main program" or are they expendable?
then, is there a way to make variables in a program/library available to other programs without having to declare them in the other program? (gotta get argc/argv to the main program preferredly global)
my final question, unreleated. if you had a procedure that did not contain a prologue and epilogue, should the values of esi (and possibly edi) change whenever they are accessed. a while ago, i was experimenting with procedures and the PROLOGUE:NONE and EPILOGUE:NONE options. a simple "length of a null-terminated string" procedure that took in one argument (the address of the string). so (i think) it looked something like this:OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
StringLength PROC STDCALL, String:PTR BYTE
MOV esi,String
XOR ecx,ecx
.WHILE BYTE PTR [esi] != 0
INC ecx
INC esi
.ENDW
MOV eax,ecx
RET
StringLength ENDP
then when i was debugging this program, i found that when it went to "INC esi", esi turned to some funky number. should that have happened? i understand the concept of a stack frame and how the procedure usually refers to procedural arguments and local variables are referenced relative to the ebp register, but from what i see, it has nothing to do with that.
Let me answer your final question because I think it is more interesting :toothy
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
Means that masm will not automatically create the stack frame or remove the stack for you. You only use that if you have no use for the stack frame and want to save some bytes.
In your example, you used "mov esi, string" where string is a parameter for your function. MASM will interpret the variable "string" as mov esi, [ebp+8]. As you haven't set up the frame stack, the value in ebp at [ebp+8] obviously does not contain your parameter. Your parameter is still found in your stack (esp). So to access your parameter, you should use mov esi, [esp+4] instead.
Do read my article on the stack if you are interested at http://win32asmcommunity.net/phpwiki/index.php?pagename=TheStack
thanks roctiv. i am pretty confident in my knowledge of the stack but i will definitely be reading that. ;) i guess i was hoping that MASM would have done the appropriate changes to the reference (but it was expecting me to set up the frame :D). i think i made a break after the loop and it was some big number (definitely larger than my string). mov esi, string probably moved a 2 into esi and took a looooooong time to find a null. :)
PS since there is no epilogue, the procedure is using the STDCALL calling convention, and there is an argument, i would have to clean up the stack as well huh? (the possible solution to the crashes :D)
Yes, you should be using "retn 4*numberofparameters" instead of ret.
Btw it is always good to load it up in a debugger (like Ollydbg) and figure out what's wrong. It's a great way to learn new things.
Jeff,
You used ESI so it needs to be preserved as well.
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
StringLength PROC STDCALL, String:PTR BYTE
push esi
MOV esi,[esp+8] ; fixed with thanks to Victor.
XOR ecx,ecx
.WHILE BYTE PTR [esi] != 0
INC ecx
INC esi
.ENDW
MOV eax,ecx
pop esi
RET 4
StringLength ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
You normally set the prologue and epilogue back to default after unless you want any following code to have the same characteristic.
You should ideally define String as :DWORD because the only thing that :PTR BYTE does is allow you to skip out the byte ptr when doing a mov mem,immed which is confusing.
Once you get it working, have a go at coding your own loop rather than using .while.
Hutch,
In your code, it should be [esp+8] instead. That's the problem with not using a stack frame - you tend to create unwanted crashes due to carelessness.
I think that MASM should have the option of making prologue and epilogue code ebp-less. Is there a "suggestion box" on the Microsoft website?
Victor,
> In your code, it should be [esp+8] instead. That's the problem with not using a stack frame - you tend to create unwanted crashes due to carelessness.
You are right, I added the push/pop ESI after. :P Stack should be corrected for changes due to ESI with PUSH. Main observation was the algo should be coded with one register but with two it would have saved the problem using ECX or EDX.
The problem is neither using or not using a stack frame, its not calculating the change in ESP.
Quote from: AeroASM on June 20, 2005, 12:18:44 PM
I think that MASM should have the option of making prologue and epilogue code ebp-less. Is there a "suggestion box" on the Microsoft website?
Yes, there is (http://lab.msdn.microsoft.com/ProductFeedback/makesuggestion/search.aspx), but I much doubt they will support it.
Petroizki already made "ebp-less" macro set (http://www.masmforum.com/simple/index.php?topic=1063.0). I will release the plugin one day to simplify the syntax, hopefully.
Wow that is really a difficult task. How come I did not get to see the thread?
:( It seems that only a few coders saw the thread, or almost nobody needs "ebp-less" procedures - there is no feedback.
Maybe I have missed something here but as you can already write procedures without a stack frame in MASM using the OPTION PROLOGUE/EPILOGUE syntax, I am not sure what the gain is. They tend to be used when the algo design needs the extra register EBP and there is a minor stack overhead gain by doing so.
thanks for your input guys. that helped a bunch. now would anyone happen to know about the other questions? :D
Hi,
At startup there are two registers that have significance. EAX and EBX both contain interesting values.
EBX on NT based systems contains a pointer to the PEB (Process Environment Block) that contains various useful information about the program that is running. For example it's heap handle, parent PID, command line and many other tid bits. Because the pointer is only set on NT based systems you can use it as a quick way to determine the OS family for API's such as PSAPI, which are only available in the NT family. EBX will always contain 00530000h on 9x systems.
Note that you can also obtain the PEB pointer using fs mov eax,[30h]
EAX will contain the application entry point on 9x systems, on XP systems it will contain 10000000h, NULL on all other NT versions.
Here is a partially completed list of the PEB/PDB
TIB STRUCT
pvExcept DD // 00h Head of exception record list
pvStackUserTop DD // 04h Top of user stack
pvStackUserBase DD // 08h Base of user stack
; NT only
SubSystemTib DD // 0Ch
FiberData DD // 10h
; 9X & NT
pvArbitrary DD // 14h Available for application use
ptibSelf DD // 18h Points to the structure itself ???
; NT only
unknown1 DD // 1Ch
processID DD // 20h
threadID DD // 24h
unknown2 DD // 28h
pvTLSArray DD // 2Ch Thread Local Storage array
; NT only
pPEB DD // 30h Pointer to Process Environment Block (PDB)
ENDS
PDB STRUCT
header DD 2 DUP (?) // 00 Kernel object header
module DD // 08 Main exe module (NT)
LdrData DD // 0c Pointer to loader information
ProcessParameters DD // 10 Process parameters
unknown1 DD // 14 Unknown
heap DD // 18 Default process heap
mem_context DD // 1c Process memory context
dwflags DD // 20 Flags
pdb16 DD // 24 DOS PSP (pointer)
PSP_sel DW // 28 Selector to DOS PSP
imte DW // 2a IMTE for the process module
threads DW // 2c Number of threads
running_threads DW // 2e Number of running threads
free_lib_count DW // 30 Recursion depth of FreeLibrary calls
ring0_threads DW // 32 Number of ring 0 threads
system_heap DD // 34 System heap to allocate handles
task DD // 38 Win16 task
mem_map_files DD // 3c Pointer to mem-mapped files (pointer)
env_db DD // 40 Environment database (pointer)
handle_table DD // 44 Handle table (pointer)
parent DD // 48 Parent process (pointer)
modref_list DD // 4c MODREF list (pointer)
thread_list DD // 50 List of threads (pointer)
debuggee_CB DD // 54 Debuggee context block (pointer)
local_heap_free DD // 58 Head of local heap free list (pointer)
unknown2 DD // 5c Unknown
crit_section DD // 60 Critical section
unknown3 DD 3 DUP (?) // 78 Unknown
console DD // 84 Console (pointer)
tls_bits DD 2 DUP (?) // 88 TLS in-use bits
process_dword DD // 90 process dword (??)
group DD // 94 Process group (pointer)
exe_modref DD // 98 MODREF for the process EXE (pointer)
top_filter DD // 9c Top exception filter (pointer)
priority DD // a0 Priority level
heap_list DD // a4 Head of process heap list
heap_handles DD // a8 Head of heap handles list (pointer)
unknown4 DD // ac Unknown
console_provider DD // b0 Console provider (??)
env_selector DD // b4 Selector to process environment
error_mode DD // b6 Error mode
load_done_evt DD // b8 Event for process loading done
UTState DD // bc Head of Univeral Thunk list
unknown5 DD // c0 Unknown (NT)
locale DD // c4 Locale to be queried by GetThreadLocale (NT)
ENDS
That's very handy info Donkey, thanks! :)
Later: Hmm, EAX == 0 upon start for me (XP).
EBX = 7FFDE000h, some seemingly valid data there. Wonder if one of those bytes can be used as an OS version flag. Perhaps someone more knowledgable can expand on this:
; make as "console build and link"
include masm32rt.inc ; "runtime" libs
.data
sz9x BYTE "Win9x",0
szNT BYTE "WinNT",0
szXP BYTE "WinXP",0
.code
start:
cmp eax, 10000000h ; WinXP?
jz WinXP
test eax,eax ; WinNT?
jz WinNT
print addr sz9x ; must be 9x here
jmp endit
WinXP:
print addr szXP
jmp endit
WinNT:
print addr szNT
jmp endit
endit:
print chr$(" detected!",13,10)
mov eax, input("Press enter to exit")
invoke ExitProcess, 0 ; exit gracefully
end start
Quote from: hutch-- on June 20, 2005, 11:35:47 PM
Maybe I have missed something here but as you can already write procedures without a stack frame in MASM using the OPTION PROLOGUE/EPILOGUE syntax, I am not sure what the gain is. They tend to be used when the algo design needs the extra register EBP and there is a minor stack overhead gain by doing so.
In MASM, the only user-friendly (I mean, no hardcoded offsets) way to access procedure locals and arguments is via EBP, i.e. standard stack frame. When you suppress this standard frame, the only way to access them is via ESP with hardcoded offsets:
mov [esp+8],eax ; store result to localvar2
Now, when you add some new PUSH + POP, you have to revise all appropriate offsets:
push ecx
mov [esp+8+4],eax ; store result to localvar2
pop ecx
That's quite annoying, especially when the procedure is not a few lines of source code long. You can also often forgot to revise some of them.
And that's the gain of pmacros: you get rid of those revisions, because the offset is revised automatically:
mov [localvar2],eax ; will be assembled to [ESP+8]
_push ecx
mov [localvar2],eax ; will be assembled to [ESP+8+4]
_pop ecx
I, personally, don't like those underlined macros, like _push or _pop, and that's why I code that "plugin".
im getting EAX = 00000000 EBX = 7FFD7000. so information on my program could be accessed by using the address pointed to by EBX at start? so i take it i should at least preserve EBX during processing. ok, ill do that unless anyone else could shed the light on the important registers. :)
another question:
this involves determining what are arguments and what arent. in general, individual arguments are seperated by (any number of) spaces and paired quotes.
within a quoted argument, if there are leading spaces in the argument, are those spaces included with the argument or ignored?
i remember reading something on this in the msdn library but cant remember which topic it was. so if you ran a program with this command:
[cmd]program.exe "what the!?" yabba da"bba d"o! "dd "a" ood ao [/cmd]
(there is a space at the end)
the arguments should be interpreted as:
arg0 [arg]program.exe[/arg]
arg1 [arg]what the!?[/arg]
arg2 [arg]yabba[/arg]
arg3 [arg]da[/arg]
arg4 [arg]bba d[/arg]
arg5 [arg]o![/arg]
arg6 [arg]dd [/arg]
arg7 [arg]a[/arg]
arg8 [arg] ood ao [/arg]
right?
or are the quoted arguments' leading/trailing spaces ignored?
Quote from: Jeff on June 22, 2005, 08:22:09 AM
im getting EAX = 00000000 EBX = 7FFD7000. so information on my program could be accessed by using the address pointed to by EBX at start? so i take it i should at least preserve EBX during processing. ok, ill do that unless anyone else could shed the light on the important registers. :)
AFAIK this EBX thing is documented nowhere so noone should rely on it. You can get the PEB address at fs:[30h], so there's no need to preserve EBX IMO.
Quote from: Jeff on June 22, 2005, 08:22:09 AM
[cmd]program.exe "what the!?" yabba da"bba d"o! "dd "a" ood ao [/cmd]
(there is a space at the end)
the arguments should be interpreted as:
arg0 [arg]program.exe[/arg]
arg1 [arg]what the!?[/arg]
arg2 [arg]yabba[/arg]
arg3 [arg]da[/arg]
arg4 [arg]bba d[/arg]
arg5 [arg]o![/arg]
arg6 [arg]dd [/arg]
arg7 [arg]a[/arg]
arg8 [arg] ood ao [/arg]
right?
I think it's right.
Quote from: Jeff on June 22, 2005, 08:22:09 AM
or are the quoted arguments' leading/trailing spaces ignored?
nope!!
hey guys, more arguments questions.
what is the maximum amount of arguments an application can take at the command line?
i currently wrote it so it would take in a maximum of 10 additional arguments but i want to know my absolute limit.
and
what is the maximum size of a line on the command line?
i currently have a buffer of magnitude 208 bytes. so thats 208 bytes available for the argument strings and null terminators. again, i want to know my limit.
[edit]
one more: i noticed that when i made a main procedure without arguments (tho i havent tested with procedures in general), it did not create a new stack frame. is this normal? i thought that for all "formal procedures" were created with a new stack frame unless given alternate prologues/epilogues.
thanks guys for being so helpful