Recent Posts

Pages: [1] 2 3 ... 10
1
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by MediocreVeg1 on January 19, 2021, 05:27:09 PM »
Quote
I confesss I cannot see how it works, too...  ;)
Yeah, I think I'll change my thing to your not solution so I at least understand what is going on.

And about the registers, I think I finally get it now. Thanks for all the help!
2
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by fredericopissarra on January 19, 2021, 05:13:06 PM »
Quote from: MediocreVeg1
Oh man, I'm so stupid. How does my procedure even work then? Sheesh, now I'm even confused as to how my own procedure works :P
I confesss I cannot see how it works, too... ;)

Quote
Oh, I see. Would that mean that I would be able to enter the call number in eax too? And for example in the second argument of a system call, would I use ebx or edi for 32-bit registers?
A simple example. If you want to print a string you can do:
Code: [Select]
  bits 64
  default rel   ; Use RIP relative addressing.

  section .rodata

msg: db `Hello\n`
msg_len equ $ - msg

  section .text

  global writemsg
writemsg:
  mov eax,1    ; EAX, instead of RAX will zero upper 32 bits.
  mov edi,eax  ; RDI=STDOUT_FILENO (upper 32 bits zeroed).
  lea rsi,[msg] ; Need to load RSI (because it is a pointer). LEA because 'msg' is RIP relative.
  mov edx,msg_len ; RDX (upper 32 bits zeroed).
  syscall
  ret

Those E?? movs are shorter than if you use R?? regs.

[]s
Fred
3
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by MediocreVeg1 on January 19, 2021, 11:31:18 AM »
Quote
Nope... repnz scasb searches for AL in RDI and forward (increasing RDI and decreasing RCX). It won't change RBX.
Oh man, I'm so stupid. How does my procedure even work then? Sheesh, now I'm even confused as to how my own procedure works :P

Quote
It won't get confused because E?? registers are part of R?? registers. When you use a R?? register NASM is forced to add a prefix to the instruction (REX prefix). This prefix isn't added if you use E?? registers. Of course, if the argument is an address (a pointer) you are forced to use R??
Oh, I see. Would that mean that I would be able to enter the call number in eax too? And for example in the second argument of a system call, would I use ebx or edi for 32-bit registers?

Quote
Yep... The problem when you ser RCX to -1 is that, theoretically you can search a 18446744073709551615 bytes long string. If such string exists (not possible), than REP SCASB would take 192 YEARS to complete (@ 3 GHz). If you restrict the string to 2³²-1 bytes, if such string exists (improbable), it would take only 1.5 seconds to scan.
Wow, that's... a lot. Anyway, I think this answers my question. Thanks.
4
Programming with NASM / character insensitive compare
« Last post by mik3ca on January 19, 2021, 02:15:53 AM »
I'm trying to figure out the least processor intensive way to compare two characters to see if they match regardless of whether the letter is capital or small. Let's say the characters are stored in registers CL, and DL.

Initially I thought of this:

Code: [Select]
mov AL,CL
anl AL,11010000b ;make letter upper case
mov BL,DL
anl BL,11010000b ;make this one upper case
cmp AL,BL
je charsaresame

But the problem is if I used numbers or compared other symbols, then they would match up with other symbols, and I don't want that.

How would I be able to do this the most efficient way?
5
Programming with NASM / Re: Jump table not working as expected
« Last post by mik3ca on January 18, 2021, 09:15:53 PM »
Ok I solved my issue from experimentation, trial and error.

the offset value (BP) needed to be zero in the com file when doing the math for the jump table.

6
Programming with NASM / Re: A proper, fleshed out explanation for brk()?
« Last post by MediocreVeg1 on January 18, 2021, 06:58:17 PM »
Ok, I found out how it works. I'll give a very brief answer to each question I've made:
1. Basically, brk() just reserves space between the heap and the stack and returns an address pointing towards the address of this reserved space.
2. Mostly mentioned in my answer to 1 as well. About accessing, I was being a total idiot and just made a variable that pointed towards the address returned by brk.
3. Still not sure about this yet.

