News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Increment pointer of dynamically allocated buffer in 64-bit MASM ML64

Started by cyrus, August 22, 2023, 10:18:36 AM

Previous topic - Next topic

cyrus

Hi. I have been running around in circles trying to figure out how to increment a pointer. I am not new to programming, having years of C and C++ experience. In my current project, I am connecting to a socket and calling send/recv. These functions are being called correctly and the data is returned to my buf correctly and the loop works correctly as well, because I am able to get r12 to match the size of a file. I allocated my buf to a max size of 256k which I returned the address of correctly from rax to buf.

I declare my pointer as such:

.data
buf DQ ?

.code

    ; call VirtualAlloc using the handle and size of bytes to allocate
    mov r9, 4h             ; PAGE_READWRITE
    mov r8, 1000h          ; MEM_COMMIT | MEM_RESERVE
    mov rdx, 3E800h        ; 256000 bytes
    mov rcx, 0             ; NULL
    sub rsp, 20h
    call VirtualAlloc
    mov buf, rax           ; return pointer to the allocated memory block and save to buf


    loop_recv:
        xor r9, r9             ; flags; set to NULL
        xor r8, r8             ; clear reg
        mov r8w, 200h          ; 512 bytes; max size from recv
        lea rdx, buf           ; pointer to our buf for recv to write to
        mov rcx, socket_desc   ; socket descriptor
        sub rsp, 20h
        call recv              ; ws2_32.recv(socket desc, pointer to buf, length of data in buffer, flags)
        mov r11, rax           ; copy bytes returned from rax to r11
       
        ; version 1
        ;xor rbx, rbx
        ;mov bx, r11w           ; move num of bytes returned from r11 - could only be 0 - 512 so no more than a WORD - 2 bytes
        ;next_c:
        ;    inc buf            ; increment position of array r11 times (num of bytes returned from recv) to store next buf
        ;    dec bx
        ;    cmp bx, 0
        ;    jne next_c

        ; version 2
        add buf, r11          ; dynamic memory pointer advance by 512 bytes

        add r12, r11           ; r12 is the total counter so each successful call to recv adds num of bytes returned from rax to it; saved in r11
        cmp r11, 0             ; last call from recv will return data copied to r11
        jne loop_recv          ; if not 0, continue recv'ing more

Neither version 1 or 2 increment buf. I am basically incrementing buf num bytes amount. recv is stated to return 512 bytes but some calls may return less, so this must be in bytes. If the amount returned from recv is 512 as usual, it should increment buf by 512. But It is failing telling me invalid buffer from my debugger. If I comment out the last 2 statements where I do not loop, my final buf is only 512 bytes. But if I let it run, the final buf is actually 0 because it claims it is an INVALID_BUFFER.

Any ideas on how I could do this in assembly? It's very straight forward in C and C++.

fearless

Just at a quick glance, I think that as buf is already a pointer to memory allocated, you can change:


lea rdx, buf
to just:

mov rdx, buf

mineiro

VirtualAlloc is returning a pointer.
When you're doing "lea rdx,buf", you're doing something like a pointer to a pointer. So, follow fearless suggestion.

Or you can do this:
lea rdx,buf               ;pointer to buf variable in rdx register.
mov rdx,qword ptr [rdx]   ;contents of buf variable == pointer to virtualalloc returned pointer.

So, if you wanna increase that pointer by one byte:
lea rdx,buf               ;load effective address (lea) of buf into rdx register
add qword ptr [rdx],1     ;add 1 to contents of buf variable == +1 displacement (offset).
                          ;next time you load buf again by using lea, that virtualalloc returned pointer  has been increased by 1 byte.
;But by doing this you're changing (increasing) start pointer of data. So a bit safe way can be create a counter and leave returned pointer by virtualalloc untouched.

.data
counter dq 0
.code
lea rdx,buf      ;rdx = pointer to buf variable
mov rdx,qword ptr [rdx]    ;rdx = pointer to virtualalloc returned
add rdx,counter            ;displacement
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

cyrus

Unfortunately, nothing seems to work. Tried a million combinations. When I use it in a loop, my buffer is worthless and I can't write anything to the disk. If I don't have a loop, I can write my 512 bytes. What else can I do here? Is this a bug?

zedd151

Hi Cyrus, welcome to the forum.

I don't see anywhere that you are zeroing r12...  :icon_idea:

Do you have the masm64 SDK installed? I made a test program... not meant to be run outside of a debugger, just a test program - there is no exiting this loop as-is. I did not put in a compare that would exit the loop.... beware.

I don't have either the recv procedure or socket_desc (never worked with sockets). This is just to test the buffer incrementation ... watch the program in a debugger and step the code. I used x64dbg.
include \masm64\include64\masm64rt.inc

