Here is how to solve the problem with any of the masm32 library command line procedures.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
comment * -----------------------------------------------------
Build this template with
"CONSOLE ASSEMBLE AND LINK"
----------------------------------------------------- *
.code
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
call main
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
main proc
.if len(rv(GetCommandLine)) > 128
print "Warning, Some idiot is trying a stack overflow exploit.",13,10
ret
.endif
cls
print "Hello World",13,10
ret
main endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
:cheekygreen:
Hee hee hee! :) :U
Funny! :bg
Let's just hope no one takes it seriously and believe it's actually a good solution! :bdg
As Quasimodo says, I expect nobody will take that seriously.
Ramon
Most buffer overflow exploits involve user inputted data to a buffer that is not controlled in its length so if you set up a CGI on the web or have a very high exposure app then it may be vulnerable to someone feeding it more than the buffer allows. This situation is very simple to fix, limit the length but of course there will be other forms of buffer overflow exploits.
I would be interested to hear solutions to other cases.
Quote from: QvasiModo on May 05, 2005, 08:52:44 PM
Funny! :bg
Let's just hope no one takes it seriously and believe it's actually a good solution! :bdg
I don't get it. What is so funny? Why is it not a good solution?
I think QvasiModo meant that the resulting "protection" would be easy to defeat by an experienced cracker?
Personally I find "safeguarding" my executables to be harder than optimizing them. Optimization has its limits - but there's no end to a cracker's infatuation. :(
No, I just don't think that limiting command line handling to 128 bytes is good solution, from a design point of view -- I'd just drop the GetCL function entirely. It
does fix the problem, the code posted by Hutch is not exploitable at all (no matter how experienced you are).
QuotePersonally I find "safeguarding" my executables to be harder than optimizing them. Optimization has its limits - but there's no end to a cracker's infatuation. :(
So do I :) that's why I prefer string handling functions to enforce text limits (instead of the caller), as well as having those limits configurable (instead of hardcoded into the function).
Quasimodo,
The GetCL version was written on a win9x box and is dated in 1999 where later OS versions document 32 thousand character command line limits so what I will do when I get the time is produce a table based version for the later OS versions. I could not think of a way to get around checking the command line length but I was interested if anyone else knew of a way to do it as its worth pubishing any tricks like this if it helps.
Quote from: hutch-- on May 06, 2005, 10:30:47 PM
The GetCL version was written on a win9x box and is dated in 1999...
Ok, now I understand the text size limit.
Quote...where later OS versions document 32 thousand character command line limits so what I will do when I get the time is produce a table based version for the later OS versions. I could not think of a way to get around checking the command line length but I was interested if anyone else knew of a way to do it as its worth pubishing any tricks like this if it helps.
My two cents: how about leaving this one for backwards compatibility, adding the command line length check inside the function (to secure existing apps without having to change their code), and writing a new function? :)
i am inclined to leave these old ones as they are and do a new one when I get the time. The design I had in mind was one that uses spaces, tabs and commas as delimiters and handles quoted text as well which is a bit more like programming parsing than command line parsing but I think it will work OK.
There are two choices, my preferred one is to grab each argument as the old ones do as it means you can spot arg 1 to the end in one call but there is another option that takes a little more parsing that is no big deal to do which is to rewrite the buffer so that each argument is zero terminated and load the sum total of addresses into an array of pointers. The new comand line limit of 32 thousand characters can be bashed very quickly if it is done right although I doubt that there is a lot of use for this capacity.
Hutch,
That is a great idea. I was just looking at GetCL.asm and it looks like a very well written utility. Since most of my apps expect a path in the commandline I have the luxury of using PathGetArgs in the shlwapi library. Did you ever notice that this particular library is just chock full of 'path' stuff? It might help you in writing your new version.
Anyway, nice utility,
Paul
Making users perform the check on their own is not a fix, it's a workaround :bg.
The first time I brought up the problem (http://www.old.masmforum.com/viewtopic.php?t=3549) (about a year ago), I posted a simple command line parser which creates the C style argv[] array of pointers to command line arguments. Perhaps some of the ideas could be of use in creating the next version?
Either way, I think it's important to fix the problem at the source no matter how many new similar functions are added to the library. And it would not change the functionality other than apps will no longer crash when given too long a command line :green.
Quote from: hutch-- on May 06, 2005, 11:56:28 PM
i am inclined to leave these old ones as they are and do a new one when I get the time. The design I had in mind was one that uses spaces, tabs and commas as delimiters and handles quoted text as well which is a bit more like programming parsing than command line parsing but I think it will work OK.
There are two choices, my preferred one is to grab each argument as the old ones do as it means you can spot arg 1 to the end in one call but there is another option that takes a little more parsing that is no big deal to do which is to rewrite the buffer so that each argument is zero terminated and load the sum total of addresses into an array of pointers. The new comand line limit of 32 thousand characters can be bashed very quickly if it is done right although I doubt that there is a lot of use for this capacity.
I have a FASM routine that goes half that way: turns a given ASCIIZ string into an ASCIIZ array (strings one next to the other, ends with a double NULL). Traversing the array forward is fairly trivial, and the code size is small. I also think speed is not important, even with 32k command lines (which are going to be rare anyway). I can post it here if you want to take a look. :)
Jibz,
What if the user wants a 32 character limit ? use5r defined methods are more flexible, especially when you are dealing with assembler programmers.
QvasiModo,
Thanks for the offer, it will be worth having a look at. I am currently working on the version that grabs a single argument if it exists as I find this type more useful but the other design is also very useful for very long command lines. About the only problem I see is knowing how large to make the array to hold the pointers to each word start on the command line. Probably a user defined limit with the array is the safest way to go as the user should have some idea of the maximum number of arguments that the program will accept.
I think it would be a bad idea to stuff another one with some static limit into the library. The user doesn't always know a fixed limit either, e.g. build tools must handle any number of obj files you throw at them.
I would prefer some design that will allow any length and number of command line arguments :U.
Jibz,
The reference material says there is an absolute limit of 32k characters so there cannot be such a thing as an unlimited command line length.
Quote from: hutch-- on May 11, 2005, 01:47:00 PM
Jibz,
The reference material says there is an absolute limit of 32k characters so there cannot be such a thing as an unlimited command line length.
There could be in the future, though. Or the limit could be expanded again. It's probably better to be on the safe side and let the user decide the limit, IMHO.
My apologies for taking so long to post, had some problems with my computer back home.
Here's the code, it's FASM syntax, should be easy to port and adapt to the winapi calling convention. I'd also add some code to get the commandline string, make a copy (allocated with HeapAlloc), and resize the memory object at the end of the routine.
;-----------------------------------------------------------------------------
; Command line parser by QvasiModo.
; Turns the string pointed to by EBX into an ASCIIZ array in place.
; Recognizes parameters enclosed in double quotes as a single token.
; Destroys EBX, ESI y EDI.
; NOTE: The GetCommandLine API returns a string that should be
; considered READ-ONLY. Make a copy of it before calling this proc.
tokenizar:
mov edi, ebx
.token_espacios: ; Skip spaces between tokens.
movzx eax, byte [ebx]
add ebx, 1
test eax, eax
jz .token_fin
cmp eax, ' '
je .token_espacios
cmp eax, '"'
je .token_comillas
.token_palabra: ; Search for the end of a word.
lea esi, [ebx - 1]
@@: movzx eax, byte [ebx]
add ebx, 1
test eax, eax
jz .token_copiar
cmp eax, ' '
jne @b
jmp .token_copiar
.token_comillas: ; Search for the end of a quoted string.
mov esi, ebx ; Special case: "" is ignored.
movzx eax, byte [ebx]
add ebx, 1
cmp eax, '"'
je .token_espacios
test eax, eax
jz .token_fin
@@: movzx eax, byte [ebx]
add ebx, 1
test eax, eax
jz .token_copiar
cmp eax, '"'
jne @b
.token_copiar: ; Copy a token in [EDI].
mov byte [ebx - 1], 0 ; [ESI] -> beginning, [EBX - 1] -> end.
xor eax, eax
@@: mov al, byte [esi]
mov byte [edi], al
add esi, 1
add edi, 1
cmp al, 0
jne @b
jmp .token_espacios
.token_fin: ; We're done!
mov byte [edi], 0
ret
Hope it's of any use! :)
Nice .. is there some way to escape a double quote to insert it into an argument?
I mean something like: "this arg with spaces contains \" characters"
No, I wrote it originally to receive filenames only. But it's a good idea. :)
Does anybody know, in the pointer that GetCommandLine returns, is the file executed ALWAYS surrounded in quotes, i.e.
00141EE0 22 43 3A 5C 44 57 47 2E 65 78 65 22 20 22 63 3A "C:\abc.exe" "c:
00141EF0 5C 70 72 6F 67 72 61 6D 20 66 69 6C 65 73 5C 66 \program files\f
00141F00 72 65 6C 6C 2E 65 78 65 22 20 2F 61 72 67 3A 77 rell.exe" /arg:w
00141F10 68 6F 61 00 AB AB AB AB AB AB AB AB EE FE EE FE hoa.««««««««îþîþ
Here "C:\abc.exe" is the file being debugged and its name is quoted, even though it doesn't have to be. Can I expect all versions of windows to behave the same way? Because it would be convenient to just scan for the 2nd instance of the quotation character to determine where the real arguments begin. Thanks!
Mark,
It appears to vary from OS version to OS version and about the only safe way I know of handling this variation is to deal with either quoted blocks or normal blocks of text at the same time.
"drv:\path\my app with spaces.exe" arg1 "quoted text" arg3 "more quoted" arg5
becomes
"drv:\path\my app with spaces.exe"
arg1
"quoted text"
arg3
"more quoted"
arg5
Interestingly, when ran from a console prompt instead of start-->run, the returned argument filename is not quoted and is lacking an extension, i.e.,
00141EE0 44 57 47 20 22 63 3A 5C 70 72 6F 67 72 61 6D 20 abc "c:\program
00141EF0 66 69 6C 65 73 5C 66 72 65 6C 6C 2E 65 78 65 22 files\frell.exe"
00141F00 20 2F 61 72 67 3A 77 68 6F 61 00 AB AB AB AB AB /arg:whoa.««««««
I'm assuming that either one of these two behaviors can be expected on most versions of windows... because parsing this is a requirement of DWG, hopefully I've implemented a tiny and lightweight method in the DWG project:
http://www.masm32.com/board/index.php?topic=5317.0
It's easy to deal with. While parsing, every time you encounter a quote, toggle a bit/byte/flag somewhere. If the flag is set, spaces are part of the argument; if the flag is clear, spaces delimit the arguments. Then you can check for quotes surrounding a parameter and remove them if desired.
Cheers,
Zooba :U