0

I wrote the following x86-64 functions to be called in a C program: The first one takes in a 2-digit hexadecimal character and rotates its bit towards the right by one bit, i.e, if '12'(means 0x12 but 0x is not feeded as input) is rotated right by one bit to give '09'(0x09)

.file "rotate_right.s"
.section .rodata
.data
.globl rotate_right
.type   rotate_right, @function
.text
rotate_right:
pushq %rbp
movq %rsp,%rbp

pushq %rsi
pushq %rdi
pushq %rbx

subl $4, %esp
movb 8(%ebp), %al
sarb $1, %al

leave
ret
.size rotate_right, .-rotate_right

Similarly, this function rotates the bits to the left one position, so '12'(0x12) becomes '24'(0x24).

.file "rotate_left.s"
.section .rodata
.data
.globl rotate_left
.type   rotate_left, @function
.text
rotate_left:
pushq %rbp
movq %rsp,%rbp

pushq %rsi
pushq %rdi
pushq %rbx

subl $4, %esp
movb 8(%ebp), %al
sarb $1, %al

leave
ret
.size rotate_left, .-rotate_left

The create_key() function gets a four bit input like 0110 and outputs an 8-bit output as unsigned int i.e 01100110:

.file "create_key.s"
.section .rodata
ask_key:
.string "Enter 4-bit key:"
.data
.globl create_key
.type   create_key, @function
.text
create_key:
pushq %rbp
# stack holding
movq %rsp, %rbp
movl $ask_key, %edi
# printing the ask string
movl $0, %eax
# calling the C functions

call printf
movl $0,%esi
# rsi is set to 0. the key
pushq %rsi
# take it to the stack

# call getchar for getting each key bit
call getchar
popq %rsi
subl $48,%eax
# doing this will give us 1 or 0 if the we input 1 or 0
sall $1,%esi
# shift the key by one bit
orl %eax,%esi
# OR key and the sigle input
pushq %rsi
# push rsi to stack to save the value in rsi

# Do the above operation a total of 4 times
call getchar
popq %rsi
subl $48,%eax
sall $1,%esi
orl %eax,%esi
pushq %rsi

call getchar
popq %rsi
subl $48,%eax
sall $1,%esi
orl %eax,%esi
pushq %rsi

call getchar
popq %rsi
subl $48,%eax
sall $1,%esi
orl %eax,%esi

# copy first 4-bits of the key into %rax
movl %esi,%eax
#left shift the key 4 times
sall $4,%esi
# OR the secont 4-bits into %rax
orl %esi,%eax
leave
ret
# return the values and end the function
.size create_key, .-create_key

Here is the C-program,

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>

extern unsigned int create_key();
extern unsigned char rotate_left(unsigned char x);
extern unsigned char rotate_right(unsigned char x);

int main(){
/* This Part Takes The Input To Be Ciphered 
    And Prints The Hexadecimal Value*/
    
char word[200], outword[400], xor_hex[400], hex_rot[400], ch, hex[2];
unsigned int i_key, encodant, rotated;
int i, len, j;
/* i_key is the integer equvivalent of the cipher key*/

printf("enter cleartext:");

i = 0;
ch = getchar();
while(ch!='\n'){
    word[i] = ch;
    ++i;
    ch = getchar();
}
fflush(stdin);
len = i;
word[i] = '\0';
printf("%s\n", word);
printf("hex encoding:\n");
for(i = 0; i<len; i++){
    sprintf(outword+(i*2), "%02X", word[i]);
}
for(i=0;i<(2*len);i++){
    if(i%2==0 & i>0)printf(" ");
    if(i%20==0 & i>0)printf("\n");
    printf("%c", outword[i]);
}
printf("\n");

/* This Part Asks For The Cipher Key */
i_key =  create_key();

/* XOR Encoding of the Hex Cyphertext*/
for(i=0;i<len*2;i+=2){
    hex[0] = outword[i];
    hex[1] = outword[i+1];
    encodant = (int)strtol(hex, NULL, 16);
    sprintf(xor_hex+(i), "%02X", (i_key^encodant));
}

/* Encoding the text using bit rotation */
j=1;
for(i=0;i<len*2;i+=2){
    hex[0]=xor_hex[i];
    hex[1]=xor_hex[i+1];
    encodant = (int)strtol(hex, NULL, 16);
    if(j%2==0)rotated = rotate_right(encodant);
    else rotated = rotate_left(encodant);
    j++;
    sprintf(hex_rot+(i), "%02X", rotated);
}

/* Printing The Finished Ciphered Text */
printf("hex ciphertext:\n");
for(i=0;i<(2*len);i++){
    if(i%2==0 & i>0)printf(" ");
    if(i%20==0 & i>0)printf("\n");
    printf("%c", hex_rot[i]);
}
printf("\n");

return 0;
}

The function prototypes can't be changed, i.e rotate functions must be char and have char parameters, the create_key function is good appearently, but my code gives segmentation fault. I don't know what to do in this situation so any help is appreciated.

mediocrevegetable1
  • 4,086
  • 1
  • 11
  • 33