.data
    socket_desc dq 12345678h ; I dont know the actual value for this
    buf DQ 0
   
.code
start proc
    xor r12, r12 ; zero r12!!!
    ; call VirtualAlloc using the handle and size of bytes to allocate
    invoke VirtualAlloc, 0, 256000, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE  ;; masm64 "invoke" syntax
    mov buf, rax          ; return pointer to the allocated memory block and save to buf
loop_recv:
    invoke recv, socket_desc, buf, 200h, 0 ; mockup procedure returns 512    ;; masm64 "invoke" syntax
    add buf, rax          ; dynamic memory pointer advance by 512 bytes
    add r12, rax          ; r12 is the total counter so each successful call to recv
    cmp rax, 0            ; last call from recv will return data
    jne loop_recv          ; if not 0, continue recv'ing more
    invoke ExitProcess, 0
start endp

recv proc desc:qword, bufr:qword, len1:qword, flags:qword ; mockup procedure --->> always returns 512
    mov rax, 512
    ret
recv endp

end

Note: I am, no expert with 64 bit code, but this little test should help somewhat... assuming that you have masm64 SDK installed...  :smiley:

It would help greatly if you could post more code...if at all possible.

cyrus

Hi Zedd151. Actually I am zeroing it out before  the recv call  :biggrin:

I am not able to call invoke. Im pretty new to MASM, Im basically just using ml64.exe from my visual studio 2022 version in the x86_64 Cross Tools Command Prompt

But believe me, to my surprise, the

add buf, rax
just adds to the value inside the buffer just as mineiro mentioned. Yes it actually changes the value in the buffer, unbelievably. I tried many combinations with jaw dropping results of frustration  :nie:

zedd151

I have modified your code with "mov rdx, buf" instead of "lea..."
Also zeroing r12
Your (commented out) "version 1" code removed also

.data
buf DQ 0

.code
    xor r12, r12
    ; call VirtualAlloc using the handle and size of bytes to allocate
    mov r9, 4h            ; PAGE_READWRITE
    mov r8, 1000h          ; MEM_COMMIT | MEM_RESERVE
    mov rdx, 3E800h        ; 256000 bytes
    mov rcx, 0            ; NULL
    sub rsp, 20h
    call VirtualAlloc
    mov buf, rax          ; return pointer to the allocated memory block and save to buf


loop_recv:
    xor r9, r9            ; flags; set to NULL
    xor r8, r8            ; clear reg
    mov r8w, 200h          ; 512 bytes; max size from recv
    mov rdx, buf          ; pointer to our buf for recv to write to
    mov rcx, socket_desc  ; socket descriptor
    sub rsp, 20h
    call recv              ; ws2_32.recv(socket desc, pointer to buf, length of data in buffer, flags)
    mov r11, rax          ; copy bytes returned from rax to r11
 
    add buf, r11          ; dynamic memory pointer advance by 512 bytes

    add r12, r11          ; r12 is the total counter so each successful call to recv adds num of bytes returned from rax to it; saved in r11
    cmp r11, 0            ; last call from recv will return data copied to r11
    jne loop_recv          ; if not 0, continue recv'ing more

Give that a try.  :smiley:

Edit =
Whoops! I made and error and removed some necessary code... it's fixed now. I was editing on my ipad.  :tongue:  ... and didn't catch my blunder til just now.

cyrus

Ok I have an update. While debugging, I noticed that rdx kept incrementing the address returned from VirtualAlloc by 512 bytes each time so that was good. I was sure the buffer was being copied but my WriteFile wasn't able to copy to the disk correctly and then it hit me. buf has been incremented. You would think you could just assign the same

mov rdx, buf
in WriteFile and it would work but nope. I feel like buf is no longer at that address since it has been "incremented". So what I did was, after the call to VirtualAlloc, I also saved rax to r15 and then in WriteFile, I basically just used r15 instead of buf since that has the beginning address of where all that new data lives. This is almost like using a pointer in C as the iterator through an array. After iterating through the array, you can't reference *p anymore since it now points passed the array. You need to use whatever has the start address of the array.

So in a nut shell, Zedd151 had the right code that worked for me. It's possible the other code probably works too and I just haven't experimented any further after all the frustration lol.

When I went to call WriteFile, buf had been incremented passed the entire buffer  :biggrin:

Thank you guys for all the help!  :biggrin:

zedd151

Good that you saved the buffer pointer to r15.
In your original code, you did in fact increment "buf" as well. My example code followed from that. You did not include WriteFile code in your post...  but in retrospect, should have used r15 or other register in the loop, rather than increment "buf" directly.  Then the buf variable (pointer to allocated buffer) would remain unchanged and reusable later. It was late here, and I had made a couple of errors myself.

