flat assembler
Message board for the users of flat assembler.
Index
> Windows > strcat(GetLastError) |
Author |
|
bitshifter 20 Jul 2010, 18:23
Code: aaaa dd "AA",0 should be BYTE or TCHAR and not dword Code: aaaa db "AA",0 It takes a dword to point to it though... As for GetLastError, you should use SetLastError(0) to clear the last code before using it... And strcat, well win32 api has its lstrcat procedure. I prefer to use it when already implemented by win lib. It works for ASCII and unicode so its more robust. But i can see the need to roll your own whether it for practice or just for fun or special purpose. Edit: Here is one that Loco wrote which is small and has no length restrictions... http://board.flatassembler.net/topic.php?t=7063 |
|||
20 Jul 2010, 18:23 |
|
LocoDelAssembly 20 Jul 2010, 18:44
bitshifter wrote:
jumpex, since you're using win32ax.inc then you're already provided with the proc macro so, you can do this: Code: proc astrocat, src, dst mov edi,[dst] xor al,al or ecx, -1 ; This also takes 3 bytes. cld ; In general this is not required, by convention, anything that used std should use cld before leaving so the DF is always set to forward (all Windows API adhere to this convention, for instance). repne scasb dec edi mov esi, [src] cpy: lodsb stosb test al,al jnz cpy ret ; REQUIRED AND IT MUST BE WRITTEN ALL LOWERCASE, endp doesn't add it automatically endp To invoke the procedure you can either push the values and call or use "stdcall astrocat, source, destination" that does the same automatically. |
|||
20 Jul 2010, 18:44 |
|
bitshifter 20 Jul 2010, 19:01
LocoDelAssembly wrote:
MSDN says for each time you expect a meaningful result... Code: invoke SetLastError,0 call DoSomething invoke GetLastError ... invoke SetLastError,0 call DoSomethingElse invoke GetLastError _________________ Coding a 3D game engine with fasm is like trying to eat an elephant, you just have to keep focused and take it one 'byte' at a time. |
|||
20 Jul 2010, 19:01 |
|
jumpex 20 Jul 2010, 19:04
Thanks for pointing these out.
I've added SetLastError. Also tried with lstrcat, but I don't know how to pass [errm] to it. My strcat gets it like this - [errm]. But I'm not sure that's even right. As for the DD or DB - does it matter that much? Another question - how do I convert this mystical errm char* pointer to some variable I can use with my strcat, the strcat you gave me a link to or lstrcat? Again - WHY does my program work with any other error, but not with "no error"? (it concatenates right) The pieces of code aren't even linked. And lastly, do you see the "p" macro in my source? If I put that in my strcat macro like this: Code: macro astrocat dst,src { mov edi,dst xor al,al xor ecx,ecx dec ecx cld repne scasb dec edi mov esi,src cpy: lodsb push eax p pop eax stosb test al,al jnz cpy } ... it works. Pauses every time, but doesn't hang with ANY values. What's this black magic? |
|||
20 Jul 2010, 19:04 |
|
LocoDelAssembly 20 Jul 2010, 20:02
Well, after doing some testings seems that the problem is that you can't write the memory allocated by FormatMessageA, so one solution would be to allocate memory yourself or even reserve it in the data area like this:
Code: macro e { invoke GetLastError mov [errn],eax invoke FormatMessageA,FORMAT_MESSAGE_FROM_SYSTEM,0,[errn],0,errm,64*1024 - 1,0 astrocat errm,slen invoke MessageBoxA,0,errm,errt,0 } Code: errm rb 128*1024; errm dd 0 I hope I understand correctly that your problem was that the program was either not showing the message or even crashing (the latter was the problem I had here but it was working when running from OllyDbg). |
|||
20 Jul 2010, 20:02 |
|
jumpex 20 Jul 2010, 21:03
I am very thankful for your time, LocoDelAssembly.
But sadly, you have misunderstood my problem. Problem { The thing is, it works with any error number that's not a ZERO(0). "It works" -> concatenated and outputed with MessageBox without problems or crashes. But with a GetLastError return (stored in errn) equal TO 0, it crashes. } In these conditions (NULL in errn), it doesn't crash only if I add the magical "p" macro. I don't understand what the MessageBox call in the p macro changes. It doesn't even crash after debugging, if I choose to skip the exception (null memory reference). |
|||
20 Jul 2010, 21:03 |
|
LocoDelAssembly 20 Jul 2010, 21:54
It works here with errn==0 (but with the changes I provided).
Code: lea ebx,[errm] invoke FormatMessageA,FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,0,0,0,ebx,40000,0 mov ebx, [ebx] mov dword [ebx+44], '234' ; There is a '\0' char implicitly at the end invoke MessageBoxA,0,ebx,errt,0 It seems that we are having heap corruption here, but I don't understand why it happens, the documentation says that the parameter specifies the MINIMUM number of TCHARs (when FORMAT_MESSAGE_ALLOCATE_BUFFER is used, maximum size otherwise), but it seems that it ends up allocating exactly the amount needed for that message... The fact you see your problem solved when using that MessageBox inside astrocat may be just consequence of the unpredictible behaviour when corrupting the heap (apparently favorable in your case, but maybe if you exercise the heap a bit more you'll see new problems) The stack when 46 is used: Code: Call stack of main thread Address Stack Procedure / arguments Called from Frame 0007FF14 7E3A890D ? ntdll.RtlAllocateHeap USER32.7E3A8907 0007FF10 0007FF18 00090000 hHeap = 00090000 0007FF1C 00000008 Flags = HEAP_ZERO_MEMORY 0007FF20 00000064 HeapSize = 64 (100.) 0007FF28 7E3A8927 ? USER32.7E3A88F7 USER32.7E3A8922 0007FF24 0007FF40 7E3E6433 ? USER32.MBToWCSEx USER32.7E3E642E 0007FF3C 0007FF74 7E3D0877 USER32.MessageBoxTimeoutA USER32.7E3D0872 0007FF70 0007FF94 7E3D082F USER32.MessageBoxExA USER32.7E3D082A 0007FF90 0007FF98 00000000 hOwner = NULL 0007FF9C 00093850 Text = "La operaci",F3,"n se ha co 0007FFA0 00401004 Title = "ERROR" 0007FFA4 00000000 Style = MB_OK|MB_APPLMODAL 0007FFA8 00000000 LanguageID = 0 (LANG_NEUTRAL) 0007FFB0 00402050 USER32.MessageBoxA test.0040204A 0007FFAC 0007FFB4 00000000 hOwner = NULL 0007FFB8 00093850 Text = "La operaci",F3,"n se ha co 0007FFBC 00401004 Title = "ERROR" 0007FFC0 00000000 Style = MB_OK|MB_APPLMODAL I'll also quote the documentation for reference: Quote: nSize [in] |
|||
20 Jul 2010, 21:54 |
|
jumpex 20 Jul 2010, 22:13
Your changes scare me, I admit.
But that's not te only reason I don't do it your way - I just see my way working with every other combination. But why does your way work? What's rb (EDIT: reserve data, I see... still don't undertand why it works my way with the other numbers)? And heap corruption was exactly what I wanted to see - that's what's happening. Because the strcat call and FormatMessage are linked only by [errm]. But I can't see why this happens and HOW a MessageBox can fix it. That's why I was looking for manual ways of "fixing" the heap (push, pop, xor...). But another thing - you say that it's crashing because it's overflowing [errm]. I agree with that. But why does it work with, for example, 2 as error number? Or 1, or anything else, except 0? How does the zero for error number affect things so, that the stack gets corrupted? This works, NO problems: Code: lea eax,[errm] invoke FormatMessageA,FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,0,2,0,eax,0,0 astrocat [errm],slen invoke MessageBoxA,0,[errm],errt,0 Shows "The system cannot find the file specified. 1440" (with a new line before "1440" - is that strange?). But it works. I'm really glad that someone's getting near the stage I am at understanding the problem - I honestly didn't expect that. EDIT(maybe 5th): I think my way, with [errm] as dd worked by magic with other values, because it was wrong. rb was something I had thought about, but never knew existed. Thanks for pointing that out. I think that's the right way to allocate a buffer and have a pointer to it. |
|||
20 Jul 2010, 22:13 |
|
LocoDelAssembly 20 Jul 2010, 23:07
Quote:
If you want to investigate this further, you should take a look at what happens when you do a LocalAlloc, FreeAlloc and LocalAlloc again (always same size) to see if the second LocalAlloc gets the same memory address. Maybe the reason that it is healed with MessageBox is that further memory allocations from the heap are performed far away from the corrupted region and that is the reason it is working (notice that "p" is first executed BEFORE the first, and more importantly, the second write which is the first one in overflow the buffer). I've also checked some minutes ago how FormatMessageA works when you specify your custom size, it first calls an undocumented function CreateVirtualBuffer requesting your curstom size plus one, then it call something that gets the unicode string of your error code, then it is converted to ansi and LocalAlloc is used to get an smaller buffer, and finally ntdll.memmove is used to copy from the virtual buffer to the memory allocated by LocalAlloc (and before returning FreeVirtualBuffer is used). I think that now we can be certain that you get a buffer which better approximates the needed size. PS: BTW, heap allocations has some granularity, it is not possible to allocate exactly 1 byte for instance AFAIK, this also must be helping your program to survive to some messages whose (strlen(message) + 1) mod granularity >= (strlen(slen) + 1). |
|||
20 Jul 2010, 23:07 |
|
jumpex 20 Jul 2010, 23:30
Man, I was writing my post and it contaiend your "P.S" in other words.
Exactly - granularity. (I remember really understanding that from "Write Great Code" - not to advertise it or anything, it's nice though) But your research is way more in depth. Thanks for that. I'll use rb and your method (which I, after giving it considerable thought, declared the right one). Also, I did astrocat (name intended) as a procedure, along with another function that I ruled out of my original source for simplicity - itoa (I named it aitos). So here they are, if anyone needs them (along with my errors and crude documentation style): Code: proc pastrocat,dst,src mov edi,[dst] xor al,al xor ecx,ecx dec ecx cld repne scasb dec edi mov esi,[src] @@: lodsb stosb test al,al jnz @B ret endp ;==================================================== ;DOCUMENTATION======================================= ;==================================================== ;ARG------------------------------------------------- ;---------------------------------------------------- ;dst - destination string to contain both (in,out) ;src - source to be added to destination (in) ;---------------------------------------------------- ;mov edi,[dst] - destination address is in edi ; ;xor al,al - clear al ;xor ecx,ecx - clear ecx ;dec ecx - ecx = -1 ;cld - clear direction flag ; ;repne scasb - find first ascii(0) in string ;dec edi - repne scasb skips null so we get back ; ;mov esi,[src] - source address is in esi ; ;cpy(@@): - copy src to end of dst ;lodsb - load byte from source (esi) to al ;stosb - store byte from al to destination (edi) ;test al,al - AND to test if zero ;jnz cpy(@B) - continue until al is zero (end of esi) ;==================================================== ;---------------------------------------------------- ;TODO------------------------------------------------ ;---------------------------------------------------- ;boundry check ;length of each and both together ;---------------------------------------------------- Code: proc paitos num,str mov edi,[str] mov esi,[str] xor ebx,ebx mov eax,[num] @@: inc ebx xor edx,edx mov ecx,10 div ecx push eax add dl,'0' mov al,dl stosb pop eax test eax,eax jnz @B xor eax,eax stosb inc ebx sub edi,2 mov ecx,ebx shr ecx,1 cld @@: mov al,[esi] xchg al,[edi] mov [esi],al inc esi dec edi loop @B ret endp ;==================================================== ;DOCUMENTATION======================================= ;==================================================== ;ARG------------------------------------------------- ;---------------------------------------------------- ;num - number to be converted (in) ;str - string version of num (out) ;\ebx - holds [str] length (x) ;---------------------------------------------------- ;push num - store [num] as it will change ;mov edi,[str] - destination string address is in edi ;mov esi,[str] - in esi too for later reversing ;xor ebx,ebx - ebx will hold [str] length ;mov eax,[num] - register ops faster, keep [num] val ; ;spt(@@): - split number digit by digit /10 ;inc ebx - increase [str] length ; ;xor edx,edx - edx is 0 (number to divide is edx:eax) ;mov ecx,10 - ecx is 10 (divisor) ;div ecx - divide eax/ecx, eax holds [num] ; ;push eax - it's next devident if not zero ; ;add dl,'0' - remainder in edx to char num ;mov al,dl - move remainder to al ;stosb - store al (char [num] of remainder) in edi ; ;pop eax - restore eax to check if zero ;test eax,eax - compare eax to 0 ;jnz spt(@B) - if eax is not zero, continue ; ;xor eax,eax - zero eax ;stosb - store terminating zero at end of string ;inc ebx - increase string length ;sub edi,2 - scasb skips one, skip '\0' and goto last ; ;mov ecx,ebx - ecx is ebx (length of string) ;shr ecx,1 - divide by 2 (swap chars to half of str) ;cld - clear direction flag ; ;rvs(@@): - reverse edi as it's stored backwards ;mov al,[esi] - byte in esi (1-first in str) to al ;xchg al,[edi] - swap al and edi ;mov [esi],al - al to byte in esi ;inc esi - increase pointer < ;dec edi - decrease pointer > ;loop rvs(@B) - until ecx is 0 ;==================================================== ;---------------------------------------------------- ;TODO------------------------------------------------ ;---------------------------------------------------- ;valid input check ;---------------------------------------------------- |
|||
20 Jul 2010, 23:30 |
|
LocoDelAssembly 21 Jul 2010, 00:01
Something I forgot to tell earlier, if you plan to go the rb way, then make sure that after using rb nothing other than more rb, db ?, and all other things that declares uninitialized space comes after in the container section, not taking care of this will result in a bigger executable.For instance:
Code: section '.data' readable writable buff rb 4096 message db "Hello World", 0 Code: section '.data' readable writable message db "Hello World", 0 buff rb 4096 |
|||
21 Jul 2010, 00:01 |
|
jumpex 21 Jul 2010, 00:08
Tested - true.
Extremely valuable. You've been very helpful, thank you. |
|||
21 Jul 2010, 00:08 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.