Saving DNS response in assembly using syscalls - linux

So I am wanting to learn more about DNS and I am wanting to use assembly to accomplish the task.
I noticed in tcpdump that my request appears to be accurate and I receive a response back, but my code doesn't know how to handle this.
I looked on a few sites and it looks like a recvfrom should be used , but I don't understand how I would call that function. Another answer I see is to use DUP2 and from what I understand it redirects stdin,out and err . From what I have been reading , I should manipulate sockcall ( 0x66 ) to call the recvfrom and get the data that has been sent to me. Most of the examples are using TCP and don't quite fit here since I am using UDP for the DNS.
Here is my code which queries google for example.com
nop
nop
nop
nop
; we create a socket fd, using again syscall 0x66 and argument SYS_SOCKET so ebx = 1
push 0x66
pop eax
push 0x1
pop ebx
xor ecx,ecx
push ecx
; but this times it will be a SOCK_DGRAM UDP, so 0x2 as argument
push 0x2
push 0x2
mov ecx,esp
int 0x80
; saving fd
push 0x08080808 ; 8.8.8.8 ; I love that this doesn't really need to be backwards.
;push 0x0100007F ; 0100007F 1.0.0.127 for testing...
xor edx,edx
mov dh, 0x35 ; port 53; comment this for variable port
push dx ; comment this for variable port
; push word PORT ; UNcomment this for variable port
push word 0x2 ;
mov ecx,esp ; save pointer to ecx
push 0x10 ; addrlen
push ecx ; pointer to sockaddr
push eax ; fd received previously
mov ecx,esp ;
mov esi,eax ; save fd for next call
xor eax,eax
mov al,0x66
add bl,0x2
int 0x80
; now we send a UDP packet to open stateful firewall :]
xor eax,eax
mov al,0x66 ; ssize_t send(int sockfd, const void *buf, size_t len, int flags);
push 0x00000001
push 0x00010000
push 0x6d6f6303
push 0x656c706d
push 0x61786507
push 0x00000000
push 0x00000100
push 0x0001AAAA
mov edx,esp ; Move the string to EDX so we can send it.
xor ecx,ecx
push ecx
push 64 ; size of message to be sent is 8
push edx
push esi
mov ecx,esp
xor ebx,ebx
mov bl,0x9
int 0x80
mov ebx,esi
xor ecx,ecx
mov cl,0x2
loop: ;Not sure if this is needed at all
; syscall dup2
mov al,0x3f
int 0x80
dec ecx
jns loop
xor esi,esi
push esi
mov edx,esp
push ebx
mov ecx,esp
int 0x80
nop
nop
nop

