Inline Assembly -- Access local char* variable -- gcc - gcc

I am trying to write a simple program that prints out a C string without using one of the linux system calls or the standard C library functions. This is for learning purposes only, and I would never do this in production (unless I got really good at it =)).
First my system info:
[mehoggan#fedora sandbox-print_chars]$ uname -a
Linux fedora.laptop 2.6.35.14-106.fc14.i686.PAE #1 SMP Wed Nov 23 13:39:51 UTC 2011 i686 i686 i386 GNU/Linux
[mehoggan#fedora sandbox-print_chars]$ gcc --version
gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Next the code:
#include <unistd.h>
#include <sys/syscall.h>
void main()
{
char *str = "Hello World";
while(*(str) != '\0') {
//printf("%c", *(str++));
//syscall(__NR_write, 1, *(str++), 1);
__asm__( "movl %0, %%ecx" :"=c" (str));
__asm__( "movl $0X4, %eax" );
__asm__( "movl $0X1, %ebx" );
__asm__( "movl $0X1, %edx" );
__asm__( "int $0X80" );
str++;
}
return;
}
Compiled with the following makefile:
all: sandbox_c
sandbox_c: sandbox.c
gcc -Wall -o sandbox_c ./sandbox.c
gcc -S -Wall -o sandbox_c.asm ./sandbox.c
Things compile just fine, I just cant get the syntax right to get the thing to work. Your corrections are greatly appreciated, but if you could also point me to how you obtained the solution that would be great. I am trying to get better at using the man pages etc.
ADDITION
Running the executable through gdb I can see that ecx is not being pointed at the right address:
[mehoggan#fedora sandbox-print_chars]$ gdb ./sandbox_c
GNU gdb (GDB) Fedora (7.2-52.fc14)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/mehoggan/Code/Assembly/sandbox/sandbox-print_chars/sandbox_c...done.
(gdb) break sandbox.c:8
Breakpoint 1 at 0x80483a2: file ./sandbox.c, line 8.
(gdb) run
Starting program: /home/mehoggan/Code/Assembly/sandbox/sandbox-print_chars/sandbox_c
Breakpoint 1, main () at ./sandbox.c:8
8 while(*(str) != '\0') {
Missing separate debuginfos, use: debuginfo-install glibc-2.13-2.i686
(gdb) step
11 __asm__( "movl %0, %%ecx" :"=c" (str));
(gdb) info registers
eax 0x48 72
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x567ff4 5668852
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483a4 0x80483a4 <main+16>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) step
12 __asm__( "movl $0X4, %eax" );
(gdb) info registers
eax 0x48 72
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x34092fad 873017261
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483ab 0x80483ab <main+23>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) step
13 __asm__( "movl $0X1, %ebx" );
(gdb) info registers
eax 0x4 4
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x34092fad 873017261
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483b0 0x80483b0 <main+28>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) step
14 __asm__( "movl $0X1, %edx" );
(gdb) info registers
eax 0x4 4
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x1 1
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483b5 0x80483b5 <main+33>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) step
15 __asm__( "int $0X80" );
(gdb) info registers
eax 0x4 4
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x1 1
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483ba 0x80483ba <main+38>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) step
16 str++;
(gdb) info registers
eax 0xfffffff2 -14
ecx 0x34092fad 873017261
edx 0x1 1
ebx 0x1 1
esp 0xbffff2a4 0xbffff2a4
ebp 0xbffff2b8 0xbffff2b8
esi 0x0 0
edi 0x0 0
eip 0x80483bc 0x80483bc <main+40>
eflags 0x200206 [ PF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

Try this:
__asm__ volatile ( "movl $0X4, %eax
movl $0X1, %ebx
movl $0X1, %edx
int $0X80"
: /* outputs: */ /* none */
: /* inputs: */ "c" (str)
: /* clobbers: */ "eax", "ebx", "edx");
I've not tested it, but the syntax looks right. You might need to add some more "clobbers" if the syscall overwrites anything else - check the documentation.
Breaking it down:
There's no need to move %0 to %ecx because the "c" constraint already did that.
str is an input, but you had it as an output.
volatile tells the compiler not to remove it - it has no output so the compiler might think it can.
You need to tell the compiler what registers are 'clobbered' by the code. I've added the obvious ones, but the system call might clobber more?
You need to put them in all one asm or the compiler might think it can move them around.

For whatever it's worth, calling "int 0x80" is making a system call.
You're just not using "printf", or using the standard C library wrappers for Linux syscalls.
And there's nothing wrong with that :)
Anyway, here's a complete example illustrating EXACTLY what you're after:
http://asm.sourceforge.net/intro/hello.html
'Hope that helps ... and have fun!

Related

VMWare Workstation's strange bootloader behaviour (failing to print a character)

.code16
.text
.org 0x0
.global _start
_start:
jmp _testing
nop
_testing:
mov $0x0E, %ah
mov the_byte, %al #the line in question
int $0x10
jmp .
the_byte: .byte 0x41
.fill (510-(.-_start)), 1, 0
.word 0xAA55
This simple 'bootloader', as it were, is supposed to print out A to the screen, but it fails to do so when I'm using VMWare Workstation 16 (unlike Bochs, which happily shows A on its screen). If I change the line in question to
mov $0x41, %al
I can see A on VMWare Workstation as well.
Have you by any chance got any idea what can be causing such strange behaviour?
PS. It is indeed loaded into 0x7C00 with a separate linker file.
Apparently, mov the_byte, %al is assembled into something akin to mov %ds:0x7C0C, %al, hence DS had to be zeroed out (along with other segment pointers):
cli
mov %cs, %ax # CS is set to 0x0
mov %ax, %es # ES = CS = 0x0
mov %ax, %ds # DS = CS = 0x0
mov %ax, %ss # SS = CS = 0x0
sti

Assembly, Reserving Space with resq in YASM

Using YASM I have tried to reserve space for 2000 quadwords, but when I
do this I get a SIGSEGV when I try to write into the reserved block of quadwords.
If I reserve space for only 300 quadwords, the program runs without error.
What causes this?
; Using Windows 7 (Intel Celeron 64-bits)
; yasm -f win64 -l forth.lst forth.asm
; gcc -o forth forth.obj
segment .data
controlstr db "%x", 13, 10, 0
segment .bss
dictionaryspace resq 2000
datastackspace resq 300
databottom dq 0
returnstackspace resq 300
returnbottom dq 0
segment .text
global main
extern printf
main:
push rbp ; setup stack frame
mov rbp, rsp
sub rsp, 32 ; reserve space
lea r15, [databottom] ; initialize data stack pointer
sub r15, 8 ; point to the last word in data stack
mov rax, 666
mov [r15], rax ; SIGSEGV happens here.
mov rdx, [r15]
lea rcx, [controlstr]
call printf
leave
ret
; End of code.

Program will assemble and link but not run(ASM) on OSX [duplicate]

The following program compiles without errors, but when run it doesn't prompt for any input and nothing prints. What's the problem, and how can I fix it?
I use these commands to assemble and link:
/usr/local/bin/nasm -f macho32 $1
ld -macosx_version_min 10.9.0 -lSystem -o run $filename.o -e _start -lc
My code is:
section .data
;New line string
NEWLINE: db 0xa, 0xd
LENGTH: equ $-NEWLINE
section .bss
INPT: resd 1
section .text
global _start
_start:
;Read character
mov eax, 0x3
mov ebx, 0x1
mov ecx, INPT
mov edx, 0x1
int 80h
;print character
mov eax, 0x4
mov ebx, 0x1
mov ecx, INPT
mov edx, 0x1
int 80h
;Print new line after the output
mov eax, 0x4
mov ebx, 0x1
mov ecx, NEWLINE
mov edx, LENGTH
int 0x80
;Terminate
mov eax, 0x1
xor ebx, ebx
int 0x80
There are signs in your code that you may have been using a Linux tutorial when producing code for OS/X(BSD). Linux and OS/X have differing SYSCALL calling conventions. In OS/X 32-bit programs int 0x80 requires parameters (except the syscall in EAX) to be passed on a stack.
The important things to be aware of with 32-bit SYSCALLs via int 0x80 on OS/X are:
arguments passed on the stack, pushed right-to-left
you must allocate an additional 4 bytes (a DWORD) on the stack after you push all the arguments
syscall number in the eax register
call by interrupt 0x80
After pushing arguments on the stack in reverse order for int 0x80 you must allocate an additional 4 bytes (a DWORD) on the stack. The value in that memory location on the stack doesn't matter. This requirement is an artifact from an old UNIX convention.
A list of the SYSCALL numbers and their parameters can be found in the APPLE header files. You'll need these SYSCALLs:
1 AUE_EXIT ALL { void exit(int rval); }
3 AUE_NULL ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); }
4 AUE_NULL ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); }
I have commented some example code that would be similar in functionality to what you may have been attempting to achieve:
section .data
;New line string
NEWLINE: db 0xa, 0xd
LENGTH: equ $-NEWLINE
section .bss
INPT: resd 1
global _start
section .text
_start:
and esp, -16 ; Make sure stack is 16 byte aligned at program start
; not necessary in this example since we don't call
; external functions that conform to the OS/X 32-bit ABI
push dword 1 ; Read 1 character
push dword INPT ; Input buffer
push dword 0 ; Standard input = FD 0
mov eax, 3 ; syscall sys_read
sub esp, 4 ; Extra 4 bytes on stack needed by int 0x80
int 0x80
add esp, 16 ; Restore stack
push dword 1 ; Print 1 character
push dword INPT ; Output buffer = buffer we read characters into
push dword 1 ; Standard output = FD 1
mov eax, 4 ; syscall sys_write
sub esp, 4 ; Extra 4 bytes on stack needed by int 0x80
int 0x80
add esp, 16 ; Restore stack
push dword LENGTH ; Number of characters to write
push dword NEWLINE ; Write the data in the NEWLINE string
push dword 1 ; Standard output = FD 1
mov eax, 4 ; syscall sys_write
sub esp, 4 ; Extra 4 bytes on stack needed by int 0x80
int 0x80
add esp, 16 ; Restore stack
push dword 0 ; Return value from program = 0
mov eax, 1 ; syscall sys_exit
sub esp, 4 ; Extra 4 bytes on stack needed by int 0x80
int 0x80
The and esp, -16 is only necessary if you need to align the stack to a 16-byte boundary as a baseline for future stack operations. If you intend to call external functions that conform to the OS/X 32-bit ABI the stack is expected to be 16-byte aligned immediately preceding a function CALL. This alignment is not necessary for system calls via int 0x80.
You should be able to assemble and link it with:
nasm -f macho32 test.asm -o test.o
ld -macosx_version_min 10.9.0 -o test test.o -e _start -lSystem
And run it with:
./test

