如何在汇编中将 RAX 中的值写入 STDOUT?

发布于 2024-12-01 02:21:05 字数 991 浏览 0 评论 0原文

我可以使用 syscall 进行 write 将内存中的一些数据打印到 STDOUT:

ssize_t write(int fd, const void *buf, size_t count);

也就是说:

movq    $1, %rax
movq    $1, %rdi
move address_of_variable %rsi
movq    $5, %rdx
syscall

但是如何打印寄存器值?

更新

.text
        call start
    start:
        movq $100, %rdi
        movq $10, %rsi
        call print_number
        ret

    buffer:
        .skip 64
    bufferend:
    # rdi = number
    # rsi = base
    print_number:
        leaq bufferend, %rcx
        movq %rdi, %rax
    1:
        xorq %rdx, %rdx
        divq %rsi

        add $'0', %dl
        cmp $'9', %dl
        jbe 2f
        add $'A'-'0'-10, %dl

    2:
        sub $1, %rcx
        mov %dl, (%rcx)

        and %rax, %rax
        jnz 1b

        mov %rcx, %rsi
        lea bufferend, %rdx
        sub %rcx, %rdx

        movq    $1, %rax
        movq    $1, %rdi
        syscall
        ret

I can use syscall for write to print some data in memory to STDOUT:

ssize_t write(int fd, const void *buf, size_t count);

That is:

movq    $1, %rax
movq    $1, %rdi
move address_of_variable %rsi
movq    $5, %rdx
syscall

But how can I print register values?

UPDATE

.text
        call start
    start:
        movq $100, %rdi
        movq $10, %rsi
        call print_number
        ret

    buffer:
        .skip 64
    bufferend:
    # rdi = number
    # rsi = base
    print_number:
        leaq bufferend, %rcx
        movq %rdi, %rax
    1:
        xorq %rdx, %rdx
        divq %rsi

        add 
0', %dl
        cmp 
9', %dl
        jbe 2f
        add 
A'-'0'-10, %dl

    2:
        sub $1, %rcx
        mov %dl, (%rcx)

        and %rax, %rax
        jnz 1b

        mov %rcx, %rsi
        lea bufferend, %rdx
        sub %rcx, %rdx

        movq    $1, %rax
        movq    $1, %rdi
        syscall
        ret

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

〃温暖了心ぐ 2024-12-08 02:21:05

您必须先将其转换为文本。您可以走简单的路线,使用 libc 中的 printf,或者,如果您愿意,可以编写自己的转换实用程序。

更新:如果您希望代码与位置无关,那么使用堆栈会更容易。简单地将缓冲区移动到代码段中,因为您在注释中链接到的代码不再起作用,因为现代处理器将代码段设为只读。我已经更新了代码以使用堆栈进行临时存储。

    .text

    call start
start:
    movq    $186, %rax # sys_gettid
    syscall

    movq %rax, %rdi
    movq $10, %rsi
    call print_number

    #ret
    mov $60, %rax # sys_exit
    mov $0, %rdi
    syscall

# rdi = number
# rsi = base
print_number:
    sub $72, %rsp # alloc room for buffer on the stack, 8 more than needed

    lea 64(%rsp), %rcx # 64(%rsp) == buffer end
    movq %rdi, %rax # rax holds current number 
1:
    xorq %rdx, %rdx # clear rdx (div uses the 128-bit number in rdx:rax)
    divq %rsi # divide by base, updating rax for the next iteration 
              # and giving us our digit in rdx (dl)
    add 

相反,如果您想将缓冲区实际存储在代码段中,这是可以完成的。您必须使用例如mprotect(2)buffer 所在的页面标记为可写。这里在汇编中没有进行错误检查(并假设 sysconf(_SC_PAGE_SIZE) == 4096):

     mov $10, %rax # sys_mprotect
     lea buffer, %rdi # addr
     and $-4096, %rdi # page align
     mov $4096, %rsi # len
     mov $7, %rdx #prot = PROT_READ|PROT_WRITE|PROT_EXEC
     syscall

现在,即使缓冲区位于代码段中,代码也应该可以工作。顺便说一句,我在示例中使用 sys_exit ,因为我正在使用 gcc xs -nostdlib -ggdb -o x 将其作为独立文件进行测试。

Update2:要在代码重定位时使用它,请使用 RIP 相对寻址。将 bufferend 更改为 bufferend(%rip)

0', %dl # turn into printable character cmp

相反,如果您想将缓冲区实际存储在代码段中,这是可以完成的。您必须使用例如mprotect(2)buffer 所在的页面标记为可写。这里在汇编中没有进行错误检查(并假设 sysconf(_SC_PAGE_SIZE) == 4096):


现在,即使缓冲区位于代码段中,代码也应该可以工作。顺便说一句,我在示例中使用 sys_exit ,因为我正在使用 gcc xs -nostdlib -ggdb -o x 将其作为独立文件进行测试。

Update2:要在代码重定位时使用它,请使用 RIP 相对寻址。将 bufferend 更改为 bufferend(%rip)

9', %dl # handle digits > 10 jbe 2f add

相反,如果您想将缓冲区实际存储在代码段中,这是可以完成的。您必须使用例如mprotect(2)buffer 所在的页面标记为可写。这里在汇编中没有进行错误检查(并假设 sysconf(_SC_PAGE_SIZE) == 4096):


现在,即使缓冲区位于代码段中,代码也应该可以工作。顺便说一句,我在示例中使用 sys_exit ,因为我正在使用 gcc xs -nostdlib -ggdb -o x 将其作为独立文件进行测试。

Update2:要在代码重定位时使用它,请使用 RIP 相对寻址。将 bufferend 更改为 bufferend(%rip)