Here is how it's done .
;Author : Krash
section .text
global main ;must be declared for linker (ld)
; SOCKETCALL PARAMS You can pass a value to EBX to utilize one of these functions.
; #define SYS_SOCKET 1 /* sys_socket(2) */
; #define SYS_BIND 2 /* sys_bind(2) */
; #define SYS_CONNECT 3 /* sys_connect(2) */
; #define SYS_LISTEN 4 /* sys_listen(2) */
; #define SYS_ACCEPT 5 /* sys_accept(2) */
; #define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
; #define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
; #define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
; #define SYS_SEND 9 /* sys_send(2) */
; #define SYS_RECV 10 /* sys_recv(2) */
; #define SYS_SENDTO 11 /* sys_sendto(2) */
; #define SYS_RECVFROM 12 /* sys_recvfrom(2) */
; #define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
; #define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
; #define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
; #define SYS_SENDMSG 16 /* sys_sendmsg(2) */
; #define SYS_RECVMSG 17 /* sys_recvmsg(2) */
; #define SYS_ACCEPT4 18 /* sys_accept4(2) */
; #define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
; #define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */
;The Message We want to send.
;DNS HEADER;
; AA AA - ID
; 01 00 - Query parameters
; 00 01 - Number of questions
; 00 00 - Number of answers
; 00 00 - Number of authority records
; 00 00 - Number of additional records
; DNS QUESTION --
; 07 - 'example' has length 7, ;so change this to be the length of domain ; keep in mind there are not '.' in the question.
; 65 - e
; 78 - x
; 61 - a
; 6D - m
; 70 - p
; 6C - l
; 65 - e
; 03 - subdomain '.com' length 03 ; change this to be the length of type.
; 63 - c
; 6F - o
; 6D - m
; 00 - zero byte to end the QNAME
; 00 01 - QTYPE
; 00 01 - QCLASS
; DNS ANSWER! This is What We Want to receive ;
; aa aa
; 81 80
; 00 01
; 00 01
; 00 00
; 00 00
; 07 65
; 78 61
; 6d 70
; 6c 65
; 03 63
; 6f 6d
; 00 00
; 01 00
; 01 c0
; 0c 00
; 01 00
; 01 00
; 00 12
; 8d 00
; 04
; IP ADDRESS IN HEX -- 93.184.216.34
; 5d
; b8
; d8
; 22
main:
; we create a socket fd, using again syscall 0x66 and argument SYS_SOCKET so ebx = 1
push 0x66
pop eax
push 0x1
pop ebx
xor ecx,ecx
push ecx
; but this times it will be a SOCK_DGRAM UDP, so 0x2 as argument
push 0x2
push 0x2
mov ecx,esp
int 0x80 ; SYS_SOCKET
; saving fd on the stack ; In reality I think I will save the port here instead
push eax
push 0x08080808 ; 8.8.8.8 ; I love that this doesn't really need to be backwards.
;push 0x0100007F ; 0100007F 1.0.0.127 for testing...
xor edx,edx
mov dh, 0x35 ; port 53; comment this for variable port
push dx ; comment this for variable port
; push word PORT ; UNcomment this for variable port
push word 0x2 ;
mov ecx,esp ; save pointer to ecx
push 0x10 ; addrlen
push ecx ; pointer to sockaddr
push eax ; fd received previously
mov ecx,esp ;
mov esi,eax ; save fd for next call
xor eax,eax
mov al,0x66
add bl,0x2 ; BL = 3 SYS_CONNECT
int 0x80 ;CALL SYS_CONNECT
; now we send a UDP packet to open stateful firewall :]
xor eax,eax
mov al,0x66
push 0x00000001 ; Origional Working request to google dns for example.com
push 0x00010000
push 0x6d6f6303
push 0x656c706d
push 0x61786507
push 0x00000000
push 0x00000100
push 0x0001AAAA ; This is the DNS HEADER above in little endian order.
mov edx,esp ; Move the string to EDX so we can send it.
xor ecx,ecx
push ecx
push 64 ; size of message to be sent is 8
push edx
push esi
mov ecx,esp
xor ebx,ebx
mov bl,0x9
int 0x80 ;CALL SYS_SEND
mov eax, 3 ; Prepare for SYSCALL_READ
mov ebx, 3 ;
mov ecx, esp
mov edx, 100
int 0x80 ;CALL SYSCALL_READ
add esp, eax
sub esp, 4
mov eax ,[esp]
push eax ; Probably not needed, I just want to ensure it's saved on the stack for further use..

Related

Assembly code to shell code: section .data and section .text in which order?