Ego
  • 13
  • 4
  • I am not very used to assembly language, but this looks weird: `pushq %rbp movq %rsp,%rbp`. If I am correct, this saves RBP into stack, but **erases** the stack pointer with RBP. Shouldn't it be `movq %rbp, %rsp`? – Serge Ballesta Mar 31 '21 at 08:31
  • Super common FAQs: use `int` for the result of `getchar`. `fflush(stdin)` is undefined behavior. You seem in need of a different source for learning C, since your current one is teaching you wrong. – Lundin Mar 31 '21 at 08:33
  • 1
    Did you run your program in a debugger? It will point you to the place of the erroneous access. -- Did you check your assembler function separately? – the busybee Mar 31 '21 at 09:26
  • I don't know that much about x86-64 Att assembly I myself started a couple of days ago. – Ego Mar 31 '21 at 09:57
  • and whenever I try to compile them seperately, I get this error message for both rotate functions: – Ego Mar 31 '21 at 10:03
  • Error: junk at end of line, first unrecognized character is `r' – Ego Mar 31 '21 at 10:03
  • The create_key() was also made this way, but it also gave similar error. Both files also gave " Warning: .type pseudo-op used outside of .def/.endef: ignored." and " Warning: .size pseudo-op used outside of .def/.endef: ignored." warnings – Ego Mar 31 '21 at 10:05
  • @SergeBallesta: This is AT&T syntax; destination last (or more generally, list of operands in reverse order from Intel syntax). `mov %rsp,%rbp` is correct at the top of a function for setting RBP up as a frame pointer, if you want to both with that at all. But of course `movb 8(%ebp), %al` is going to segfault in 64-bit code; truncating a stack address to 32-bit will produce an invalid address. – Peter Cordes Mar 31 '21 at 15:45
  • @PeterCordes: Thank you for the information. I had used 80x86 assembly, but never heard of the AT&T syntax. Day is not lost... – Serge Ballesta Mar 31 '21 at 15:48
  • @SergeBallesta: https://stackoverflow.com/tags/att/info - GCC (and clang) use it by default, unless you compile with `-masm=intel` to get their dialect of Intel syntax. – Peter Cordes Mar 31 '21 at 15:51
  • @Lundin: POSIX.1-2008 does specify the behaviour of `fflush(input_stream)` at least for seekable files. It's not clear from the Linux or POSIX man pages whether you get a safe no-op when stdin is a TTY, or whether it's UB. https://man7.org/linux/man-pages/man3/fflush.3.html. Anyway, highly unlikely to be *useful* here, and vastly over-complicated for a [mcve] of passing args / getting return value from an asm function, with OS unspecified. – Peter Cordes Mar 31 '21 at 15:58
  • @PeterCordes POSIX wasn't mentioned in the question. And in general, I don't care about POSIX. – Lundin Apr 01 '21 at 07:02
  • @Lundin: I think the OP is using Linux (perhaps via WSL), based on the calling convention they're using. Unless they changed everything else to match Windows x64, including their manual printf calls. That's why I mentioned POSIX, because it means glibc on Linux also supports that. – Peter Cordes Apr 01 '21 at 16:31

1 Answers1

0

There is no need to do anything with the stack here. Take the argument from rdi (SysV) or rcx (Win32), put it in al, rotate and return:

    .file "rotate.S"

    .text

    .globl rotate_right
rotate_right:
    mov %rdi, %rax
    shrb $1, %al
    ret

    .globl rotate_left
rotate_left:
    mov %rdi, %rax
    shlb $1, %al
    ret

    .end

Now that is GNU as syntax, it may require some adjustment for AT&T asm.

To test it:

#include <stdio.h>

unsigned char rotate_left(unsigned char);
unsigned char rotate_right(unsigned char);

int main() {
    printf("%02x %02x\n", rotate_left(0x12), rotate_right(0x12));
    return 0;
}

Prints:

24 09
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • I did exactly that, and the code did run, but my output was: c0 30 – Ego Mar 31 '21 at 11:04
  • What is your OS? – rustyx Mar 31 '21 at 11:33
  • I have Windows 10 OS – Ego Mar 31 '21 at 13:32
  • I need to ask, does my create_key() function have the same problems? – Ego Mar 31 '21 at 13:42
  • 1
    For Win32 the calling convention is different - 1st function argument is passed in `rcx` instead of `rdi`. See [here](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention) for details. Regarding create_key() - it's unclear what it's supposed to do, so can't really advise on that. I do see that it overwrites `rdi`, but on Win32 `rdi` must be preserved. – rustyx Mar 31 '21 at 14:10
  • @Ego: How did you get a "segmentation fault" on Windows 10? That's a Unix/Linux signal. Are you using WSL under Windows or something? You can check what calling convention your C compiler is using by checking its asm output (with a disassembler) to see which register a simple test function is putting the arg in when calling your function. – Peter Cordes Mar 31 '21 at 15:49
  • Thank you, it worked @rustyx – Ego Mar 31 '21 at 16:03
  • rustyx and @Ego: A more efficient way to implement these would be `movzbl %dil, %eax` / `shr $1, %eax`. Or just `mov %edi, %eax` / `shr $1, %al` to save code size, since you declared the return value as `unsigned char` so you're not responsible for zeroing upper bytes. But also no need to copy the upper 32 bits of garbage from RDI. – Peter Cordes Mar 31 '21 at 19:52
  • Also worth mentioning that this is a shift, not a rotate. `ror $1, %al` would shift bits from the bottom into the top, instead of shifting in zeros. – Peter Cordes Mar 31 '21 at 19:54
  • Thank you everyone. That really helped me – Ego Apr 01 '21 at 14:09