The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Astro on August 19, 2009, 09:01:16 PM

Title: Windows Weirdness
Post by: Astro on August 19, 2009, 09:01:16 PM
I've likely made another beginners mistake here...

It appears to work correctly in a stand-alone .exe, but when I moved the code to a DLL loaded during Windows boot, it generates a memory access error 0x00000000 (can't remember if it was read or write).

num db "SomeString",0,0,0,0,0,0
...
pushad ; required - Windows crashes without this

; my code in here...

cld
mov ecx,1h
mov esi,offset num
mov edi,0h
cmpsb

; my code in here...

popad ; required - Windows crashes without this


What I wanted to do was check the first byte for a null value. In the code it contains an 8 byte string, null terminated, but padded with sufficient nulls to extend the total length to 16 bytes, including null terminator.

Is there anything obvious here that I'm missing that would create a memory access violation?

I do wonder if I should use 'byte ptr' instead of 'offset', but I can't see how that would prevent a memory access violation, especially as 'offset' works in a stand-alone .exe, and neither result in reading beyond the end of the string, or otherwise beyond the end of the data. Very bizarre.  :eek

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: FORTRANS on August 19, 2009, 09:38:11 PM
Hi,

   You are loading ESI with the offset to your strong.
You load EDI with zero.  CMPSB will then read from
your string and the memory at zero to compare them.
That causes the memory access error.

   You probably want to put zero in AL, The offset in
EDI and use SCASB to scan for a zero.

HTH,

Steve
Title: Re: Windows Weirdness
Post by: Astro on August 19, 2009, 09:49:26 PM
 :eek :eek :eek :eek

That makes sense - it is expecting a memory address in esi/edi not a value.

Thanks!

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 19, 2009, 09:58:22 PM
also, if you are going to cmpsb a single byte, ecx need not hold the quantity
ecx needs to be set to a length if the rep prefix is used
hang in there, Astro   :U
try this code....

        xor     eax,eax
        mov     ecx,eax
        mov     edi,offset num
        repnz   scasb
        dec     edi
        neg     ecx

afterwards, edi will point to the first 0
ecx will be the string length
hint - one of the few cases where ecx=0 means 4,294,967,296, or 100000000h
it makes the scas comparison, then decrements the ecx register, so after the first byte, ecx=FFFFFFFFh
Title: Re: Windows Weirdness
Post by: Astro on August 19, 2009, 10:13:57 PM
Ahh - I thought ecx was decremented by cmpsb not rep.

Quotehang in there
Sure will!

Thanks for the code snippet!  :thumbu

I see you put the address of num in edi, not esi? Is it not addressed edi:esi ?????

EDIT:
QuoteAE SCASB Compare AL with byte at ES:(E)DI and set status flags

OK - when is it esi:edi and edi:esi? Is there a rule or is it just how the instruction is put together at the time?

mov edi,offset num
xor eax,eax
cmpsb ; compare al with dl
jz SomeLabel ; jmp if byte NULL


Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 19, 2009, 10:37:49 PM
well - it isn't "esi:edi" or "edi:esi" - it is esi and edi - lol
when you put a ":" between registers, it implies they are paired to hold a single large value
sometimes. you may also see a segment register paired with a register - es:[di], for example
esi is the source index, edi is the destination index
some string instructions (like scas) require only one index register
lods - esi
stos - edi
scas - edi
cmps - esi and edi
movs - esi and edi

Quotecmpsb ; compare al with dl
that should read cmpsb ; compare al with [edi]
Title: Re: Windows Weirdness
Post by: Astro on August 19, 2009, 11:03:09 PM
OK...

If I have:

MyString db "abcd",0

is it loaded into EDI as:

d|c|b|a
-------- edi
     ---- di
        -- dl

?

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 19, 2009, 11:55:10 PM
first, DL is the lower 8 bits of the EDX register - not EDI
only EAX, EBX, ECX, and EDX are split registers
but, yes, if you were to "mov edi,dword ptr MyString", the ascii "d" would be the high 8 bits of edi
notice i used "dword ptr" as a size override
that is because the data was defined as bytes and we are moving it into a dword register
the lower case letter 'a' is 97, or 61h, so edi would be 64636261h
this is not a normal use of edi, however
usually, we would put the offset of MyString into the edi register with "mov edi,offset MyString"
in that case, it is a 32-bit address that is assigned by the assembler
as assembler programmers, we are not generally too concerned with the exact value of the addresses
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 12:44:51 AM
btw Astro - you have a great nic for avatars - lol
i found a couple you might like
Title: Re: Windows Weirdness
Post by: Astro on August 20, 2009, 12:54:32 AM
hehe - thanks!  :thumbu

I see - it is either EDI or DI.

Hmm - so if I got this right:

mov edi,dword ptr var - moves the CONTENTS of var into EDI

whilst:

mov edi,offset var - moves the ADDRESS of var into EDI

?

(Off-topic slightly)...so following on from that, what is the effect of:

mov eax,dword ptr [SomePointer]

if SomePointer was a DWORD pointing to an arbitrary block of memory for example?

Would it be:

0x00000000: 0x45 = SomePointer
0x00000001: 0xF4 = SomePointer
0x00000002: 0xAC = SomePointer
0x00000003: 0x12 = SomePointer
...
0x45F4AC12: 0x00 - first byte of the dword at 0x45F4AC12 pointed to by SomePointer?

If it was mov eax,offset [SomePointer] am I correct in thinking this would move the CONTENTS of 0x45F4AC12 into eax and not the ADDRESS of SomePointer (0x00000000)?

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 01:06:02 AM
that gets the value at the address named SomePointer
now, understand, that may be an address of some other data, stored as a dword pointer
we often pass pointers instead of data because you can send a pointer
to a structure that may contain many types and sizes of data
we often pass pointers to strings, as the string may be any length
i am working on a program now that has a circular buffer
the buffer is an array, 4 dwords per element
i use 2 pointers in the same structure to keep track of the "head" and "tail" of the buffer
in this case, as the buffer becomes full, i want to discard the oldest data
so, when i add an element to the array, i add 16 to the tail pointer
if the tail pointer is past the end of the array, i wrap it back to the beginning
if the new tail pointer is equal to the head pointer, i have to discard an element by bumping the head by 16
(that is because the condition of head=tail indicates empty buffer in this case)
that also means the updated tail pointer points to the location of the next entry
when i want to add something to the buffer, i grab the tail pointer and use it as a destination address
well - you get the idea - just trying to give you an example of uses of pointers
as you can see, this is much faster than moving all the elements in the array up by 16 bytes each time i add an element
Title: Re: Windows Weirdness
Post by: Astro on August 20, 2009, 01:11:07 AM
Actually I'm confusing myself now.

Address of SomePointer = 0000

0000: 0005
0005: 2345

mov reg,SomePointer ; 0000

mov reg,[SomePointer] ; 0005

mov reg,word ptr SomePointer ; 0005

mov reg,word ptr [SomePointer] ; 2345 or 0005 ?

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 01:23:53 AM
what may be confusing you is the brackets
in your examples, nothing will change if you remove the brackets altogether
the only time you must use brackets is to use a register as an address like: "[ebx]" is different than "ebx"
another thing that may be confusing you is the use of "ptr"
there are two times you need to use "ptr"
1) to override the declared size of defined data
2) to specify the bit-width of an immediate value