For an assignment, I wrote the following assembly code shell_exec.asm that should execute a shell in Linux:
section .data ; declare stuff
arg0 db "/bin/sh",0 ; 1st arg
align 4
argv dd arg0, 0 ; 2nd arg
envp dd 0 ; 3rd arg
section .text
global _start
_start:
mov eax, 0x0b ; execve
mov ebx, arg0 ; 1st arg
mov ecx, argv ; 2nd arg
mov edx, envp ; 3rd arg
int 0x80 ; kernel
I used nasm -f elf32 shell_exec.asm for compilation and ld -m elf_i386 -o shell_exec shell_exec.o for linking. Everything works so far and if I run ./shell_exec the shell spawns the way I want.
Now I wanted to extract the shell code (like \12\34\ab\cd\ef...) from this program. I used objdump -D -z shell_exec to show all parts of the code including the section .data and all zeroes. The output is as follows:
shell_exec: file format elf32-i386
Disassembly of section .text:
08049000 <_start>:
8049000: b8 0b 00 00 00 mov $0xb,%eax
8049005: bb 00 a0 04 08 mov $0x804a000,%ebx
804900a: b9 08 a0 04 08 mov $0x804a008,%ecx
804900f: ba 10 a0 04 08 mov $0x804a010,%edx
8049014: cd 80 int $0x80
Disassembly of section .data:
0804a000 <arg0>:
804a000: 2f das
804a001: 62 69 6e bound %ebp,0x6e(%ecx)
804a004: 2f das
804a005: 73 68 jae 804a06f <__bss_start+0x5b>
804a007: 00 add %al,(%eax)
0804a008 <argv>:
804a008: 00 a0 04 08 00 00 add %ah,0x804(%eax)
804a00e: 00 00 add %al,(%eax)
0804a010 <envp>:
804a010: 00 00 add %al,(%eax)
804a012: 00 00 add %al,(%eax)
If I only have a section .text within my assembly code, I can usually just copy all given values and use them as my shell code. But how is the order in case I have those two sections, namely .data and .text?
Edit 1
So, my second attempt is to do the assembly code like this:
section .text
global _start
_start:
mov ebp, esp
xor eax, eax
push eax ; -4
push "/sh " ; -8
push "/bin" ; -12
xor eax, eax
push eax
lea ebx, [ebp-12]
push ebx ; 1st arg
mov ecx, esp ; 2nd arg
lea edx, [ebp-4] ; 3rd arg
mov eax, 0x0b ; execve
int 0x80 ; kernel
This avoids using multiple sections, but sadly leads to a segmentation fault.

Why is my floppy disk reading code failing?

I am writing a bootloader in x86-16 assembly on a floppy disk, meaning that I will have to read from the disk in order to load more of my code, however, every time I attempt to read the disk and check the disk status code, I always get 0x04 - sector not found. I have attempted to read with CX set to 0x0000, 0x0001, and 0x0101, but I don't really know what to do, or what I am doing wrong.
INT 13,1 - Disk Status Codes
INT 13,2 - Read Disk Sectors
BITS 16
%include "C:\x86asm\nasm\nasm.asm"
;%macro pad 1-2 0
; times %1 - ($ - $$) db %2
;%endmacro
;%idefine PUSHW PUSH STRICT WORD
;%idefine PUSHD PUSH STRICT DWORD
[org 0x7C00]
; https://stanislavs.org/helppc/ ;; BIOS
; https://c9x.me/x86/ ;; Instruction Set
; https://wiki.osdev.org/ ;; OS Development
; absolute address = (segment << 4) + address
; to simulate NES/SNES style memory mapping (banks), only use bits[9..15] ($x000)
JMP 0x0000:_start ; ensure CS = $0000
_start:
CLI ; clear interrupt flag (disable
; interrupts, opposite of 6502)
CLD ; clear direction flag
PUSH CS
POP DS ; prepare for puts / puthexbyte
PUSH CS
POP SS ; set up stack segment
MOV SP, 0x7C00
;; set graphics ;;
MOV AX, 0x0012 ; set screen mode
INT 0x10
MOV AH, 0x0B
MOV BX, 0x0201
INT 0x10
MOV DX, 0x0D22 ; display "LOADING..."
MOV BX, 0x0007
MOV SI, loadstr
CALL putsl
MOV AH, 0x86 ; wait a moment...
MOV CX, 0x000F
INT 0x15
;; load floppy disk ;;
MOV BP, 0x0800 ; if fail, read x times
.loadfailure:
SUB BP, 0x0100 ; decrement # of tries left
MOV AH, 0x02 ; print # of tries left
XOR DX, DX
INT 0x10
MOV DX, BP
CALL puthexbyte
MOV AH, 0x00 ; reset disk system
MOV DL, 0x00
INT 0x13
MOV AX, 0x0201 ; read
MOV CX, 0x0101
XOR DX, DX
PUSHW 0x1800 ; write to $18000 ~ $1FFFF
POP ES
XOR BX, BX
INT 0x13
PUSHF
MOV DH, AH ; show error code
CALL puthexbyte
POPF
JNC .loadsuccess
TEST BP, BP
JNE .loadfailure
MOV DX, 0x0D0F ; read fail;
MOV SI, badload ; print msg
CALL putsl
MOV AH, 0x86 ; wait
MOV CX, 0x001E
INT 0x15
INT 0x18 ; boot windows
.loadsuccess:
MOV DX, 0x0D0F ; read success;
MOV SI, nosystem ; DOS not finished,
CALL putsl ; tell user
MOV AH, 0x86 ; wait
MOV CX, 0x001E
INT 0x15
JMP 0x1000:0x8000 ; boot test
putsl:
; [DX] : (Y,X)
; (AH)
MOV AH, 0x02
INT 0x10 ; set cursor position
puts:
; [DS:SI] : String
; (AX, BX, SI)
MOV AH, 0x0E ; teletype mode
MOV BX, 0x000F ; set white text attribute
.putsloop:
LODSB ; load character from [DS:SI++]
TEST AL, AL ; check if NULL (x86 MOV does not
JE .endputs ; change zero-flag unlike 6502 LDA/LDX/LDY)
INT 0x10 ; print character
JMP .putsloop ; loop until NULL
.endputs:
RET
puthexbyte:
; [DH] : Value
; (AX, DH, BX)
MOV AH, 0x0E
MOV BX, asciihex
MOV AL, DH
SHR AL, 4
XLAT
MOV BX, 0x000F
INT 0x10 ; print character
MOV BX, asciihex
MOV AL, DH
AND AL, 0x0F
XLAT
MOV BX, 0x000F
INT 0x10
RET
asciihex:
db "0123456789ABCDEF", 0
loadstr:
db "LOADING...", 0
badload:
db "Unable to load disk. Attempting to load Windows...", 0
nosystem:
db "Operating System is incomplete. Loading Windows...", 0
tststr:
db "Disk read success. INT 0x20 set success. Jump success.", 0
pad 0x01FE ; pad to end of boot sector
dw 0xAA55 ; floppy disk boot sector signature
; code
PUSHW 0x0000 ; test setting INT
POP DS ; (tested; works when in
MOV [4 * 0x20], DWORD putsl ; boot sector)
MOV DX, 0x0D0D ; test INT by
MOV SI, tststr ; printing
INT 0x20 ; string
MOV AH, 0x86 ; wait
MOV CX, 0x001E
INT 0x15
INT 0x18 ; boot windows
pad 0x0400 ; pad to end of sector 1
;incbin "os.bin"
pad 0x00167FFF ; pad to disk end
db 0x00
Edit:
An explanation on how to convert disk sectors and tracks (ie. CX in INT 13,2) into a "linear address" would be greatly appreciated, as the method I am using to get my code onto a floppy disk has me opening the program HxD and manually copying and pasting my binary onto the the disk.
Also, the disk I am using is 'unformatted' (as far as Windows is concerned).
Also, also, if this changes anything, my PC's BIOS is (msinfo32)"American Megatrends Inc. 5.35, 2008-12-16."

