I'm a new to x86 and currently writing a program where I need to write a file after doing some operations with data I read from a file, but I am facing problems when I write the result to a file, because it is writing with some weird enconding.
Here I reserve the space I am going to need to store the result and the output file:
section .data
new_file db "new_file.txt", 0
section .bss
data resb 4
The code that writes data to a file called new_file.txt:
mov rax, SYS_OPEN
mov rdi, new_file
mov rsi, O_CREAT + O_WRONLY
mov rdx, 0644o
syscall
push rax
mov rdi, rax
mov rax, SYS_WRITE
mov rsi, data
mov rdx, 4
syscall
mov rax, SYS_CLOSE
pop rdi
syscall
For example, assume that I want to do some addition and then store the result in data to write it later:
mov rax, 0xF
add rax, 0x1
mov [data], rax
in this case the data would have a value of 0x10 and when I check the file generated I get something like this:
I'm lost since I don't seem to find anything about encoding in x86, so any help would be appreciated.
Consider for a moment a file that has this content, viewed in hexedit let's say.
ADDR 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------------------------------------------------------
00 0A 20 20 20 20 20 20 20 20 20 20 F0 9D 90 98 F0
10 9F 84 09 09 10 00 00 00 03 7C 2D 1A 0A 0A 0A 0A
Dumping to console with CAT tries display what it thinks is UTF-8 text.
However, if you look at the same thing using a text editor then;
or
So your data was written to file correctly, but the means by which you want to view it is not compatible with the result your expecting. I believe there are applications for Linux, that will display arrays of bytes/words/dwords or qwords and even structures, somewhat analogous to hexedit
Related
I ran sudo perf record -F 99 find / followed by sudo perf report and selected "Annotate fdopendir" and here are the first seven instructions:
push %rbp
push %rbx
mov %edi,%esi
mov %edi,%ebx
mov $0x1,%edi
sub $0xa8,%rsp
mov %rsp,%rbp
The first instruction appears to be saving the caller's base frame pointer. I believe instructions 2 through 5 are irrelevant to this question but here for completeness. Instructions 6 and 7 are confusing to me. Shouldn't the assignment of rbp to rsp occur before subtracting 0xa8 from rsp?
The x86-64 System V ABI doesn't require making a traditional / legacy stack-frame. This looks close to a traditional stack frame setup, but it's definitely not because there's no mov %rsp, %rbp right after the first push %rbp.
We're seeing compiler-generated code that simply uses RBP as a temporary register, and is using it to hold a pointer to a local on the stack. It's just a coincidence that this happens to involve the instruction mov %rsp, %rbp sometime after push %rbp. This is not making a stack frame.
In x86-64 System V, RBX and RBP are the only 2 "low 8" registers that are call-preserved, and thus usable without REX prefixes in some cases (e.g. for the push/pop, and when used in addressing modes), saving code-size. GCC prefers to use them before saving/restoring any of R12..R15. What registers are preserved through a linux x86-64 function call (For pointers, copying them with mov always requires a REX prefix for 64-bit operand-size, so there are fewer savings than for 32-bit integers, but gcc still goes for RBX then RBP, in that order, when it needs to save/restore call-preserved regs in a function.)
Disassembly of /lib/libc.so.6 (glibc) on my system (Arch Linux) shows similar but different code-gen for fdopendir. You stopped the disassembly too soon, before it makes a function call. That sheds some light on why it wanted a call-preserved temporary register: it wanted the var in a reg across the call.
00000000000c1260 <fdopendir>:
c1260: 55 push %rbp
c1261: 89 fe mov %edi,%esi
c1263: 53 push %rbx
c1264: 89 fb mov %edi,%ebx
c1266: bf 01 00 00 00 mov $0x1,%edi
c126b: 48 81 ec a8 00 00 00 sub $0xa8,%rsp
c1272: 64 48 8b 04 25 28 00 00 00 mov %fs:0x28,%rax # stack-check cookie
c127b: 48 89 84 24 98 00 00 00 mov %rax,0x98(%rsp)
c1283: 31 c0 xor %eax,%eax
c1285: 48 89 e5 mov %rsp,%rbp # save a pointer
c1288: 48 89 ea mov %rbp,%rdx # and pass it as a function arg
c128b: e8 90 7d 02 00 callq e9020 <__fxstat>
c1290: 85 c0 test %eax,%eax
c1292: 78 6a js c12fe <fdopendir+0x9e>
c1294: 8b 44 24 18 mov 0x18(%rsp),%eax
c1298: 25 00 f0 00 00 and $0xf000,%eax
c129d: 3d 00 40 00 00 cmp $0x4000,%eax
c12a2: 75 4c jne c12f0 <fdopendir+0x90>
....
c12c1: 48 89 e9 mov %rbp,%rcx # pass the pointer as the 4th arg
c12c4: 89 c2 mov %eax,%edx
c12c6: 31 f6 xor %esi,%esi
c12c8: 89 df mov %ebx,%edi
c12ca: e8 d1 f7 ff ff callq c0aa0 <__alloc_dir>
c12cf: 48 8b 8c 24 98 00 00 00 mov 0x98(%rsp),%rcx
c12d7: 64 48 33 0c 25 28 00 00 00 xor %fs:0x28,%rcx # check the stack cookie
c12e0: 75 38 jne c131a <fdopendir+0xba>
c12e2: 48 81 c4 a8 00 00 00 add $0xa8,%rsp
c12e9: 5b pop %rbx
c12ea: 5d pop %rbp
c12eb: c3 retq
This is pretty silly code-gen; gcc could have simply used mov %rsp, %rcx the 2nd time it needed it. I'd call this a missed-optimization. It never needed that pointer in a call-preserved register because it always knew where it was relative to RSP.
(Even if it hadn't been exactly at RSP+0, lea something(%rsp), %rdx and lea something(%rsp), %rcx would have been totally fine the two times it was needed, with probably less total cost than saving/restoring RBP + the required mov instructions.)
Or it could have used mov 0x18(%rbp),%eax instead of rsp to save a byte of code-size in that addressing mode. Avoiding direct references to RSP between function calls reduces the amount of stack-sync uops Intel CPUs need to insert.
I wrote:
mov 60, %rax
GNU as accepted it, although I should have written
mov $60, %rax
Is there any difference between two such calls?
Yes; the first loads the value stored in memory at address 60 and stores the result in rax, the second stores the immediate value 60 into rax.
Just try it...
mov 60,%rax
mov $60,%rax
mov 0x60,%rax
0000000000000000 <.text>:
0: 48 8b 04 25 3c 00 00 mov 0x3c,%rax
7: 00
8: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax
f: 48 8b 04 25 60 00 00 mov 0x60,%rax
16: 00
Ewww! Historically the dollar sign meant hex $60 = 0x60, but gas also has a history of screwing up assembly languages...and historically x86 assembly languages allowed 60h to indicate hex, but got an error when I did that.
So with and without the dollar sigh you get a different instruction.
0x8B is a register/memory to register, 0xC7 is an immediate to register. so as davmac answered mov 60,%rax is a mov memory location to register, and mov $60,%rax is mov immediate to register.
Encoding the shellcode three times using the x64 xor Encoder
I'm writing my own exploit and I've wondered if I need to decode the shellcode when adding it in my program or is the decoder stub inside of the shellcode already? If I need to decode, how can I do that, there is no key given?
No. You don't have to decrypt the shellcode. I ran the same command and got something which looked like this
0: 48 31 c9 xor rcx, rcx
3: 48 81 e9 b6 ff ff ff sub rcx, 0xffffffffffffffb6
a: 48 8d 05 ef ff ff ff lea rax, [rip+0xffffffffffffffef] # 0x0
11: 48 bb af cc c5 c0 90 movabs rbx, 0x29153c90c0c5ccaf
18: 3c 15 29
1b: 48 31 58 27 xor QWORD PTR [rax+0x27], rbx
1f: 48 2d f8 ff ff ff sub rax, 0xfffffffffffffff8
25: e2 f4 loop 0x1b
This was the starting part of shellcode followed by xor'd 2nd iteration payload. On decrypting I saw that It had a similar stub attached. So you don't have to decrypt. Just point execution to the start of the buffer.
Even though both the programs abide by relative addressing instructions required for shellcode execution, and both print the desired message on execution, the 2nd Sample fails when used as a shellcode. Can anyone explain the behavior? Strangely a 3rd sample identical to the 1st one fails as well.
Output:
Sample 1
Hello World
Other Samples(2&3) print garbage values
Sample 1
global _start
section .text
_start:
jmp widen
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
widen:
call pHworld
Hworld db "Hello World",0xa
Sample 2
global _start
section .text
_start:
call pHworld
Hworld db "Hello World",0xa
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
Sample 3
global _start
section .text
_start:
jmp label1
label1:
call pHworld
Hworld db "Hello World",0xa
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
Unable to rest my curiosity , I tried yet another variation , and this fails (prints garbage values) even though I the objdump does not have any 0x00.
Sample 4
global _start
section .text
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
xor rax,rax
xor rdi,rdi
mov al,60
syscall
l1:
call pHworld
Hworld db "Hello World", 0xa
_start:
jmp l1
enter code here
Objdump of sample4
./hworld2.s: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 :
400080: 5e pop rsi
400081: 48 31 c0 xor rax,rax
400084: b0 01 mov al,0x1
400086: 48 89 c7 mov rdi,rax
400089: 48 89 fa mov rdx,rdi
40008c: 48 83 c2 0b add rdx,0xb
400090: 0f 05 syscall
400092: 48 31 c0 xor rax,rax
400095: 48 31 ff xor rdi,rdi
400098: b0 3c mov al,0x3c
40009a: 0f 05 syscall
000000000040009c :
40009c: e8 df ff ff ff call 400080
00000000004000a1 :
4000a1: 48 rex.W
4000a2: 65 gs
4000a3: 6c ins BYTE PTR es:[rdi],dx
4000a4: 6c ins BYTE PTR es:[rdi],dx
4000a5: 6f outs dx,DWORD PTR ds:[rsi]
4000a6: 20 57 6f and BYTE PTR [rdi+0x6f],dl
4000a9: 72 6c jb 400117
4000ab: 64 fs
4000ac: 0a eb or ch,bl
00000000004000ad :
4000ad: eb ed jmp 40009c
TL;DR : With shellcode you want to avoid encoding 0x00 bytes, otherwise when the code is used as a string for an exploit they will truncated at the first 0x00. This effectively will cut your code short.
The extra 0x00 bytes don't cause an issue when running outside an exploit because they aren't being converted to strings. The instructions are executed as is like any normal executable.
The reason for the JMP instruction in the JMP/CALL/POP method is to eliminate the insertion of unwanted 0x00 bytes in the generated code. JMP in 64-bit code has a rel8 and rel32 encoding. In 64-bit code CALL only has a rel32 encoding. This means that if you use CALL in 64-bit code to make a small transfer forward in memory it will be encoded as a 32-bit zero extended target. That zero extension would cause unwanted 0x00 values to be placed in the shell code. In 64-bit code this CALL instruction:
call next
nop
next:
Would be encoded as:
e8 01 00 00 00
Since the JMP instruction supports rel8 (relative byte displacement) NASM can generate a JMP instruction forward in memory if the target is no more than 127 bytes away (signed byte is -128 to +127). This JMP instruction:
jmp next
nop
next:
Would be encoded as:
eb 01
So no extra zeroes. You may ask why the CALL instruction in the JMP/CALL/POP method works. The reasoning is that negative values have their sign extended across the upper bytes. Filling the upper bits with 1 will not produce extra 0x00 bytes and so it works. This CALL instruction:
prev:
call prev
Would be encoded as:
e8 fb ff ff ff
Notice that the extra bytes are not 0. This is why calling to a spot earlier in memory can avoid generating zeroes.
Sample 2
If we keep the above in mind we only need to examine the generated code for Sample 2 to see where things go wrong. objdump -D ./sample2 -Mintel generates:
0000000000000000 <_start>:
0: e8 0c 00 00 00 call 11 <pHworld> <---------- Extra zeros
0000000000000005 <Hworld>:
5: 48 rex.W
6: 65 6c gs ins BYTE PTR es:[rdi],dx
8: 6c ins BYTE PTR es:[rdi],dx
9: 6f outs dx,DWORD PTR ds:[rsi]
a: 20 57 6f and BYTE PTR [rdi+0x6f],dl
d: 72 6c jb 7b <pHworld+0x6a>
f: 64 0a 5e 48 or bl,BYTE PTR fs:[rsi+0x48]
0000000000000011 <pHworld>:
11: 5e pop rsi
12: 48 31 c0 xor rax,rax
15: b0 01 mov al,0x1
17: 48 89 c7 mov rdi,rax
1a: 48 89 fa mov rdx,rdi
1d: 48 83 c2 0b add rdx,0xb
21: 0f 05 syscall
23: 48 31 c0 xor rax,rax
26: b8 3c 00 00 00 mov eax,0x3c <---------- Extra zeros
2b: 48 31 ff xor rdi,rdi
2e: 0f 05 syscall
So we see the issue with the CALL being encoded with extra zeroes which is why you need a traditional JMP/CALL/POP. There is a second issue with mov eax, 60 since it encodes extra bytes. I think you meant to use mov al, 60
Sample 3
objdump -D ./sample3 -Mintel generates:
0000000000000000 <_start>:
0: eb 00 jmp 2 <label1> <---------- Extra zeros
0000000000000002 <label1>:
2: e8 0c 00 00 00 call 13 <pHworld> <---------- Extra zeros
0000000000000007 <Hworld>:
7: 48 rex.W
8: 65 6c gs ins BYTE PTR es:[rdi],dx
a: 6c ins BYTE PTR es:[rdi],dx
b: 6f outs dx,DWORD PTR ds:[rsi]
c: 20 57 6f and BYTE PTR [rdi+0x6f],dl
f: 72 6c jb 7d <pHworld+0x6a>
11: 64 0a 5e 48 or bl,BYTE PTR fs:[rsi+0x48]
0000000000000013 <pHworld>:
13: 5e pop rsi
14: 48 31 c0 xor rax,rax
17: b0 01 mov al,0x1
19: 48 89 c7 mov rdi,rax
1c: 48 89 fa mov rdx,rdi
1f: 48 83 c2 0b add rdx,0xb
23: 0f 05 syscall
25: 48 31 c0 xor rax,rax
28: b8 3c 00 00 00 mov eax,0x3c <---------- Extra zeros
2d: 48 31 ff xor rdi,rdi
30: 0f 05 syscall
Same type of issues as Sample 2.
Sample 1
objdump -D ./sample1 -Mintel generates:
0000000000000000 <_start>:
0: eb 1f jmp 21 <widen>
0000000000000002 <pHworld>:
2: 5e pop rsi
3: 48 31 c0 xor rax,rax
6: b0 01 mov al,0x1
8: 48 89 c7 mov rdi,rax
b: 48 89 fa mov rdx,rdi
e: 48 83 c2 0b add rdx,0xb
12: 0f 05 syscall
14: 48 31 c0 xor rax,rax
17: b8 3c 00 00 00 mov eax,0x3c <---------- Extra zeros
1c: 48 31 ff xor rdi,rdi
1f: 0f 05 syscall
0000000000000021 <widen>:
21: e8 dc ff ff ff call 2 <pHworld>
0000000000000026 <Hworld>:
26: 48 rex.W
27: 65 6c gs ins BYTE PTR es:[rdi],dx
29: 6c ins BYTE PTR es:[rdi],dx
2a: 6f outs dx,DWORD PTR ds:[rsi]
2b: 20 57 6f and BYTE PTR [rdi+0x6f],dl
2e: 72 6c jb 9c <Hworld+0x76>
30: 64 fs
31: 0a .byte 0xa
Although you say sample 1 works, it still has a problem that needs to be corrected. mov rax, 60 needs to be mov al, 60.
I use objdump to disassemble some ELF file on 32 bit Linux.
The asm file are in Intel format.
In the disassemble file, I notice some memory slot like below:
80483ed: c7 44 24 18 07 00 00 mov DWORD PTR [esp+0x18],0x7
80483f4: 00
80483f5: c7 44 24 1c 0c 00 00 mov DWORD PTR [esp+0x1c],0xc
80483fc: 00
80483fd: c7 44 24 20 01 00 00 mov DWORD PTR [esp+0x20],0x1
8048404: 00
8048405: c7 44 24 24 fe ff ff mov DWORD PTR [esp+0x24],0xfffffffe
804840c: ff
and the original assemble file is :
mov DWORD PTR [esp+24], 7
mov DWORD PTR [esp+28], 12
mov DWORD PTR [esp+32], 1
mov DWORD PTR [esp+36], -2
Could anyone tell me what does the memory address like "80483f4","80483fc" do?
Is this issue related to the memory alignment?
Thank you!
These are part of the previous line's operands. The "immediate" (constant) numbers are encoded as 32-bits. So 0x07 takes up 4 bytes: 07 00 00 00. Whatever you're using to disable is showing you the last byte on a different line.