1) if i have a data item defined as a dword "DwordVal dd 12345678h" (32-bit value)
let's say i want to load only the low-order 16-bits into ax - then "mov ax,word ptr DwordVal"
basically, this just avoids the assembler error "operand sizes must match"
another example:
i have a string "ByteString db 'some dang string' "
i want to grab 4 bytes at a time - "mov eax,dword ptr ByteString"

2) this is where you actually need to use the ptr to inform the assembler how wide an immediate value is
cmp  [ebx],0
the assembler has no idea if you are refering to a byte, a word, or a dword
cmp dword ptr [ebx],0
now, the assembler knows you want to compare a dword to 0
Title: Re: Windows Weirdness
Post by: Astro on August 20, 2009, 01:55:58 AM
Hmm....

If I had:

call HeapAlloc
mov SomeMemPtr,eax

and then:

mov SomeMemPtr,5h

this would trash SomeMemPtr and I'd lose the pointer to my memory.

I'd need to use:

mov byte ptr [SomeMemPtr],5h

so that 0x5 went into the first byte of where SomeMemPtr pointed, and not into SomeMemPtr itself.

Without the brackets, I'd trash the reference.

:eek :eek :eek

Actual example:

mov esi,offset [ptr_DEVICE_LIST_INFO+16]

I'll look at this tomorrow - 0300 here. I'm going to utterly confuse myself.  :eek

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 02:01:54 AM
same same
the last example would also trash it
there are no labels to directly address allocated memory
you might do this:

        mov     ebx,SomeMemPtr
        mov dword ptr [ebx],5