NASM Segmentation fault

I'm using a 64-bit Ubuntu 18.04.3 LTS VM and I'm trying to write a simple x64 assembly code that will print "Owned!!!".
Because I don't want any 0x00 or 0x0a bytes and I want the code to be position independent (because I'm learning how to write shellcodes), I wrote it this way:
;hello4.asm attempts to make the code position independent
section .text
global _start
_start:
;clear out the registers we are going to need
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
;write(int fd, char *msg, unsigned int len)
mov al, 4
mov bl, 1
;Owned!!! = 4f,77,6e,65,64,21,21,21
;push !,!,!,d
push 0x21212164
;push e,n,w,O
push 0x656e774f
mov rcx, rsp
mov dl, 8
int 0x80
;exit(int ret)
mov al,1
xor rbx, rbx
int 0x80
This is the output that I'm getting:
user#PC:~/Desktop/exploitsclass/hello_shellcode$ nasm -f elf64 hello4.asm
user#PC:~/Desktop/exploitsclass/hello_shellcode$ ld hello4.o -o hello4
user#PC:~/Desktop/exploitsclass/hello_shellcode$ objdump -d hello4 -M intel
hello4: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 48 31 db xor rbx,rbx
400086: 48 31 c9 xor rcx,rcx
400089: 48 31 d2 xor rdx,rdx
40008c: b0 04 mov al,0x4
40008e: b3 01 mov bl,0x1
400090: 68 64 21 21 21 push 0x21212164
400095: 68 4f 77 6e 65 push 0x656e774f
40009a: 48 89 e1 mov rcx,rsp
40009d: b2 08 mov dl,0x8
40009f: cd 80 int 0x80
4000a1: b0 01 mov al,0x1
4000a3: 48 31 db xor rbx,rbx
4000a6: cd 80 int 0x80
user#PC:~/Desktop/exploitsclass/hello_shellcode$ ./hello4
Segmentation fault (core dumped)
How do I fix this?
UPDATE:
I've understood that int 0x80 is intended for 32-bit programs and I should use syscall instead and that syscall has different ids for each system call.
The new code is:
;hello4.asm attempts to make the code position independent
section .text
global _start
_start:
;clear out the registers we are going to need
xor rax, rax
xor rsi, rsi
xor rdi, rdi
xor rdx, rdx
;write(int fd, char *msg, unsigned int len)
mov al, 1
add di, 1
;Owned!!! = 4f,77,6e,65,64,21,21,21
;push !,!,!,d
push 0x21212164
;push e,n,w,O
push 0x656e774f
mov rsi, rsp
mov dl, 8
syscall
;exit(int ret)
mov al, 60
xor rdi, rdi
syscall
The output is Owne% instead of Owned!!! now.
It still needs to be fixed.
With the help of #CertainLach I've written the correct code:
;hello4.asm attempts to make the code position independent
section .text
global _start
_start:
;clear out the registers we are going to need
xor rax, rax
xor rsi, rsi
xor rdi, rdi
xor rdx, rdx
;write(int fd, char *msg, unsigned int len)
mov al, 1
add di, 1
;Owned!!! = 4f,77,6e,65,64,21,21,21
mov rsi, 0x21212164656e774f
push rsi
mov rsi, rsp
mov dl, 8
syscall
;exit(int ret)
mov al, 60
xor rdi, rdi
syscall
This code contains no null bytes or 0x0a bytes and it's position-independent, as following:
user#PC:~/Desktop/exploitsclass/hello_shellcode$ objdump -d hello4 -M intel
hello4: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 48 31 f6 xor rsi,rsi
400086: 48 31 ff xor rdi,rdi
400089: 48 31 d2 xor rdx,rdx
40008c: b0 01 mov al,0x1
40008e: 66 83 c7 01 add di,0x1
400092: 48 be 4f 77 6e 65 64 movabs rsi,0x21212164656e774f
400099: 21 21 21
40009c: 56 push rsi
40009d: 48 89 e6 mov rsi,rsp
4000a0: b2 08 mov dl,0x8
4000a2: 0f 05 syscall
4000a4: b0 3c mov al,0x3c
4000a6: 48 31 ff xor rdi,rdi
4000a9: 0f 05 syscall
This is also a correct way of implementing the solution, which is 1 bytecode less, but with more memory consumption:
user#PC:~/Desktop/exploitsclass/hello_shellcode$ cat hello4.asm
;hello4.asm attempts to make the code position independent
section .text
global _start
_start:
;clear out the registers we are going to need
xor rax, rax
xor rsi, rsi
xor rdi, rdi
xor rdx, rdx
;write(int fd, char *msg, unsigned int len)
mov al, 1
add di, 1
;Owned!!! = 4f,77,6e,65,64,21,21,21
;push !,!,!,d
push 0x21212164
;push e,n,w,O
push 0x656e774f
mov rsi, rsp
mov dl, 16
syscall
;exit(int ret)
mov al, 60
xor rdi, rdi
syscall
user#PC:~/Desktop/exploitsclass/hello_shellcode$ objdump -d hello4 -M intel
hello4: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 48 31 f6 xor rsi,rsi
400086: 48 31 ff xor rdi,rdi
400089: 48 31 d2 xor rdx,rdx
40008c: b0 01 mov al,0x1
40008e: 66 83 c7 01 add di,0x1
400092: 68 64 21 21 21 push 0x21212164
400097: 68 4f 77 6e 65 push 0x656e774f
40009c: 48 89 e6 mov rsi,rsp
40009f: b2 10 mov dl,0x10
4000a1: 0f 05 syscall
4000a3: b0 3c mov al,0x3c
4000a5: 48 31 ff xor rdi,rdi
4000a8: 0f 05 syscall
Thank you so much!
Can't answer your comment, you can't just change int 0x80 to syscall to make it work, system call numbers differ, i.e sys_write you have here, have id 4 for int 0x80, and id 1 with syscall
Here you can see numbers for syscall
And here for int 0x80

