汇编学习(7), Bit 操作

2022-12-11 09:35:46 浏览数 (1)

本篇介绍

本篇介绍下汇编中的bit操作。

bit操作

首先写一个包含bit各种操作的函数:

代码语言:javascript复制
; bits1.asm
extern printb  
extern printf
section .data
    msgn1   db  "Number 1",10,0
    msgn2   db  "Number 2",10,0
    msg1    db  "XOR",10,0
    msg2    db  "OR",10,0
    msg3    db  "AND",10,0
    msg4    db  "NOT number 1",10,0
    msg5    db  "SHL 2 lower byte of number 1",10,0
    msg6    db  "SHR 2 lower byte of number 1",10,0
    msg7    db  "SAL 2 lower byte of number 1",10,0
    msg8    db  "SAR 2 lower byte of number 1",10,0
    msg9    db  "ROL 2 lower byte of number 1",10,0
    msg10   db  "ROL 2 lower byte of number 2",10,0
    msg11   db  "ROR 2 lower byte of number 1",10,0
    msg12   db  "ROR 2 lower byte of number 2",10,0
    number1 dq  -72
    number2 dq      1064
section .bss
section .text                           
    global main                     
main:
    mov rbp, rsp; for correct debugging
    push rbp
    mov rbp,rsp
; print number1
mov     rsi, msgn1              
call printmsg   
        mov rdi, [number1]
    call printb
; print number2
mov     rsi, msgn2              
call printmsg
    mov rdi, [number2]
        call printb

; print XOR (exclusive OR)------------------------       
mov     rsi, msg1               
call printmsg           
; xor and print
        mov rax,[number1]
        xor rax,[number2]
        mov rdi, rax
    call printb

; print OR ---------------------------------------             
mov     rsi, msg2               
call printmsg
; or and print
        mov rax,[number1]
        or rax,[number2]
        mov rdi, rax
    call printb

; print AND  ---------------------------------------              
mov     rsi, msg3               
call printmsg           
; and and print
        mov rax,[number1]
        and rax,[number2]
        mov rdi, rax
    call printb

; print NOT  ---------------------------------------              
mov     rsi, msg4               
call printmsg           
; not and print
    mov rax,[number1]
    not rax
    mov rdi, rax
    call printb