Quotemov esi,offset [ptr_DEVICE_LIST_INFO+16]

        mov     esi,offset ptr_DEVICE_LIST_INFO+16

esi will contain the address of 16 bytes above ptr_DEVICE_LIST_INFO
i think what you are wanting to do isn't allowed
that is to use a stored memory location as an address to access another memory location
you have to either get the address into a register or address it directly with a label - or a combination of the two

http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_4/CH04-1.html
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 02:13:44 AM
8 hrs diff - you must be in England   :U
wifee is a Brit
Title: Re: Windows Weirdness
Post by: Astro on August 20, 2009, 10:03:54 AM
push offset ptr_DEVICE_LIST_INFO+96 ; handle

and

push offset [ptr_DEVICE_LIST_INFO+96] ; handle

work.

Are the [] required at all (except as noted for registers)?

I'm trying to figure out just what goes where in memory.

I see the following happening:

SomeString (the variable itself) exists:

Address: Contents

0x00001234/5/6/7: 0x00452634 ; SomeString is at 0x1234 and points to the actual string at 0x00452634
0x00452634: 0x41 ; Capital letter 'A'

so:

push SomeString will push 0x00452634

push byte ptr SomeString will push 0x41

?

Quote8 hrs diff - you must be in England
I am indeed!  :U  That puts you somewhere along a very long West Coast!  :lol

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 01:36:20 PM
no - the brackets are only needed if a regster holds the address

one thing that may help you a lot is to look at code that others have written and figure out what it does
that way, you will only see constructs that are valid
you are letting yourself be confused by storing a pointer in memory and attempting to use it to address data
it is best to just learn to address data, first - the fact that it is a pointer is just one type of meaning for that data
the \masm32\examples folder has many working programs
also, there are many working programs posted here in the forum

Quotepush SomeString will push 0x00452634

when you "push SomeString", the assembler generates "push dword ptr [00001234]"
the dword value at address 00001234 goes on the stack (00452634h)
i have to use brackets there because "push 00001234" will place the value 00001234 on the stack
the assembler does not allow you to write instructions using brackets and numbers
but, that is what a debugger or disassembler will see

Quotepush byte ptr SomeString will push 0x41

i have never tried to push a byte - lol - i don't think it will let you
(i could force it to happen, but it is best to always keep esp aligned by 4 in 32-bit programs)
i think you can only push words and dwords (unless it is a 64-bit operating system)
notice that "byte ptr" or "word ptr" only tells the assembler what bit-width to use
"ptr" does not otherwise change the behaviour of the instruction
again, you are trying to use a pointer, stored in memory, to address another memory location
this simply isn't allowed
"push word ptr SomeString" will push 2634h - the low order portion of the dword
if you want to get the 41 onto the stack...

        mov     ebx,SomeString
        push word ptr [ebx]

or

        mov     ebx,SomeString
        push dword ptr [ebx]