However, I learnt that for dynamic memory allocation, mmap might be a better choice. I can't seem to find any good examples and explanations on that though. If anyone has a point to add, I probably won't respond for a few hours since it's pretty late at night here.
7
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by fredericopissarra on January 18, 2021, 06:40:38 PM »
Quote
I found strange your approach, since sub rdi,rbx (with rbx == 0) do nothing, except affect the flags. Din't you mean to use 'mov rbx,rdi' instead of 'xor rbx,rbx'?
Maybe I'm not understanding how scas works, but isn't the result of scasb stored in rbx in 64-bit assembly? That's why I cleared it with XOR before using scasb and why I substracted it from rdi (which has starting address of string). I'm probably wrong here though.
Nope... repnz scasb searches for AL in RDI and forward (increasing RDI and decreasing RCX). It won't change RBX.

Quote
Quote
PS: Try to use E?? registers as many times as possible. Using R?? will imply a REX prefix and bigger instructions. moving and doing arithmetic/logical operations to E?? registers will automatically zero the upper 32 bits of R?? registers for you (this doen't work in a few instructions as, for example, CDQ [will zero upper RDX but not upper RAX).
Wouldn't the assembler get confused if I used 64-bit syscalls on 32-bit registers? Or if I put some arguments of a syscall in R?? registers and others in E?? registers?
It won't get confused because E?? registers are part of R?? registers. When you use a R?? register NASM is forced to add a prefix to the instruction (REX prefix). This prefix isn't added if you use E?? registers. Of course, if the argument is an address (a pointer) you are forced to use R??.

Quote
Quote
S: Notice in my routine, if '\0' isn't found in a block of 2³²-1 bytes it returns -1 (all bits set) in RAX. This allows you to test an error:
Yeah, I put it into my procedure as well after you showed your example. Wouldn't this event be highly unlikely though? I think 2^32-1 is like 4294967295 bytes so every single byte after the starting address of the string would have to be non-zero, right?
Yep... The problem when you ser RCX to -1 is that, theoretically you can search a 18446744073709551615 bytes long string. If such string exists (not possible), than REP SCASB would take 192 YEARS to complete (@ 3 GHz). If you restrict the string to 2³²-1 bytes, if such string exists (improbable), it would take only 1.5 seconds to scan.
8
Programming with NASM / Jump table not working as expected
« Last post by mik3ca on January 18, 2021, 04:38:08 PM »
I have a problem with some addressing issues. I made large assembly code that is about 2KB in size and copying the whole code into dos QuickBasic is tedious and takes up precious local space. Due to the complexity of the code, I will only show a portion.

For me to get the jump table to work, I created the following assembly code, compiled it as a 16-bit DOS binary, and extracted the hex values (with linux xxd utility)

Code: [Select]
push BP       ;save register (Qbasic stack is changed)
mov BP,SP  ;save pointer here to freeze Qbasic stat
push ES ;save other frequently used pointer registers
push DI
push DS
push SI
mov AX,[BP+06h] ;Load our function number
mov BP,[BP+08h] ;Load offset of this code
cmp AX,3h ;See if function number is from 0 - 3
jg endit ;Function number is too high so exit
shl AX,1 ;Multiply function number by 2
add AX,BP ;Add this code offset
mov SI,AX ;Save as SI
mov SI,[CS:SI+jt] ;Load address of our option
add SI,BP ;Add this code offset since nasm doesn't automatically do it
jmp SI ;Go to chosen function

;Our jump table
jt:
dw option0
dw option1
dw option2
dw option3

option0:
;insert code here
jmp endit

option1:
;insert code here
jmp endit

option2:
;insert code here
jmp endit

option3:
;insert code here
jmp endit

;common ending from all functions
endit:
pop SI ;restore all registers in order
pop DS
pop DI
pop ES
pop BP
retf 4h ;release our used paramters (2 ints) to QB

The Qbasic code is as follows:

Code: [Select]
dim c as string * 2000 'reserve enough space to store binary version of above code
c=acode$ 'load the code in
def seg=varseg(c) 'set segment to code
func%=4 'we want to try function 4
codeoffs%=varptr(c)
call absolute(byval codeoffs%,byval func%, codeoffs%) 'run the code
end

'My apologies for not using meaningful variable names but this function converts the hexadecimal code to the binary version of the code above. I wanted to avoid making the binary code above as a separate file required by this program.
FUNCTION acode$
w$ = <insert binary code here as hex digits>
cd$ = "": soc% = LEN(w$): FOR z% = 1 TO soc% STEP 2: cd$ = cd$ + CHR$(VAL("&H" + MID$(w$, z%, 2))): NEXT:  acode$ = cd$
END FUNCTION

Note in the call absolute, the first parameter is at BP+08 which is the offset. without this, the jump table would not execute and some random spot of memory would be accessed instead.

So anyways, I tried to make some changes to make the assembly code resident in memory. The code is as follows:

Code: [Select]
org 100h ;this is a COM file we are keeping in memory
jmp installer

'We add a word so outside program can see if int is installed
db 'INTCHECK'
;Here we expect outside program to pass in AX as function number and BP as offset to this code
;along with ES:DI as a pointer to an struct made in QuickBasic.
myinterrupt:
push BP  ;Save outside pointer registers that we modify
push ES
push DI
push DS
push SI
pusha
cmp AX,3h ;See if function number is from 0 - 3
jg endit ;Function number is too high so exit
shl AX,1 ;Multiply function number by 2
add AX,BP ;Add this code offset
mov SI,AX ;Save as SI
mov SI,[CS:SI+jt] ;Load address of our option
add SI,BP ;Add this code offset since nasm doesn't automatically do it
jmp SI ;Go to chosen function

;Our jump table
jt:
dw option0
dw option1
dw option2
dw option3

option0:
;insert code here
jmp endit

option1:
;insert code here
jmp endit

option2:
;insert code here
jmp endit

option3:
;insert code here
jmp endit

;common ending from all functions
endit:
popa
pop SI ;restore all registers in order
pop DS
pop DI
pop ES
pop BP
iret  ;exit from interrupt

;Here we install the interrupt

installer:
;To keep things simple, I made a hard-coded interrupt value and omitted the interrupt saving code.

mov AL,63h ;we will make our interrupt #63h

mov AH,25h ;install our interrupt
push DS ;save original DS
push CS
pop DS           
mov DX,myint ;DS:DX -> address to our handler
int 21h ;install
pop DS  ;restore original DS
mov AX,3100h ;return code to DOS: AL=0
mov DX,0F00h ;hog about 60kb
int 21h ;exit as a TSR

I compiled this as a dos binary, renamed it as a .COM file and executing that does work.

So now I make a short assembly routine in QuickBasic to call the newly installed interrupt:

Code: [Select]
org 0h
PUSH BP ;save registers as usual
MOV BP, SP
push ES
push DI
push DS
push SI
mov DI,[BP+06h] ;load values directly
mov ES,[BP+08h]
mov BX,[BP+0Ah] ;BX=TSR code offset (for debugging)
mov AX,[BP+0Ch] ;our function
int 63h ;We made our int as 63h in assembly code, so we have to call it as that.
pop SI ;restore registers
pop DS
pop DI
pop ES
pop BP
retf 6h ;return to QuickBasic

And in quickbasic, the code has minor changes as follows:

Code: [Select]
type struct
whatever as integer
anything as string * 20
something as long
end type

dim s as struct
dim c as string * 2000 'reserve enough space to store binary version of above code
c=acode$ 'load the code in
def seg=varseg(c) 'set segment to code
func%=4 'we want to try function 4
codeoffs%=varptr(c)

'can't figure out the correct value. Its the value (BP) that is added to the assembly jump table before the jump is executed. If I get this wrong, the code crashes on most options.
addr%=0

call absolute(byval func%,byval addr%,byval varseg(s),byval varptr(s), codeoffs%) 'run the code
end

FUNCTION acode$
'note here, I can reduce the w$ = lines to one and not have the value so long.
w$ = <insert binary code here as hex digits>

cd$ = "": soc% = LEN(w$): FOR z% = 1 TO soc% STEP 2: cd$ = cd$ + CHR$(VAL("&H" + MID$(w$, z%, 2))): NEXT:  acode$ = cd$
END FUNCTION

Now what works in both versions is that if I use a function outside of number 0, 1, 2, or 3, then no function is performed and the assembly routine exits normally.

But if I use any of the functions, the code immediately crashes in the new version (when installed and called as an interrupt), but when I put all the code in one space (and not use it as an interrupt, see above) then the code executes fine. So I'm guessing that with the interrupt version that I am sending the wrong address to SI for the jump but I do not know how to calculate the correct address. Maybe nasm is doing something to the addresses.

I even made another attempt to make things work by adding the following after saving all registers in my interrupt code:

Code: [Select]
mov BP,myinterrupt

And I still am not successful.


How do I calculate the addresses properly so the interrupt routine that is stored in memory can successfully execute the jump without the executing the wrong things?
9
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by MediocreVeg1 on January 18, 2021, 03:06:55 PM »
Quote
I found strange your approach, since sub rdi,rbx (with rbx == 0) do nothing, except affect the flags. Din't you mean to use 'mov rbx,rdi' instead of 'xor rbx,rbx'?
Maybe I'm not understanding how scas works, but isn't the result of scasb stored in rbx in 64-bit assembly? That's why I cleared it with XOR before using scasb and why I substracted it from rdi (which has starting address of string). I'm probably wrong here though.

Quote
PS: Try to use E?? registers as many times as possible. Using R?? will imply a REX prefix and bigger instructions. moving and doing arithmetic/logical operations to E?? registers will automatically zero the upper 32 bits of R?? registers for you (this doen't work in a few instructions as, for example, CDQ [will zero upper RDX but not upper RAX).
Wouldn't the assembler get confused if I used 64-bit syscalls on 32-bit registers? Or if I put some arguments of a syscall in R?? registers and others in E?? registers?

Quote
S: Notice in my routine, if '\0' isn't found in a block of 2³²-1 bytes it returns -1 (all bits set) in RAX. This allows you to test an error:
Yeah, I put it into my procedure as well after you showed your example. Wouldn't this event be highly unlikely though? I think 2^32-1 is like 4294967295 bytes so every single byte after the starting address of the string would have to be non-zero, right?
10
Example Code / Re: My own 64-bit `puts' instruction (No length required)
« Last post by fredericopissarra on January 18, 2021, 02:22:02 PM »
Interesting, I'm not exatly sure how you've used not here (Probably way faster than my sub alternative), but I'll try to figure it out.
Not necessarily 'faster', but since 2³²-1-len is the same as ~len, I just used this fact to calculate the string length. (DEC ECX because we're excluding the final NUL char).

I found strange your approach, since sub rdi,rbx (with rbx == 0) do nothing, except affect the flags. Din't you mean to use 'mov rbx,rdi' instead of 'xor rbx,rbx'?

PS: Try to use E?? registers as many times as possible. Using R?? will imply a REX prefix and bigger instructions. moving and doing arithmetic/logical operations to E?? registers will automatically zero the upper 32 bits of R?? registers for you (this doen't work in a few instructions as, for example, CDQ [will zero upper RDX but not upper RAX).

PS: Notice in my routine, if '\0' isn't found in a block of 2³²-1 bytes it returns -1 (all bits set) in RAX. This allows you to test an error:
Code: [Select]
size_t size = strlen_( str );
if ( (long)size < 0 ) { ... handle error... }
Pages: [1] 2 3 ... 10