; print SHL  (shift left----------------------------              
mov     rsi, msg5               
call printmsg           
; shl and print
    mov rax,[number1]
    shl al,2
    mov rdi, rax
    call printb

; print SHR  (shift right)--------------------------              
mov     rsi, msg6               
call printmsg           
;shr and print
    mov rax,[number1]
    shr al,2
    mov rdi, rax
    call printb

; print SAL  (shift arithmetic left)----------------              
mov     rsi, msg7               
call printmsg           
; sal and print
    mov rax,[number1]
    sal al,2
    mov rdi, rax
    call printb

; print SAR  (shift arithmetic right)----------------              
mov     rsi, msg8               
call printmsg           
; sar and print
    mov rax,[number1]
    sar al,2
    mov rdi, rax
    call printb

; print ROL  (rotate left)---------------------------              
mov     rsi, msg9               
call printmsg           
; rol and print
    mov rax,[number1]
    rol al,2
    mov rdi, rax
    call printb
mov     rsi, msg10              
call printmsg   
    mov rax,[number2]
    rol al,2
    mov rdi, rax
    call printb
; print ROR  (rotate right)---------------------------              
mov     rsi, msg11              
call printmsg           
; ror and print
    mov rax,[number1]
    ror al,2
    mov rdi, rax
    call printb
mov     rsi, msg12              
call printmsg   
    mov rax,[number2]
    ror al,2
    mov rdi, rax
    call printb
leave
ret     


printmsg:   ; print the heading for every bit operation
    section .data
            .fmtstr db  "%s",0
    section .text
                push rbp
                mov rbp, rsp
        mov rdi,.fmtstr
        mov rax,0
        call    printf
        leave
    ret                         

这儿再回顾下leave 和ret的区别: leave 本质上就是epilogue 指令,恢复rsp指针,从栈上弹出rbp指针。 ret本质上也是弹栈,将栈上保存的返回地址弹出并赋值给rip指针,这样就可以接着执行了。

再写一个打印二进制的函数printb:

代码语言:javascript复制
#include <stdio.h>

void printb(long long n){
    long long s,c;  
    for (c = 63; c >= 0; c--)
    {
        s = n >> c;
        // space after every 8th bit
        if ((c 1) % 8 == 0) printf(" ");

        if (s & 1)
                printf("1");
            else
                printf("0");
    }   
    printf("n");
}
输出如下:
Number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 10111000
Number 2
 00000000 00000000 00000000 00000000 00000000 00000000 00000100 00101000
XOR
 11111111 11111111 11111111 11111111 11111111 11111111 11111011 10010000
OR
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 10111000
AND
 00000000 00000000 00000000 00000000 00000000 00000000 00000100 00101000
NOT number 1
 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000111
SHL 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11100000
SHR 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00101110
SAL 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11100000
SAR 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11101110
ROL 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11100010
ROL 2 lower byte of number 2
 00000000 00000000 00000000 00000000 00000000 00000000 00000100 10100000
ROR 2 lower byte of number 1
 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00101110
ROR 2 lower byte of number 2
 00000000 00000000 00000000 00000000 00000000 00000000 00000100 00001010

基本看结果就可以和对应的操作对上。 这儿唯一需要注意的是shl,shr和sal,sar的区别,前者用0填补空出来的最高位,后者会用符号位填补空出来的最高位。 再看一个例子:

代码语言:javascript复制
; bits2.asm
extern printf
section .data

    msgn1   db  "Number 1 is = %d",0
    msgn2   db  "Number 2 is = %d",0
    msg1    db      "SHL 2 = OK multiply by 4",0
    msg2    db      "SHR 2 = wrong divide by 4",0
    msg3    db      "SAL 2 = correctly multiply by 4",0
    msg4    db      "SAR 2 = correctly divide by 4",0
    msg5    db  "SHR 2 = OK divide by 4",0
                                    
    number1 dq  8
        number2 dq      -8
        result dq       0

section .bss
section .text                           
    global main                     
main: 
    push rbp
    mov rbp,rsp    

;SHL            
;positive number
        mov     rsi, msg1
        call printmsg       ;print heading
        mov rsi, [number1]
    call printnbr       ;print number1
        mov rax,[number1]
        shl rax,2       ;multiply by 4 (logic)
        mov rsi, rax
    call printres
;negative number
        mov     rsi, msg1
        call printmsg       ;print heading
        mov rsi, [number2]
        call printnbr       ;print number2  
        mov rax,[number2]
        shl rax,2       ;multiply by 4 (logic)
        mov rsi, rax
    call printres
;SAL
;positive number
        mov     rsi, msg3
        call printmsg       ;print heading  
        mov rsi, [number1]
    call printnbr       ;print number1
        mov rax,[number1]
        sal rax,2       ;multiply by 4 (arithmetic)
        mov rsi, rax
    call printres
;negative number
        mov     rsi, msg3
        call printmsg       ;print heading
        mov rsi, [number2]
        call printnbr       ;print number2  
        mov rax,[number2]
        sal rax,2       ;multiply by 4 (arithmetic)
        mov rsi, rax
    call printres

;SHR
;positive number
        mov     rsi, msg5
        call printmsg       ;print heading  
        mov rsi, [number1]
    call printnbr       ;print number1
        mov rax,[number1]
        shr rax,2       ;divide by 4 (logic)
        mov rsi, rax
    call printres
;negative number
        mov     rsi, msg2
        call printmsg       ;print heading
        mov rsi, [number2]
        call printnbr       ;print number2  
        mov rax,[number2]
        shr rax,2       ;divide by 4 (logic)
        mov [result], rax
        mov rsi, rax
    call printres

;SAR
;positive number
        mov     rsi, msg4
        call printmsg       ;print heading  
        mov rsi, [number1]
    call printnbr       ;print number1
        mov rax,[number1]
        sar rax,2       ;divide by 4 (arithmetic)
        mov rsi, rax
    call printres
;negative number
        mov     rsi, msg4
        call printmsg       ;print heading
        mov rsi, [number2]
        call printnbr       ;print number2  
        mov rax,[number2]
        sar rax,2       ;divide by 4 (arithmetic)
        mov rsi, rax
    call printres

leave
ret

printmsg:
    section .data
            .fmtstr db  10,"%s",10,0 ;format for a string 
    section .text
                 push rbp
                 mov rbp, rsp
        mov rdi,.fmtstr
        mov rax,0
        call    printf
                leave
    ret
        
printnbr:
    section .data
            .fmtstr db  "The original number is %lld",10,0 ;format for an int 
    section .text
                 push rbp
                 mov rbp, rsp
        mov rdi,.fmtstr
        mov rax,0
        call    printf
                leave
    ret 

printres:
    section .data
            .fmtstr db  "The resulting number is %lld",10,0 ;format for an int 
    section .text
                 push rbp
                 mov rbp, rsp
        mov rdi,.fmtstr
        mov rax,0
        call    printf
                leave
    ret                     
输出如下:
SHL 2 = OK multiply by 4
The original number is 8
The resulting number is 32

SHL 2 = OK multiply by 4
The original number is -8
The resulting number is -32

SAL 2 = correctly multiply by 4
The original number is 8
The resulting number is 32

SAL 2 = correctly multiply by 4
The original number is -8
The resulting number is -32

SHR 2 = OK divide by 4
The original number is 8
The resulting number is 2

SHR 2 = wrong divide by 4
The original number is -8
The resulting number is 4611686018427387902

SAR 2 = correctly divide by 4
The original number is 8
The resulting number is 2

SAR 2 = correctly divide by 4
The original number is -8
The resulting number is -2

从结果可以看出,左移也就是放大,逻辑左移和算术左移结果是一样的,可是对于右移,也就是缩小,对于负数,逻辑右移就是明显错误的了,需要用算术右移。

因此涉及符号数字运算的时候,需要用算术移动,另外能用bit运算就用bit运算,有明显的性能优势。

最后再看一个bit操作的例子: 这儿还用到了printb,实现和前面一样,就不再重复。

代码语言:javascript复制
; bits3.asm
extern printb  
extern printf
section .data
    msg1 db "No bits are set:",10,0                                     
    msg2 db 10,"Set bit #4, that is the 5th bit:",10,0  
    msg3 db 10,"Set bit #7, that is the 8th bit:",10,0
    msg4 db 10,"Set bit #8, that is the 9th bit:",10,0
    msg5 db 10,"Set bit #61, that is the 62nd bit:",10,0
    msg6 db 10,"Clear bit #8, that is the 9th bit:",10,0
    msg7 db 10,"Test bit #61, and display rdi",10,0
    bitflags    dq  0
section .bss
section .text                           
    global main                     
main:
    mov rbp, rsp; for correct debugging
    push rbp
    mov rbp,rsp
    ;print title
    mov rdi, msg1
    xor rax,rax
    call printf

    ;print bitflags
        mov rdi, [bitflags]
    call printb

;set bit 4 (=5th bit)
    ;print title
    mov rdi, msg2
    xor rax,rax
    call printf

    bts qword [bitflags],4  ; set bit 4
    ;print bitflags
        mov rdi, [bitflags]
    call printb

;set bit 7 (=8th bit)
    ;print title
    mov rdi, msg3
    xor rax,rax
    call printf

    bts qword [bitflags],7  ; set bit 7
    ;print bitflags
        mov rdi, [bitflags]
    call printb

;set bit 8 (=9th bit)
    ;print title
    mov rdi, msg4
    xor rax,rax
    call printf

    bts qword [bitflags],8  ; set bit 8
    ;print bitflags
        mov rdi, [bitflags]
    call printb

;set bit 61 (=62nd bit)
    ;print title
    mov rdi, msg5
    xor rax,rax
    call printf

    bts qword [bitflags],61     ; set bit 61
    ;print bitflags
        mov rdi, [bitflags]
    call printb

;clear bit 8 (=9th bit)
    ;print title
    mov rdi, msg6
    xor rax,rax
    call printf

    btr qword [bitflags],8  ; bit reset 8
    ;print bitflags
        mov rdi, [bitflags]
    call printb

; test bit 61 (will set carry flag CF if 1)
    ;print title
    mov rdi, msg7
    xor rax,rax
    call printf
        xor rdi,rdi
    mov rax,61          ; bit 61 to be tested
    xor rdx,rdx             ; make sure all bits are 0
    bt  [bitflags],rax      ; bit test
    setc    dil                      ; set dil (=low rdi) to 1 if CF is set
    call printb         ; display rdi       

leave
ret

结果如下:
No bits are set:
 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

Set bit #4, that is the 5th bit:
 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00010000

Set bit #7, that is the 8th bit:
 00000000 00000000 00000000 00000000 00000000 00000000 00000000 10010000

Set bit #8, that is the 9th bit:
 00000000 00000000 00000000 00000000 00000000 00000000 00000001 10010000

Set bit #61, that is the 62nd bit:
 00100000 00000000 00000000 00000000 00000000 00000000 00000001 10010000

Clear bit #8, that is the 9th bit:
 00100000 00000000 00000000 00000000 00000000 00000000 00000000 10010000

Test bit #61, and display rdi
 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

需要注意的如下:

  1. bit操作的索引也是从0开始
  2. bts 用来将某位设置为1
  3. btr用来将某位设置为0
  4. bt指令是测试某个bit位是否是1, bit位索引放到rax中,结果会放到relfag中的CF位
  5. setc是setCC中的一种,对于setc dil,如果reflags中CF是1,那么就会设置rdi的低八位为1。

0 人点赞