QuoteThat puts you somewhere along a very long West Coast!
i am near Phoenix Arizona, where the damn sun won't stop shining
at least it has dropped back below 110F (43.3C)
Title: Re: Windows Weirdness
Post by: Astro on August 20, 2009, 03:58:12 PM
Quotei am near Phoenix Arizona, where the damn sun won't stop shining
at least it has dropped back below 110F (43.3C)
43.3°? How do you live out there??? I'm finding 26° (72F) and 60% rel. too much (but then no aircon so...).

Thanks for the avatar by the way!  :bg

OK! I'm not sure where I picked up the brackets thing.

What is the deal with offset then? is that dword ptr in disguise? It makes sense.

Best regards,
Astro.
Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 05:26:13 PM
"offset" is used to get the address of a variable, as opposed to getting the variable, itself
you may also see "addr" - i think that's the same as "offset"
"ptr" is a size override, as explained earlier
you never see the 2 used together, because when you want the address, you don't care about it's width

yes - most everyone here has air conditioning
i survive by being outside as little as possible from about 10:00 AM to about 6:00 PM
that makes a lot of us here "night owls" in the summer
the middle of the night is a nice time to go for a dip in the pool or something
if you have to go outside in the daytime - sunglasses - hat - keep the skin covered
also, try to stay in the shade whenever possible and drink lots of water
soon, we will get our monsoon rains then a bit of humiduty
after that passes, it will be nice out - temp in the 80's and 10% humidity
Title: Re: Windows Weirdness
Post by: MichaelW on August 20, 2009, 06:10:23 PM
The ADDR operator is intended for use with INVOKE. Because the OFFSET operator specifies an address constant that is resolved at assembly time, it will not work for a local variable that is allocated from the stack at run time. The ADDR operator duplicates the action of the OFFSET operator, or generates code to pass the address of a local variable, as necessary.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      globaldd dd 1234h
      buffer   db 20 dup(0)
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
myproc proc
    LOCAL localdd:DWORD
    mov localdd, 5678h
    nop
    invoke dw2hex, ADDR globaldd, ADDR buffer
    nop
    invoke dw2hex, OFFSET globaldd, ADDR buffer
    nop
    invoke dw2hex, ADDR localdd, ADDR buffer
    nop
    ;error A2098: invalid operand for OFFSET
    ;invoke dw2hex, OFFSET localdd, ADDR buffer
    ret
myproc endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


00401000 55                     push    ebp
00401001 8BEC                   mov     ebp,esp
00401003 83C4FC                 add     esp,0FFFFFFFCh
00401006 C745FC78560000         mov     dword ptr [ebp-4],5678h
0040100D 90                     nop
0040100E 6804304000             push    403004h
00401013 6800304000             push    403000h
00401018 E877000000             call    fn_00401094
0040101D 90                     nop
0040101E 6804304000             push    403004h
00401023 6800304000             push    403000h
00401028 E867000000             call    fn_00401094
0040102D 90                     nop
0040102E 6804304000             push    403004h
00401033 8D45FC                 lea     eax,[ebp-4]
00401036 50                     push    eax
00401037 E858000000             call    fn_00401094
0040103C 90                     nop
0040103D C9                     leave
0040103E C3                     ret

Title: Re: Windows Weirdness
Post by: dedndave on August 20, 2009, 07:27:07 PM
thanks for straightening me out Michael
i had seen them used in what appeared to be interchangable fashion

so, ADDR will generate lea instructions ?
i see in the last example that it loads the lea into eax
Quoteinvoke dw2hex, ADDR localdd, ADDR buffer
Quote0040102E 6804304000             push    403004h
00401033 8D45FC                 lea     eax,[ebp-4]
00401036 50                     push    eax
00401037 E858000000             call    fn_00401094

what if i had another parm in eax ?
how does the assembler know which register to use ? - lol
as an example, what if i had parms in eax, ecx, and edx ?
what if...

        INVOKE  WriteFile,
                eax,
                edx,
                ecx,
                ADDR localWriteCount,
                NULL
Title: Re: Windows Weirdness
Post by: jj2007 on August 20, 2009, 07:49:11 PM
Quote from: dedndave on August 20, 2009, 07:27:07 PM
what if i had another parm in eax ?

You'll get an error message- register overwrite...