Re: Completely confused. Please send help. :)



Shay wrote:
Ok, I have looked in books and PDFs but cannot find what is going on here. This is going back to my itoa() code I posted previously. I modified it to take parameters on the stack rather than via registers. I can get it to work, but I don't understand how it's working.

More precisely, I don't think I fully comprehend how the stack works (especially with indirect referencing and offsets).

If I have some code like so (and please correct me if I messed up):


sub esp, 11 ; buffer

This is an "awkward" number to subtract from the stack. Better to keep the stack aligned to a four-byte boundary (at least). This isn't any part of "the problem", but it will result in lousy performance. I'd make the buffer size 16 - a "nice round number" - 12 would be enough... 11's enough, but it's "odd".

push dword 11 ; size of buffer - itoa() changes this to length of converted number

lea ebx, [esp+15] ; pointer to buffer since it can be variable size
push ebx

Okay, you're passing the "end" (high in memory) of the buffer. That's what "itoa" expects, as it's written. It might be "better" to pass the "front" (low in memory) of the buffer, and "advance" to the "end" within "itoa" - just to save the caller(s) from having to do the calculation every time. But that isn't "the problem"... yet.

push eax ; This has a number in it

call itoa

pop eax
pop ebx ; get the address of the buffer back

mov ecx, ebx ; data to write

Here's "the problem". You've still got the "top" of the buffer, which is not where you want to begin your "write"! Since you're (incorrectly) using "inc edi" in itoa, you've put the characters where you're "write"ing from - yes, you're clobbering the stack, but there's nothing "vital" there... in this case - but "backwards". You want to be using "dec edi" in itoa... but since there's a variable number of characters, we don't want to start the "write" at the beginning of the buffer - we'll need to return the address at which to start our "write"...

mov edx, [esp] ; length of data to write

Since we *do* return the length, we can do a "quick fix" by:

sub ecx, edx

(in addition to changing "inc edi" to "dec edi" in itoa)

add esp, 15 ; get rid of buffer and buffer_size

I wouldn't get rid of it until after I'd written it, but it does no harm.

;; write_to_stdout call ...


I basically want a C call like:

void itoa(long number, char* buffer, int* buffer_size);

Stack should look like this:


--------------------


Buffer space


--------------------
11
--------------------
Buffer space addr
--------------------
number to convert
--------------------
Return address
--------------------


Now itoa() is the following (this is in its own file):


---------------- CODE --------------------------

section .text
global itoa

itoa:
push ebp
mov ebp, esp

;; parameters on stack (number, buffer*, buffer_size*)
%define number [ebp+8]
%define buf_addr [ebp+12]
%define buf_size [ebp+16]

push ebx
push edx
push edi

; Get address of where to store return data
mov edi, buf_addr

; Number to stringify
mov eax, number

; Do we have a positive number?
cmp eax, 0
jge .ASSIGN

; If not, make it so
neg eax

.ASSIGN:
; Set up divisor
mov ecx, 10

; Counter for length to make sure we don't
; go past buffer_size bytes
mov ebx, 0

%macro CHECK_BUFFER_SIZE 0
; Check to make sure we don't overflow
; the size of buffer
;
cmp ebx, buf_size
je .RET
%endmacro

.DO.WHILE:
mov edx, 0

; [eax] is always being changed to what we
; need so no need to reassign it here
div ecx

add edx, 48

; Add next number to the sequence
mov [edi], dl
inc edi ; This doesn't seem right to me since the buffer space "grows" down into memory

Correct. Should be "dec".

inc ebx

CHECK_BUFFER_SIZE

; Do we need to loop?
cmp eax, 10
jge .DO.WHILE

; Account for a zero being passed in so
; we don't end up with "00"
cmp eax, 0
je .CHECK.NEG

; Store final digit
add eax, 48
mov [edi], al
inc edi

"dec"

inc ebx

CHECK_BUFFER_SIZE

.CHECK.NEG:
cmp dword number, 0
jge .RET

; Add the sign for negative numbers
mov byte [edi], '-'
inc edi

"dec" (do we really need this?)

inc ebx

.RET:
; Modify the buffer size parameter to be the
; number of bytes we wrote to buffer
mov buf_size, ebx

; Restore stack and kill local variables
pop edi
pop edx
pop ebx
mov esp, ebp
pop ebp
ret

---------------- END CODE --------------------------

Now this works, but the number comes out backwards (i.e. 13 == 31).
If I change the 'inc edi' to 'dec edi', I only get the last digit (i.e. 13 == 3).

As noted, your string *is* there... but you're only writing part of it (and some additional garbage).

It seems I cannot figure out how to return everything on the stack. If I return via [eax], I can get it to work fine with only a few changes, but I don't know if it's working correctly (as in I'm not stomping all over the stack).

You could put a known value on the stack, and make sure it's still there. One way to do this would be to make a dummy subroutine that calls itoa - if it returns properly... you haven't trashed *that* part of the stack, at least.

I tried debugging it, which is how I got this far, but the debugger (ald in this case) is confusing me. When I 'examine' edi, I get from some address (where the buffer starts for instance) to an address up in memory (i.e. +X).

This is the confusing part for me... when I set up the call to itoa(), I am pushing items down into the stack; memory addresses should get lower yes? So when I attempt to modify those addresses within itoa() should I not get the start address and 'dec' into the buffer? For example, if I do:

mov edi, buf_addr

This is moving the address of where my buffer starts (yes?). So if I then do:

dec edi (10 times)

should I not then be to where my "buffer_size" is sitting on the stack?

Well... I guess so.

Thanks for any help you can give this poor soul. I must say, trying to learn assembly has aged me a year in the month or so I've been at it.

By the first of the year, you'll be as old as Yoda - and maybe as wise! :)

You seem to have the concepts straight. You just confused yourself by starting the "write" at the wrong place. Other than that, your code works with "dec edi" - as you understand it "should be" to get the digits in the right order. Frustrating? Yeah, unless you learn to think of it as "amusement" :)

Best,
Frank

.



Relevant Pages

  • Completely confused. Please send help. :)
    ... This is going back to my itoa() code I posted previously. ... mov edx,; ... mov edi, buf_addr ... mov eax, number ...
    (comp.lang.asm.x86)
  • Re: Help: ASM to ASM calls
    ... Johnathan Bartlett uses this in his book, "Programming From The Ground Up", to demonstrate "add ebx, eax" without having to provide a "display a number" routine. ... Unlike ending a C program with "ret", the stack doesn't have to be sane, and you don't have to preserve ebx, esi, edi, and ebp. ... So far 'mov' has worked just fine. ...
    (comp.lang.asm.x86)
  • Re: Win32forth v4stc
    ... add ecx, edi ... You're using the hardware stack as the data stack, ... mov eax, ebp ...
    (comp.lang.forth)
  • Re: Apache Vulnerability through a Proxy?
    ... if the chunk handling vulnerability carries through ... does explain how the expected SEGV from overrunning the stack is ... if you arrange for the negative offset of the buffer to point at ... at this point, we've decided to go backwards, edi is dest, esi is source ...
    (Bugtraq)
  • Re: switch between functions
    ... int something ... subtraction because x86 'push' instruction decrements the the stack pointer ... The C variables esi and edi represent the cpu registers esi and edi. ...
    (alt.lang.asm)