Bus error when executing `emms` MMX instruction

I'm working on a port of some software with inline assembly because we took a few bug reports from a Debian maintainer under X32. The code is fine under both X86 and X64.
We're catching a bus error on the emms instruction:
...
0x005520fd <+3885>: pop %rsp
0x005520fe <+3886>: emms
=> 0x00552100 <+3888>: pop %rbx
0x00552101 <+3889>: jmpq 0x5519e3
0x00552106 <+3894>: nopw %cs:0x0(%rax,%rax,1)
...
According to the manual, the following exceptions are raised:
Exceptions:
RM PM VM SMM Description
#UD #UD #UD #UD If CR0.EM = 1
#NM #NM #NM #NM If CR0.TS = 1
#MF #MF #MF #MF If pending FPU Exception
Here is the mask used in the MMX status register:
mxcsr 0x1f80 [ IM DM ZM OM UM PM ]
I don't believe I have access to the control registers to determine what actually caused the exception, so I'm having trouble locating the cause of the bus error.
What are some of the potential causes of the bus error? Or how can I trouble shoot this further?
Here's info float:
(gdb) info float
R7: Empty 0xffffffffffffffffffff
R6: Empty 0xffffa5a5a5a5a5a5a5a5
R5: Empty 0xfffffedcba9876543210
R4: Empty 0xffffb182db48cf349120
R3: Empty 0xffff926cd0b6a839b535
R2: Empty 0xfffff373de2d49584e7a
R1: Empty 0xffff16166e76b1bb925f
=>R0: Empty 0xffff24f0130c63ac9332
Status Word: 0x0000
TOP: 0
Control Word: 0x037f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round to nearest
Tag Word: 0xffff
Instruction Pointer: 0x00:0x00000000
Operand Pointer: 0x00:0x00000000
Opcode: 0x0000
And here's from info registers:
(gdb) info registers
rax 0xffffcb58 0xffffcb58
rbx 0x30 0x30
rcx 0x14f3 0x14f3
rdx 0x61d560 0x61d560
rsi 0xffffcb08 0xffffcb08
rdi 0x14 0x14
rbp 0xffffcb58 0xffffcb58
rsp 0xb62f7cbfffffc8d8 0xb62f7cbfffffc8d8
r8 0x0 0x0
r9 0x40 0x40
r10 0x2e676e696e6e7572 0x2e676e696e6e7572
r11 0x246 0x246
r12 0x9028a0 0x9028a0
r13 0xffffcaf0 0xffffcaf0
r14 0x8f6120 0x8f6120
r15 0xffffca6c 0xffffca6c
rip 0x552100 0x552100
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 0x33
ss 0x2b 0x2b
ds 0x2b 0x2b
es 0x2b 0x2b
fs 0x63 0x63
gs 0x0 0x0
Here's a breakout of the MMX status register bits:
IM - Invalid Operation Mask
DM - Denormalized Mask
ZM - Divide By Zero Mask
OM - Overflow Mask
UM - Underflow Mask
PM - Precision Mask
EMMS is executing without problem, as shown the arrow and the value of rip the fault is with the following pop because rsp points to invalid memory. The correct value of rsp is something less than 0x100000000.