A'-'0'-10, %dl # adjust number so A=10, B=11 ... 2: sub $1, %rcx # subtract before adding character as we start from the end mov %dl, (%rcx) # store character in string and %rax, %rax # rax == 0? jnz 1b # if not, then keep working mov %rcx, %rsi # buf = address of last character stored lea 64(%rsp), %rdx # calculate length by subtracting buffer end sub %rcx, %rdx # from the buffer start movq $1, %rax # sys_write movq $1, %rdi # fd = STDOUT_FILENO syscall add $72, %rsp # adjust stack back ret

相反,如果您想将缓冲区实际存储在代码段中,这是可以完成的。您必须使用例如mprotect(2)buffer 所在的页面标记为可写。这里在汇编中没有进行错误检查(并假设 sysconf(_SC_PAGE_SIZE) == 4096):

现在,即使缓冲区位于代码段中,代码也应该可以工作。顺便说一句,我在示例中使用 sys_exit ,因为我正在使用 gcc xs -nostdlib -ggdb -o x 将其作为独立文件进行测试。

Update2:要在代码重定位时使用它,请使用 RIP 相对寻址。将 bufferend 更改为 bufferend(%rip)

You have to convert it to text first. You can go the easy route and use e.g. printf from libc or, if you're so inclined, write your own conversion utility.

Update: If you want the code to be position independent it's easier to use the stack. Simply moving the buffer into the code segment as the code you linked to in the comments doesn't work anymore since modern processors have code segments as read only. I have updated the code to use the stack for temporary storage.

    .text

    call start
start:
    movq    $186, %rax # sys_gettid
    syscall

    movq %rax, %rdi
    movq $10, %rsi
    call print_number

    #ret
    mov $60, %rax # sys_exit
    mov $0, %rdi
    syscall

# rdi = number
# rsi = base
print_number:
    sub $72, %rsp # alloc room for buffer on the stack, 8 more than needed

    lea 64(%rsp), %rcx # 64(%rsp) == buffer end
    movq %rdi, %rax # rax holds current number 
1:
    xorq %rdx, %rdx # clear rdx (div uses the 128-bit number in rdx:rax)
    divq %rsi # divide by base, updating rax for the next iteration 
              # and giving us our digit in rdx (dl)
    add 

If, perversely, you want to actually store the buffer in the code segment it can be done. You have to mark the page where buffer resides as writable using e.g. mprotect(2). Here done without error checking (and assuming sysconf(_SC_PAGE_SIZE) == 4096) in assembly:

     mov $10, %rax # sys_mprotect
     lea buffer, %rdi # addr
     and $-4096, %rdi # page align
     mov $4096, %rsi # len
     mov $7, %rdx #prot = PROT_READ|PROT_WRITE|PROT_EXEC
     syscall

And now the code should work even when the buffer is located in the code segment. BTW I use sys_exit in my example because I'm testing it as a stand alone file with gcc x.s -nostdlib -ggdb -o x.

Update2: To use it when the code is relocated use RIP-relative addressing. Change bufferend to bufferend(%rip).

0', %dl # turn into printable character cmp

If, perversely, you want to actually store the buffer in the code segment it can be done. You have to mark the page where buffer resides as writable using e.g. mprotect(2). Here done without error checking (and assuming sysconf(_SC_PAGE_SIZE) == 4096) in assembly:


And now the code should work even when the buffer is located in the code segment. BTW I use sys_exit in my example because I'm testing it as a stand alone file with gcc x.s -nostdlib -ggdb -o x.

Update2: To use it when the code is relocated use RIP-relative addressing. Change bufferend to bufferend(%rip).

9', %dl # handle digits > 10 jbe 2f add

If, perversely, you want to actually store the buffer in the code segment it can be done. You have to mark the page where buffer resides as writable using e.g. mprotect(2). Here done without error checking (and assuming sysconf(_SC_PAGE_SIZE) == 4096) in assembly:


And now the code should work even when the buffer is located in the code segment. BTW I use sys_exit in my example because I'm testing it as a stand alone file with gcc x.s -nostdlib -ggdb -o x.

Update2: To use it when the code is relocated use RIP-relative addressing. Change bufferend to bufferend(%rip).

A'-'0'-10, %dl # adjust number so A=10, B=11 ... 2: sub $1, %rcx # subtract before adding character as we start from the end mov %dl, (%rcx) # store character in string and %rax, %rax # rax == 0? jnz 1b # if not, then keep working mov %rcx, %rsi # buf = address of last character stored lea 64(%rsp), %rdx # calculate length by subtracting buffer end sub %rcx, %rdx # from the buffer start movq $1, %rax # sys_write movq $1, %rdi # fd = STDOUT_FILENO syscall add $72, %rsp # adjust stack back ret

If, perversely, you want to actually store the buffer in the code segment it can be done. You have to mark the page where buffer resides as writable using e.g. mprotect(2). Here done without error checking (and assuming sysconf(_SC_PAGE_SIZE) == 4096) in assembly:

And now the code should work even when the buffer is located in the code segment. BTW I use sys_exit in my example because I'm testing it as a stand alone file with gcc x.s -nostdlib -ggdb -o x.

Update2: To use it when the code is relocated use RIP-relative addressing. Change bufferend to bufferend(%rip).

埋情葬爱 2024-12-08 02:21:05

您不能编写纯数字(因为它们将被解释为指针),您需要将该数字转换为字符串并将指针作为 write 的参数传递给该字符串。类似于这个的东西。

you can't write pure numbers (as they will be interpreted as pointers), you need to convert that number to a string and pass the pointer to that string as an arg of write. something along the lines of this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文