x86 64 Reverse shell shellcode

I am looking a reverse shell shellcode from this link. I am not able to follow the reason for the following instructions in the shellcode:
4000a3: 4d 31 d2 xor r10,r10
4000a6: 41 52 push r10
4000a8: c6 04 24 02 mov BYTE PTR [rsp],0x2
4000ac: 66 c7 44 24 02 7a 69 mov WORD PTR [rsp+0x2],0x697a
4000b3: c7 44 24 04 0a 33 35 mov DWORD PTR [rsp+0x4],0x435330a
4000ba: 04
4000bb: 48 89 e6 mov rsi,rsp
I searched other SO questions, and I find that BYTE/WORD/DWORD PTR would be used to assign a byte/word/dword. Since this x86 64, I'm assuming WORD here means 2 bytes and DWORD means 4 bytes (please correct me if I'm wrong).
The author is pushing zero on the stack. Then he has 3 mov instructions.
Assume RSP initially points to:
x00 x00 x00 x00 x00 x00 x00 x00
1) Is the following the effect of the three mov instructions (assuming little endian) ?
x04 x35 x33 x0a x7a x69 x00 x02
2) If yes, then what is the author achieving by it because isn't the next mov instructions overwriting what's pointed by rsp ?
Thanks
1) Yes
2) Author is saving the sockaddr for the socket connect syscall
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
In x86-64, the arguments are the registers rdi, rsi and rdx. So, He is moving the pointer addr pointer to rsi register.
mov BYTE PTR [rsp],0x2 ; Family Address (PF_INET)
mov WORD PTR [rsp+0x2],0x697a ; port = 27002
mov DWORD PTR [rsp+0x4],0x435330a ; addr = 10.51.53.4 (0a333504)

