When we looking on assembler at x86 CPU , syscall look like:
0F 05 syscall ; LINUX - sys_nanosleep
48 3D 01 F0 FF FF cmp rax, 0FFFFFFFFFFFFF001h
When are we talking about ARM CPU what is the convention how syscall looks like in assembler?
The source code for the musl libc library may help: all supported architectures have a small header file implementing the 'syscalls'.
x86_64:
static __inline long __syscall0(long n)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory");
return ret;
}
Arm:
#ifdef __thumb__
/* Avoid use of r7 in asm constraints when producing thumb code,
* since it's reserved as frame pointer and might not be supported. */
#define __ASM____R7__
#define __asm_syscall(...) do { \
__asm__ __volatile__ ( "mov %1,r7 ; mov r7,%2 ; svc 0 ; mov r7,%1" \
: "=r"(r0), "=&r"((int){0}) : __VA_ARGS__ : "memory"); \
return r0; \
} while (0)
#else
#define __ASM____R7__ __asm__("r7")
#define __asm_syscall(...) do { \
__asm__ __volatile__ ( "svc 0" \
: "=r"(r0) : __VA_ARGS__ : "memory"); \
return r0; \
} while (0)
#endif
Aarch64:
#define __asm_syscall(...) do { \
__asm__ __volatile__ ( "svc 0" \
: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \
return x0; \
} while (0)
Example for generated code:
/* syscall.c */
#define __asm_syscall(...) do { \
__asm__ __volatile__ ( "svc 0" \
: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \
return x0; \
} while (0)
static inline long __syscall0(long n)
{
register long x8 __asm__("x8") = n;
register long x0 __asm__("x0");
__asm_syscall("r"(x8));
}
void test(void) {
__syscall0(1);
}
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-objdump -D syscall.o
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc -c -o syscall.o syscall.c
Disassembly of section .text:
0000000000000000 <__syscall0>:
0: d10043ff sub sp, sp, #0x10
4: f90007e0 str x0, [sp, #8]
8: f94007e8 ldr x8, [sp, #8]
c: d4000001 svc #0x0
10: 910043ff add sp, sp, #0x10
14: d65f03c0 ret
0000000000000018 <test>:
18: a9bf7bfd stp x29, x30, [sp, #-16]!
1c: 910003fd mov x29, sp
20: d2800020 mov x0, #0x1 // #1
24: 97fffff7 bl 0 <__syscall0>
28: d503201f nop
2c: a8c17bfd ldp x29, x30, [sp], #16
30: d65f03c0 ret
This being said, the arm documentation cannot really be called garbage, even though you found difficult to find the exact information you were looking for: The Exploration Tools section of their web site is excellent IMHO.
You can find the pseudo-code for the SVC instructions and their exact encodings here and here, and you even could simulate the instructions: Alastair Reid wrote a couple of fascinating articles here regarding ISA formal specifications.
Related
Kernel version : 4.14.199
The spin_lock assembly instructions in crash is
crash_arm64> dis _raw_spin_lock -x
0xffffff8008c41e90 <_raw_spin_lock>: stp x29, x30, [sp,#-32]!
0xffffff8008c41e94 <_raw_spin_lock+0x4>: str x19, [sp,#16]
0xffffff8008c41e98 <_raw_spin_lock+0x8>: mov x29, sp
0xffffff8008c41e9c <_raw_spin_lock+0xc>: mov x19, x0
0xffffff8008c41ea0 <_raw_spin_lock+0x10>: nop
0xffffff8008c41ea4 <_raw_spin_lock+0x14>: mov w0, #0x1 // #1
0xffffff8008c41ea8 <_raw_spin_lock+0x18>: bl 0xffffff80080f399c <preempt_count_add>
0xffffff8008c41eac <_raw_spin_lock+0x1c>: mov w10, #0x10000 // #65536
0xffffff8008c41eb0 <_raw_spin_lock+0x20>: .inst 0xb8aa0268 ; undefined
0xffffff8008c41eb4 <_raw_spin_lock+0x24>: nop
0xffffff8008c41eb8 <_raw_spin_lock+0x28>: nop
0xffffff8008c41ebc <_raw_spin_lock+0x2c>: nop
0xffffff8008c41ec0 <_raw_spin_lock+0x30>: eor w9, w8, w8, ror #16
0xffffff8008c41ec4 <_raw_spin_lock+0x34>: cbz w9, 0xffffff8008c41edc <_raw_spin_lock+0x4c>
0xffffff8008c41ec8 <_raw_spin_lock+0x38>: sevl
0xffffff8008c41ecc <_raw_spin_lock+0x3c>: wfe
0xffffff8008c41ed0 <_raw_spin_lock+0x40>: ldaxrh w10, [x19]
0xffffff8008c41ed4 <_raw_spin_lock+0x44>: eor w9, w10, w8, lsr #16
0xffffff8008c41ed8 <_raw_spin_lock+0x48>: cbnz w9, 0xffffff8008c41ecc <_raw_spin_lock+0x3c>
0xffffff8008c41edc <_raw_spin_lock+0x4c>: ldr x19, [sp,#16]
0xffffff8008c41ee0 <_raw_spin_lock+0x50>: ldp x29, x30, [sp],#32
0xffffff8008c41ee4 <_raw_spin_lock+0x54>: ret
What's the meaning of .inst instructions ?
0xffffff8008c41eb0 <_raw_spin_lock+0x20>: .inst 0xb8aa0268 ; undefined
I found the function definition in arch/arm64/include/asm/spinlock.h.
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int tmp;
arch_spinlock_t lockval, newval;
asm volatile(
/* Atomically increment the next ticket. */
ARM64_LSE_ATOMIC_INSN(
/* LL/SC */
" prfm pstl1strm, %3\n"
"1: ldaxr %w0, %3\n"
" add %w1, %w0, %w5\n"
" stxr %w2, %w1, %3\n"
" cbnz %w2, 1b\n",
/* LSE atomics */
" mov %w2, %w5\n"
" ldadda %w2, %w0, %3\n"
__nops(3)
)
/* Did we get the lock? */
" eor %w1, %w0, %w0, ror #16\n"
" cbz %w1, 3f\n"
/*
* No: spin on the owner. Send a local event to avoid missing an
* unlock before the exclusive load.
*/
" sevl\n"
"2: wfe\n"
" ldaxrh %w2, %4\n"
" eor %w1, %w2, %w0, lsr #16\n"
" cbnz %w1, 2b\n"
/* We got the lock. Critical section starts here. */
"3:"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
: "memory");
}
In my opinion, the .inst 0xb8aa0268 should correspond to ldadda %w2, %w0, %3\n".
Why the crash displayed is different from the source code ?
I am developing a kernel in C and created something to print on screen on video memory. I expected that the first byte in video memory would be the character to print and the second byte tells the color. But my program has something different but it works!! It is very unexpected and unusual.
My kernel code -
#define VIDEO_MEM 0xb8000
void write_string( int colour, const unsigned char *string );
void main()
{
unsigned char *vid = (unsigned char*) VIDEO_MEM;
int i=0;
for (i = 0; i < 2000; i++)
{
*vid = ' ';
*(vid+2) = 0x1f;
vid += 2;
}
write_string(0x1f,"The Kernel has been loaded successfully!!");
}
void write_string( int colour, const unsigned char *string ) {
unsigned char *vid = (unsigned char*) VIDEO_MEM;
while(*string != 0)
{
*(vid) = *string;
*(vid+2) = colour;
++string;
vid+=2;
}
}
It prints the character on *vid and the color on *(vid+2) and then increments the vid by 2. It should then replace and print the next char on *(vid+2). So, the color should go but it still works.
Also, the color should be on *(vid+1)
When I use *(vid+1) instead of *(vid+2) to print the string, the screen shows down arrow characters (with ACII code 0x1f which I wanted to be the color) replacing the entire string.
Why does the code behave so unusual??
Can anyone help?
EDIT
I have edited my code and now it prints string. But another problem arose. I added a support for printing on particular line number. But now this shifts the string backwards by one character.
void write_string( int colour, const unsigned char *string, int pos ) {
unsigned char *vid = (unsigned char*) VIDEO_MEM;
vid+=pos*160;
while(*string != 0)
{
*vid = colour;
*(vid+1) = *string;
++string;
vid+=2;
}
}
So, If I tell it to print on line 10, it prints the first character on the last character of the 9th line and then continues.
I also have a character printing function that justs prints curly braces (}) instead of the given character and that too one character backwards of the given position (like the error in the write_string function). Also it doen't change the character background color given as argument.
void putChar(char character, short col, short row, char attr) {
unsigned char* vid_mem = (unsigned char *) VIDEO_MEM;
int offset = (row*80 + col)*2;
vid_mem += offset;
if(!attr) {
attr = 0x0f;
}
*vid_mem = (attr<<8)+character;
}
EDIT 2
My Boot Loader:
[org 0x7c00]
KERNEL equ 0x1000
mov [BOOT_DRIVE],dl
mov bp,0x9000
mov sp,bp
mov bx, msgReal
call print_string
call load_kernel
call switch_to_pm
jmp $
%include 'boot/bios.ASM'
%include 'boot/gdt.ASM'
%include 'boot/protected_mode.ASM'
%include 'boot/print32.ASM'
[bits 16]
load_kernel:
mov bx,msgKernel
call print_string
mov bx, KERNEL
mov dh, 15
mov dl, [BOOT_DRIVE]
call disk_load
ret
[bits 32]
BEGIN_PM:
mov ebx, msgProt
call print_string32
call KERNEL
jmp $
BOOT_DRIVE db 0
msgReal db "Booted in 16-bit mode",0
msgProt db "Successfully switched to 32-bit mode",0
msgKernel db "Loading the kernel onto memory",0
times 510-($-$$) db 0
dw 0xaa55
bios.ASM -
;BIOS Functions
[bits 16]
print_string:
pusha
mov cx,bx
mov ah,0x0e
printStringStart:
mov al,[bx]
cmp al,0
je done
int 0x10
inc bx
jmp printStringStart
done:
popa
ret
print_word:
pusha
mov ax,0x0000
mov cl,0x10
mov al,bh
div cl
call printDig
mov al,bh
and al,0x0f
call printDig
mov ax,0x0000
mov al,bl
div cl
call printDig
mov al,bl
and al,0x0f
call printDig
popa
ret
printDig:
cmp al,0x9
jg alpha
add al,'0'
mov ah,0x0e
int 0x10
jmp pDigDone
alpha:
sub al,0xa
add al,'A'
mov ah,0x0e
int 0x10
pDigDone:
ret
hex_prefix: db '0x',0
disk_load:
push dx
mov ah,0x02
mov al,dh
mov ch,0x00
mov dh,0x00
mov cl,0x02
int 0x13
jc disk_error
pop dx
cmp dh,al
jne disk_error
ret
disk_error:
mov ah,0x0e
mov al,'X'
int 0x10
mov bx,errMsg
call print_string
jmp $
errMsg:
db "Disk Read Error....."
times 80-20 db " "
db 0
gdt.ASM -
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
protected_mode.ASM -
[bits 16]
switch_to_pm:
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm
[bits 32]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp,0x90000
mov esp,0x90000
call BEGIN_PM
print32.ASM -
[bits 32]
VIDEO_MEM equ 0xb8000
DEF_COLOR equ 0x0f
print_string32:
pusha
mov edx,VIDEO_MEM
print_string32_loop:
mov al, [ebx]
mov ah, DEF_COLOR
cmp al,0
je print_string32_end
mov [edx],ax
inc ebx
add edx,2
jmp print_string32_loop
print_string32_end:
popa
ret
I also add a kernel_start.asm file just before the kernel while linking to call the main function -
[bits 32]
[extern main]
call main
jmp $
And here's my make file -
C_SOURCES = $(wildcard drivers/*.c kernel/*.c)
HEADERS = $(wildcard kernel/*.h drivers/*.h)
OBJ = ${C_SOURCES:.c=.o}
all: os-image
os-image: boot/boot_sector.bin kernel.bin
cat $^ > $#
kernel.bin: kernel/kernel_start.o ${OBJ}
ld -o $# -Ttext 0x1000 $^ --oformat binary
%.o : %.c
gcc -std=c99 -Wall -pedantic -ffreestanding -c $< -o $#
%.o : %.asm
nasm $< -f elf64 -o $#
%.bin : %.asm
nasm $< -f bin -o $#
clean:
rm -fr kernel/*.o
rm -fr drivers/*.o
rm -fr boot/*.bin
rm -fr os-image *.bin *.o
With the changes suggested in other answer and comments, your problem doesn't seem to be reproducible for me. The following code works for me. I've tried to maintain how you coded it just so it makes sense to you:
#define VIDEO_MEM 0xb8000
void write_string( unsigned char colour, const char *string );
void write_string_line( unsigned char colour, const char *string, int pos );
void putChar(char character, short col, short row, unsigned char attr);
/* Place this at top of file as first code in kernel.o */
__asm__ ("call main\r\n" \
"cli\r\n" \
"hlt\r\n"
);
void main()
{
volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
int i=0;
for (i = 0; i < 2000; i++)
{
*vid = ' ';
*(vid+1) = 0x1f;
vid += 2;
}
write_string(0x1f,"The Kernel has been loaded successfully!!");
write_string_line(0x1f,"Testing Here!!",1);
putChar('Z',3,3,0xf3);
}
void write_string( unsigned char colour, const char *string ) {
volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
while(*string != 0)
{
*(vid) = *string;
*(vid+1) = colour;
++string;
vid+=2;
}
}
void write_string_line( unsigned char colour, const char *string, int pos ) {
volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
vid+=pos*160;
while(*string != 0)
{
*vid = *string;
*(vid+1) = colour;
++string;
vid+=2;
}
}
void putChar(char character, short col, short row, unsigned char attr) {
volatile unsigned char* vid_mem = (unsigned char *) VIDEO_MEM;
int offset = (row*80 + col)*2;
vid_mem += offset;
if(!attr) {
attr = 0x0f;
}
*(unsigned short int *)vid_mem = (attr<<8)+character;
/* This would do the same as line above
*vid_mem = character;
*(vid_mem+1) = attr;
*/
}
I've added the __asm__ at the beginning to make sure that code is the first to appear in the generated object file. It likely works without it. I've modified all your *vid pointers to be volatile . Since video is memory mapped IO you don't want to have the compiler potentially remove screen writes when it optimizes. Likely your code will work without volatile, but it is proper to add it here to avoid potential problems.
When run BOCHS this code produces this screen output:
If you use the code provided here and it doesn't work that would suggest the issue you are having is likely related to the a code you write in your bootloader that read the disk, enabled A20, set the GDT, entered protected mode, and then called into your C code. It is also possible problems could occur depending on how you compile and link your kernel.
Likely Cause of Undefined Behavior
After all the code and the make file were made available in EDIT 2 it became clear that one significant problem was that most of the code was compiled and linked to 64-bit objects and executables. That code won't work in 32-bit protected mode.
In the make file make these adjustments:
When compiling with GCC you need to add -m32 option
When assembling with GNU Assembler (as) targeting 32-bit objects you need to use --32
When linking with LD you need to add the -melf_i386 option
When assembling with NASM targeting 32-bit objects you need to change -f elf64 to -f elf32
A preferable option to using a 64-bit compiler and tool chain from the host environment is to create a cross compiler toolchain for i686 or i386.
This should work.
Each VGA cell is of 2 bytes long, First byte stores Character while the second byte stores the color.
Also make sure you make marked the pointer volatile. To avoid any type of unexpected changes(or optimizations) made by the compiler on that local field.
void write_string( int colour, const unsigned char *string )
{
volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
while( *string != 0 )
{
*vid++ = *string++;
*vid++ = colour;
}
}
You use *(vid) for first video character for color
I have two files which are assembled/compiled/linked into minimalistic kernel.
start.s:
.set CPACR_EL1_FPEN, 0b11 << 20
.set BOOT_STACK_SIZE, 8 * 1024
.global __boot_stack
.global __start
.global __halt
.bss
.align 16
__boot_stack:
.fill BOOT_STACK_SIZE
.text
__start:
/* disable FP and SIMD traps */
mov x0, #CPACR_EL1_FPEN
msr cpacr_el1, x0
/* set stack */
adr x0, __boot_stack
add sp, x0, #BOOT_STACK_SIZE
/* call the Rust entry point */
bl __boot
__halt:
/* halt CPU */
wfi
b __halt
boot.rs:
#[no_mangle]
pub extern fn __boot() {
unsafe {
let ptr = 0x9000000 as *mut u8;
*ptr = '!' as u8;
}
}
For opt-level=3 the resulting code outputs single '!' to a serial port (as was intended). For opt-level=0 I have a strange infinite loop (e.g. '!!!!!!!!!....'). Here is the disassembled dump of the problematic code:
0000000000000000 <__kernel_begin>:
0: d2a00600 mov x0, #0x300000 // #3145728
4: d5181040 msr cpacr_el1, x0
8: 100007c0 adr x0, 100 <__boot_stack>
c: 9140081f add sp, x0, #0x2, lsl #12
10: 94000003 bl 1c <__boot>
0000000000000014 <__halt>:
14: d503207f wfi
18: 17ffffff b 14 <__halt>
000000000000001c <__boot>:
1c: a9bf7bfd stp x29, x30, [sp,#-16]!
20: 910003fd mov x29, sp
24: 94000003 bl 30 <aarch64::boot::__boot::__rust_abi>
28: a8c17bfd ldp x29, x30, [sp],#16
2c: d65f03c0 ret
0000000000000030 <aarch64::boot::__boot::__rust_abi>:
30: d10043ff sub sp, sp, #0x10
34: 52a12008 mov w8, #0x9000000 // #150994944
38: 2a0803e9 mov w9, w8
3c: f90007e9 str x9, [sp,#8]
40: 52800428 mov w8, #0x21 // #33
44: 39000128 strb w8, [x9]
48: 910043ff add sp, sp, #0x10
4c: d65f03c0 ret
The code is tested using qemu-system-aarch64. I don't see serious problems with it (except redundancy). Can you suggest a possible cause of such abnormal behaviour?
P.S. This is the optimised version which works properly:
0000000000000000 <__kernel_begin>:
0: d2a00600 mov x0, #0x300000 // #3145728
4: d5181040 msr cpacr_el1, x0
8: 1007ffc0 adr x0, 10000 <__boot_stack>
c: 9140081f add sp, x0, #0x2, lsl #12
10: 94000003 bl 1c <__boot>
0000000000000014 <__halt>:
14: d503207f wfi
18: 17ffffff b 14 <__halt>
000000000000001c <__boot>:
1c: 52a12008 mov w8, #0x9000000 // #150994944
20: 52800429 mov w9, #0x21 // #33
24: 39000109 strb w9, [x8]
28: d65f03c0 ret
I've succeeded to run the non-optimised code without abnormalities. Thanks to Notlikethat for the idea. My stack was just mapped into readonly memory.
So I've just added the offset statement into my linker script (". = 1024M;") in order to make all the symbols to start from 1GiB (where RAM begins). After this modification the code started to work properly.
i try writing a kernel module in assembler. In one time i needed a global vars. I define a dword in .data (or .bss) section, and in init function i try add 1 to var. My program seccesfully make, but insmod sey me:
$ sudo insmod ./test.ko
insmod: ERROR: could not insert module ./test.ko: Invalid module format
it's my assembler code in nasm:
[bits 64]
global init
global cleanup
extern printk
section .data
init_mess db "Hello!", 10, 0
g_var dd 0
section .text
init:
push rbp
mov rbp, rsp
inc dword [g_var]
mov rdi, init_mess
xor rax, rax
call printk
xor rax, rax
mov rsp, rbp
pop rbp
ret
cleanup:
xor rax, rax
ret
if i write adding in C code, all work good:
static i = 0;
static int __init main_init(void) { i++; return init(); }
But in this objdump -d test.ko write a very stainght code for me:
0000000000000000 <init_module>:
0: 55 push %rbp
1: ff 05 00 00 00 00 incl 0x0(%rip) # 7 <init_module+0x7>
7: 48 89 e5 mov %rsp,%rbp
a: e8 00 00 00 00 callq f <init_module+0xf>
f: 5d pop %rbp
10: c3 retq
What does this mean (incl 0x0(%rip))? How can I access memory? Please, help me :)
(My system is archlinux x86_64)
my C code for correct make a module:
#include <linux/module.h>
#include <linux/init.h>
MODULE_AUTHOR("Actics");
MODULE_DESCRIPTION("Description");
MODULE_LICENSE("GPL");
extern int init(void);
extern int cleanup(void);
static int __init main_init(void) { return init(); }
static void __exit main_cleanup(void) { cleanup(); }
module_init(main_init);
module_exit(main_cleanup);
and my Makefile:
obj-m := test.o
test-objs := inthan.o module.o
KVERSION = $(shell uname -r)
inthan.o: inthan.asm
nasm -f elf64 -o $# $^
build:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
Kernel mode lives in the "negative" (ie. top) part of the address space, where 32 bit absolute addresses can not be used (because they are not sign-extended). As you have noticed, gcc uses rip-relative addresses to work around this problem which gives offsets from the current instruction pointer. You can make nasm do the same by using the DEFAULT REL directive. See the relevant section in the nasm documentation.
you can always use inline assembly
asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0" \
54 : "=&r" (flag), "=r" (roksum) \
55 : "1" (addr), "g" ((long)(size)), \
56 "rm" (limit));
I have a problem with this shell code.
When I run the assembler code it prints the caracter, but when I call it as a function from c it does not.
I used gdb to test all the instruction executed and it seems to execute all the instructions.
It is very weird because I debugged both asm and c versions and they do the same but in the int 0x80 it prints nothing for the C code.
This is the C code:
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
int (*sc)();
/****************************************************************
0000000000000000 <main>: /
0: 48 31 c9 xor %rcx,%rcx /
3: 48 31 c0 xor %rax,%rax /
6: eb 13 jmp 1b <n> /
/
0000000000000008 <et>: /
8: 59 pop %rcx /
9: 48 31 c0 xor %rax,%rax /
c: 48 31 db xor %rbx,%rbx /
f: 48 31 d2 xor %rdx,%rdx /
12: b0 04 mov $0x4,%al /
14: b3 01 mov $0x1,%bl /
16: b2 01 mov $0x1,%dl /
18: cd 80 int $0x80 /
1a: c3 retq /
/
000000000000001b <n>: /
1b: e8 e8 ff ff ff callq 8 <et> /
/
0000000000000020 <abc>: /
20: 77 .byte 0x77 /
... /
******************************************************************/
char shellcode[] = "\x48\x31\xc9\x48\x31\xc0\xeb\x13\x59\x48\x31\xc0\x48\x31\xdb\x48\x31\xd2\xb0\x04\xb3\x01\xb2\x01\xcd\x80\xc3\xe8\xe8\xff\xff\xffw";
//char shellcode[] = "\x48\x31\xc9\x48\x31\xc0\xeb\x11\x59\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x48\x31\xc0\x48\xff\xc0\xcd\x80\xe8\xea\xff\xff\xffw";
int main(int argc, char **argv) {
char *ptr = mmap(0, sizeof(shellcode),
PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON
| MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(-1);
}
memcpy(ptr, shellcode, sizeof(shellcode));
sc = ptr;
(void)((void(*)())ptr)();
printf("\n");
return 0;
}
This is the nasm code:
global main
main: ; main
xor rcx, rcx ; eficient way turning register to 0
xor rax, rax ; exclusive or
jmp n
et:
pop rcx
xor rax, rax
xor rbx, rbx
xor rdx, rdx
mov al, 4 ; Number of system call (write)
mov bl, 1 ; argument(1=stdout)
mov dl, 1 ; number of characters
int 0x80
ret
n:
call et
abc: db 'w'
EDIT:
I have solved the problem.
In this webpage: http://www.exploit-db.com/papers/13065/ I found that in 64 bits the systemcalls must be called using syscall insted of int 0x80 and the registers of the arguments are different.
Then I found this other web page: http://cs.lmu.edu/~ray/notes/linuxsyscalls/. It have some examples about doing these systemcalls, think it is a good webpage.
But the question now is, why did it work with the Nasm code? Is it something about compatibility? May be the assembler code was running in compatibility mode because the assembler detected the int 0x80 instruction and in C the compiler couldn't because it cannot interpret the shellcode?
I leave here the working C code:
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
int (*sc)();
/****************************************************************
0000000000000000 <main>: /
0: 48 31 c9 xor %rcx,%rcx /
3: 48 31 c0 xor %rax,%rax /
6: eb 16 jmp 1e <n> /
/
0000000000000008 <et>: /
8: 5e pop %rsi /
9: 48 31 c0 xor %rax,%rax /
c: 48 31 db xor %rbx,%rbx /
f: 48 31 d2 xor %rdx,%rdx /
12: b0 01 mov $0x1,%al /
14: b3 01 mov $0x1,%bl /
16: 48 89 df mov %rbx,%rdi /
19: b2 01 mov $0x1,%dl /
1b: 0f 05 syscall /
1d: c3 retq /
/
000000000000001e <n>: /
1e: e8 e5 ff ff ff callq 8 <et> /
/
0000000000000023 <abc>: /
23: 77 .byte 0x77 /
/
****************************************************************/
char shellcode[] = "\x48\x31\xc9\x48\x31\xc0\xeb\x16\x5e\x48\x31\xc0\x48\x31\xdb\x48\x31\xd2\xb0\x01\xb3\x01\x48\x89\xdf\xb2\x01\x0f\x05\xc3\xe8\xe5\xff\xff\xffw";
//char shellcode[] = "\x48\x31\xc9\x48\x31\xc0\xeb\x13\x59\x48\x31\xc0\x48\x31\xdb\x48\x31\xd2\xb0\x04\xb3\x01\xb2\x01\xcd\x80\xc3\xe8\xe8\xff\xff\xffw";
//char shellcode[] = "\x48\x31\xc9\x48\x31\xc0\xeb\x11\x59\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x48\x31\xc0\x48\xff\xc0\xcd\x80\xe8\xea\xff\xff\xffw";
int main(int argc, char **argv) {
char *ptr = mmap(0, sizeof(shellcode),
PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON
| MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(-1);
}
memcpy(ptr, shellcode, sizeof(shellcode));
sc = ptr;
(void)((void(*)())ptr)();
printf("\n");
return 0;
}
You were probably building the ELF file as a 32 bit one. Even if your OS is 64 bits, if the binary is 32 then it runs in compatibility mode.
Same thing happens on Windows (it's called "Windows-on-Windows-64" or "WOW64").