Cannot view memory in stack with gdb

I'm reversing a malware on Mac OS with gdb.
Then I try to view a local variable in stack,Gdb tells me:"Cannot access memory at address 0xbffffd58".Why?
(gdb) ni
0x000086cc in ?? ()
=> 0x000086cc: 85 c0 test eax,eax
(gdb) i r
eax 0xbffffe0b -1073742325
ecx 0xbffffd58 -1073742504
edx 0x190fc 102652
ebx 0x868e 34446
esp 0xbffffb10 0xbffffb10
ebp 0xbffffb58 0xbffffb58
esi 0x1 1
edi 0x17e9a 97946
eip 0x86cc 0x86cc
eflags 0x302 [ TF IF ]
cs 0x1b 27
ss 0x23 35
ds 0x23 35
es 0x23 35
fs 0x0 0
gs 0xf 15
(gdb) ni
0x000086ce in ?? ()
=> 0x000086ce: 74 e4 je 0x86b4
(gdb) ni
0x000086d0 in ?? ()
=> 0x000086d0: 80 38 2d cmp BYTE PTR [eax],0x2d
(gdb) x/3cb $eax
0xbffffe0b: Cannot access memory at address 0xbffffe0b
(gdb) ni
0x000086d3 in ?? ()
=> 0x000086d3: 75 df jne 0x86b4
(gdb) ni
0x000086b4 in ?? ()
=> 0x000086b4: bf ff ff ff ff mov edi,0xffffffff
(gdb)
Well,I think it is a bug in GDB:
(gdb) x/12i $pc-0x2a
0x2473: push ebx
0x2474: call 0x2479
0x2479: pop ebx
0x247a: sub esp,0x34
0x247d: lea edx,[ebp-0x19]
0x2480: mov DWORD PTR [esp],edx
0x2483: mov DWORD PTR [esp+0x8],0x6
0x248b: lea eax,[ebx+0x15a3e]
0x2491: mov DWORD PTR [esp+0x4],eax
0x2495: call 0xd900
0x249a: mov DWORD PTR [esp],eax
=> 0x249d: call 0x300b3
(gdb) x/10xb $eax
0xbffffcdf: Cannot access memory at address 0xbffffcdf
(gdb) x/1xw $esp
0xbffffcc0: 0xbffffcdf
(gdb) x/10xb 0xbffffcdf
0xbffffcdf: 0x2f 0x74 0x6d 0x70 0x00 0x05 0x7e 0x01
0xbffffce7: 0x00 0x27
(gdb) ni
0x000300b3 in ?? ()
=> 0x000300b3: e8 48 42 de 8f call 0x8fe14300
(gdb)
you see? Examine the same address twice,I get two different results.
And then,I input 'ni' command ,GDB doesn't stepover!
Gdb doesn't work well on Mac OS X?

Resources