nasm bootloader, why does where i define ths string matter?

Im experimenting with writing a bootloader in nasm, at the moment it just prints a string.
[BITS 16]
[org 0x7c00]
myString:
db 'Hello World', 0x00
mov bp, 0x8000
mov sp, bp
mov bx, myString
call printString
jmp $
printString:
pusha
mov ah , 0x0e
printStringA:
mov al , [bx]
cmp al, 0x00
je printStringB
int 0x10
add bx, 0x01
jmp printStringA
printStringB:
popa
ret
times 510 -( $ - $$ ) db 0
dw 0xaa55
that works fine, but if i move the string definition to here:
[BITS 16]
[org 0x7c00]
mov bp, 0x8000
mov sp, bp
myString:
db 'Hello World', 0x00
mov bx, myString
call printString
jmp $
printString:
pusha
mov ah , 0x0e
printStringA:
mov al , [bx]
cmp al, 0x00
je printStringB
int 0x10
add bx, 0x01
jmp printStringA
printStringB:
popa
ret
times 510 -( $ - $$ ) db 0
dw 0xaa55
it prints out garbage, im running this in bochs under windows if that helps.
You are assembling to raw machine code. There are no data and text sections. Everything is interpreted as code, including what you insert using db. Hence both code snippets are wrong.
If you finish with an endless loop (as in your example) or a halt instruction, the data can safely be put after the code as it will never be reached. Otherwise you must arrange for the data to be skipped over.
You also need to set the segment registers correctly at the start.
Here is a corrected version, with early declaration of data:
[BITS 16]
[ORG 0]
;;; Set CS and DS
jmp 0x07c0:start
start:
mov ax, cs
mov ds, ax
;;; set SP
mov bp, 0x8000
mov sp, bp
;;; skip over data
jmp L1
myString:
db 'Hello World', 0x00
L1:
mov bx, myString
...
Note that in your first example, the data was interpreted as code.
db 'Hello World', 0x00
is assembled as
48 65 6c 6c 6f 20 57 6f 72 6c 64 00
and corresponds to:
dec ax
gs insb
insb
outsw
and [bx+0x6f],dl
jc short 0x76
fs
db 0x00
In effect this gets executed before your code. It is pure luck that this fragment doesn't prevent your code from working.

Resources