Anyway, I am glad you found a workable solution in the end.

So, what exactly is your program used for... if I may ask?

mineiro

You should have created a 2nd variable to hold pointer to buffer memory, or a variable that hold displacement. So, in writefile function you can use that backup variable.
like:
.data
buf dq 0
bufbkp dq 0

...
mov buf,rax
mov bufbkp,rax
...

Generally exist some conventions that can save us some readings, Microsoft uses "p" or "lp" at start of variable to mean that we are dealing with pointers. So, "buf" variable could be "pbuf" or "lpbuf". Size of something generally starts with "sz", counting bytes generally starts with "cb".
https://learn.microsoft.com/en-us/windows/win32/stg/coding-style-conventions

I forgot to say before, welcome to board sir cyrus.
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

zedd151

Quote from: mineiro on August 22, 2023, 11:35:06 PMSize of something generally starts with "sz"
You are speaking of Hungarian Notation, mineiro.  :biggrin:

Doesn't "sz" denote a zero terminated string, even in the link that you posted? That was my understanding since a very long time ago.  :smiley:
"lpsz" would then denote a (long)pointer to the string.
This information might be useful to new assembly programmers, that is why I felt the need to correct you mineiro. Don't want to confuse the newcomers.

mineiro

Exactly sir zedd151;
When I have started programming I get lost many times with variables names, well, their meaning or types.
One example happened when I used a register as being a buffer. So, that should not be a pointer, even being a buffer to something.
Oh yes, "lpsz", pointer to zero terminated strings.

---edited----
I wrote that because in ms-dos O.S., some interruptions that need some strings are not zero terminated, they use a dollar sign as being end of string.
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

cyrus

Yup exactly. I should have saved that beginning of address to another variable (pointer DQ 0) from the beginning and used that to do my incrementing but it was my first time working with pointers in assembly. I have done it a million times in C and C++ but once I started thinking about how I would do it in C, it just hit me. No wonder in WriteFile, each time I was left with 0 bytes. I did leave out the code for WriteFile because I did not think it was necessary but it did in fact play a huge role. If you increment, you go passed the entire allocated block  :rolleyes:

lingo

The same I did it my way :biggrin:

.data
;buf DQ 0
.code
xor    r12,  r12       ; r12 is the total counter   
    ; call VirtualAlloc using the handle and size of bytes to allocate
mov    r9,  4h                ; PAGE_READWRITE
mov    r8,  1000h             ; MEM_COMMIT | MEM_RESERVE
mov    rdx, 3E800h            ; 256000 bytes, rdx -> 2nd param
xor    rcx, rcx               ; NULL rcx -> 1st param
sub    rsp, 4*8           ; free space in the stack  for four registers :
            ; rcx->1st param,rdx ->2nd param,  r8->3rd param, r9->4th param
call    VirtualAlloc
; mov buf, rax                ; return pointer to the allocated memory block and save to buf
mov     r13, rax              ; return pointer to the allocated memory block and save to R13

loop_recv:
  xor    r8,  r8              ; r8->3rd param -> clear reg
  mov    rdx, r13       ; rdx->2nd param-> buf->r13  ; pointer to our buf for recv to write to
  mov    r8w, 200h            ; r8->3rd param  ->  512 bytes; max size from recv
  xor    r9,  r9                      ; r9->4th param-> flags; set to NULL
  mov    rcx, socket_desc     ; ecx->1st param -> socket descriptor
  sub    rsp, 4*8          ; free space in the stack  for four 8 bits registers :
            ; rcx->1st param,rdx ->2nd param,  r8->3rd param, r9->4th param
  call   recv                 ; ws2_32.recv(rcx->socket desc, rdx->pointer to buf, re8->length of data in buffer, r9-> flags)
        ; return rax=512
  add    r13,rax       ; new buf->dynamic memory pointer advance by 512 bytes;
  add    r12, rax       ; r12 is the total counter   
  test   rax, rax
  jne    loop_recv         
; mov  r11, rax           ; copy bytes returned from rax to r11
; add  buf, r11           ; dynamic memory pointer advance by 512 bytes;
; add  r12, r11           ; r12 is the total counter so each successful call to recv adds num of bytes returned from rax to it; saved in r11
; cmp  r11, 0             ; last call from recv will return data copied to r11
; jne  loop_recv          ; if not 0, continue recv'ing more
Quid sit futurum cras fuge quaerere.

zedd151

This topic has been split, as the query from the topic author has been satisfactorily answered, but the conversation continued with another subject.
Quote from: cyrus on August 22, 2023, 04:17:12 PMThank you guys for all the help!  :biggrin:
The split off portion is here:
Benchmark methods for 'mov rax' vs. 'mov eax'