0
点赞
收藏
分享

微信扫一扫

【操作系统】x86汇编数学运算和字符串操作 (Basic Math Functions and Using Strings)

修炼之士 2022-03-12 阅读 72

Lab Week 04 实验报告

实验内容

  • 熟悉 Linux 下 x86 汇编语言编程环境
  • 验证实验:Blum’sBook: Sample programs in Chapter08, 10 (Basic Math Functions and Using Strings)

文章目录

👽Preview

Basic knowledge

  1. Size of numbers( or operands)

    Not a few instruction related to numbers usually followed by a indicator letter, like b (for byte), w (for word), or l (for doubleword)

2.下表是通用寄存器(GPRs)所能储存的位数对应的寄存器名称。

image-20220301181312731

Experiment

Chapter08 Basic Math Functions

A. Integer Arithmetic

1.Addition

  • The ADD instruction

The ADD instruction format is

add source, destination

Notice:

  1. The destination parameter can be either a register or a value stored in a memory location

  2. cannot use a memory location for both the source and destination at the same time

  3. The result of the addition is placed in the destination location.

Assembly Code:

# addtest1.s - An example of the ADD instruction
.section .data
data:
   .int 40
.section .text
.globl _start
_start:
   nop
   movl $0, %eax
   movl $0, %ebx
   movl $0, %ecx
   movb $20, %al # 将20移动到8-bit 寄存器AL中
   addb $10, %al # 将10与寄存器AL中的值相加,结果保存在寄存器AL中
   movsx %al, %eax # movsx指令能对符号位进行扩展
   movw $100, %cx 
   addw %cx, %bx 
   movsx %bx, %ebx  
   movl $100, %edx
   addl %edx, %edx
   addl data, %eax  #data位于memory中,将寄存器EAX中的值与data中的值相加,
   addl %eax, data 	
   movl $1, %eax
   movl $0, %ebx
   int $0x80 

Result:

image-20220306171933792

The additions were performed as expected.

Assembly Code:

# addtest2.s - An example of the ADD instruction and negative numbers
.section .data
data:
   .int -40
.section .text
.globl _start
_start:
   nop
   movl $-10, %eax
   movl $-200, %ebx
   movl $80, %ecx
   addl data, %eax
   addl %ecx, %eax
   addl %ebx, %eax
   addl %eax, data
   addl $210, data
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220306173809991

We can easily calculate those results by ourselves, there is nothing unusual, just tell you the negative numbers still works in this situation.

  • Detecting a carry or overflow condition

The ADDB instruction, the carry flag is set if the result is over 255, but in the ADDW instruction, it is not set unless the result is over 65,535.

Assembly Code:

# addtest3.s  An example of detecting a carry condition
.section .text
.globl _start
_start:
   nop
   movl $0, %ebx
   movb $190, %bl 
   movb $100, %al
   addb %al, %bl # 190+100=290>255, exceeds the limit 255
   jc over # jump when Carry flag is set
   movl $1, %eax
   int $0x80
over:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

We can use instruction echo $? to check the return value of register EBX, which will return 290 unless the carry flag is set and will jump to ‘over’ section and return 0;

image-20220306175515914

But, if we change the code

movb $190, %bl  
movb $100, %al -> movb $10, %al

Let’s see what happen

image-20220306180058604


🤖题外话: 从一开始就很好奇想知道.o文件里面到底藏着什么猫腻,但一直没去做,现在试着打开.o文件看看里面到底有什么内容。

这是section .text:的部分

image-20220306180528470

在这里我使用了指令来打开重定位文件addtest3.o,看看里面到底写的是什么。

打开后让我觉得特别兴奋,让我挑出其中几行代码来试着分析一下吧。

 8:   b0 0a                   mov    $0xa,%al

很明显,它是把0xa也就是十进制的10,移动到AL寄存器中。

c:   72 07                   jb     15 <over>

这句可以看到jump指令,跳转地址是section over的15


  • Detecting an error in a signed integer addition

Assembly Code:

# addtest4.s - An example of detecting an overflow condition
.code32
.section .data
output:
   .asciz "The result is %d\n"
.section .text
.globl _start
_start:
   movl $-1590876934, %ebx
   movl $-1259230143, %eax
   addl %eax, %ebx
   jo over
   pushl %ebx
   pushl $output
   call printf
   add  $8, %esp
   pushl $0
   call exit
over:
   pushl $0
   pushl $output
   call printf
   add  $8, %esp
   pushl $0
   call exit

Result:

Screenshot 2022-03-08 at 8.00.52 AM

Apparently, overflow flag has been set, and jo over jump to ‘over’ section, so we got 0 by using printf.

  • The ADC instruction

The ADC instruction can be used to add two unsigned or signed integer values, along with the value contained in the carry flag from a previous ADD instruction.

image-20220308171919144

If we want to add two 64-bit values by using 32-bit registers, there may have carry bit produced by low-order 32-bit, and it should be added to upper 32-bit addition.

In this case, we use ADC instruction to help us implement this.

Assembly Code:

# adctest.s - An example of using the ADC instruction
.section .data
data1:
   .quad 7252051615
data2:
   .quad 5732348928
output:
   .asciz "The result is %qd\n"
.section .text
.globl _start
_start:
   movl data1, %ebx #取低32位
   movl data1+4, %eax # 移动4个字节,相当于取高32位,注意是小端存储方式
   movl data2, %edx
   movl data2+4, %ecx
   addl %ebx, %edx # 先将低32位加起来
   adcl %eax, %ecx # add the two high-order registers, along with the carry flag.
   pushl %ecx
   pushl %edx
   push $output
   call printf
   addl $12, %esp
   pushl $0
   call exit

Result:

Before ADD instruction:

image-20220308174810137

After ADD instruction:

image-20220308174856239

Then we got the answer.

image-20220308175153402

2. Subtraction

  • The SUB instruction

Assembly Code:

# subtest1.s - An example of the SUB instruction
.section .data
data:
   .int 40
.section .text
.globl _start
_start:
   nop
   movl $0, %eax
   movl $0, %ebx
   movl $0, %ecx
   movb $20, %al
   subb $10, %al
   movsx %al, %eax
   movw $100, %cx
   subw %cx, %bx # subtracts the value in CX from the value in register BX
   movsx %bx, %ebx
   movl $100, %edx
   subl %eax, %edx
   subl data, %eax
   subl %eax, data
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

Take one example

subb $10, %al subtracts 10 from the value in register AL, So we got 10 in register AL.

image-20220308180244473

subl %eax, data the last sub instruction, subtracts the value in the EAX register (-30) from the value at the data1
memory location (40): so we got 70.

image-20220308180505991

  • Carry and overflow with subtraction

Assembly Code:

# subtest2.s - An example of a subtraction carry
.section .text
.globl _start
_start:
   nop
   movl $5, %eax  # 二进制 101
   movl $2, %ebx  # 二进制 010
   subl %eax, %ebx # 010 - 101 这时产生借位
   jc under
   movl $1, %eax
   int $0x80
under:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220308193728027

The result(the value in EBX) will be either the subtraction value or 0 if CF is set and jc under will jump to ‘under’ section then set EBX to 0. Apparently, the result is 0.

Let’s view the subtraction value in EBX.

image-20220308194442895

The carry flag was set when the result was less than zero (which is invalid in unsigned integers) besides. It is used to determine when subtracting unsigned integers produces a negative result.

  • Subtracting with signed integers

Assembly Code:

# subtest3.s - An example of an overflow condition in a SUB instruction
.code32
.section .data
output:
   .asciz "The result is %d\n"
.section .text
.globl _start
_start:
   movl $-1590876934, %ebx
   movl $1259230143, %eax
   subl %eax, %ebx  # -1590876934-1259230143  -> too large for the 32-bit EBX register
   jo over 
   pushl %ebx
   pushl $output
   call printf
   add  $8, %esp
   pushl $0
   call exit
over:
   pushl $0
   pushl $output
   call printf
   add  $8, %esp
   pushl $0
   call exit

Result:

image-20220308200748542

As the result above, the subtraction result is out of limit, overflow flag has been set, and jump to ‘over’ section, then print 0;

  • The SBB instruction

This instruction is similar to ADC instruction which was introduced in last section previously.

The format of the SBB instruction is:
sbb source, destination

Notice: both source and destination can be 8- , 16-, or 32-bit registers or values in memory, but you cannot use memory locations for both the source and destination values at the same time.

image-20220308201455246

Usage:

When the previous SUB instruction is executed and a carry results, the carry bit is “borrowed” by the SBB instruction to continue the subtraction on the next data pair. (As demonstrated in the figure above)

Assembly Code:

# sbbtest.s - An example of using the SBB instruction
.section .data
data1:
   .quad 7252051615
data2:
   .quad 5732348928
output:
   .asciz "The result is %qd\n"
.section .text
.globl _start
_start:
   nop
   movl data1, %ebx
   movl data1+4, %eax
   movl data2, %edx
   movl data2+4, %ecx
   subl %ebx, %edx
   sbbl %eax, %ecx
   pushl %ecx
   pushl %edx
   push $output
   call printf
   add  $12, %esp
   pushl $0
   call exit

Result:

image-20220308201747224

3. Multiplication

  • Unsigned integer multiplication using MUL

! The MUL instruction can only be used for unsigned integers

The format for the MUL instruction is
mul source

The MUL instruction is used to multiply two unsigned integers.

? : How MUL instruction multiply two unsigned integers with just one operand?

Ans : one of the operands used in the multiplication must be placed in the AL, AX, or EAX registers, depending on the size of the value.

image-20220308210129959

Assembly Code:

# multest.s - An example of using the MUL instruction
.section .data
data1:
   .int 315814
data2:
   .int 165432
result:
   .quad 0
output:
   .asciz "The result is %qd\n"
.section .text
.globl _start
_start:
   nop
   movl data1, %eax
   mull data2
   movl %eax, result # 低32位
   movl %edx, result+4 # 高32位
   pushl %edx
   pushl %eax
   pushl $output
   call printf
   add $12, %esp
   pushl $0
   call exit

Result:

In this case, the result from the EDX:EAX register pair is both loaded into a 64-bit memory location (using indexed memory access) and displayed using the printf C function.

image-20220308204231911

image-20220308205803903

  • Signed integer multiplication using IMUL

The IMUL instruction can be used by both signed and unsigned integers

For larger values, the IMUL instruction is only valid for signed integers.

The first format of the IMUL instruction

imul source or imul source, destination

or imul multiplier, source, destination

I.For the first format, the source operand can be an 8-, 16-, or 32-bit register or value in memory, and it is multiplied with the implied operand located in the AL, AX, or EAX registers (depending on the source operand size). The result is then placed in the AX register, the DX:AX register pair(32-bit), or the EDX:EAX (64-bit) register pair.

II.For the second format, the source operand can be a 16- or 32-bit register or value in memory, and destination must be a 16- or 32-bit GPR. Putting the result to what register(the destination) is determined by yourself.

III.For the third format, where multiplier is an immediate value, source is a 16- or 32-bit register or value in memory, and destination must be a general-purpose register. You can perform a quick multiplication of a value (the source) with a signed integer (the multiplier), storing the result in a GPR (the destination).

Assembly Code:

# imultest.s - An example of the IMUL instruction formats
.section .data
value1:
   .int 10
value2:
   .int -35
value3:
   .int 400
.section .text
.globl _start
_start:
   nop
   movl value1, %ebx
   movl value2, %ecx
   imull %ebx, %ecx  # imul source, destination
   movl value3, %edx
   imull $2, %edx, %eax # imul multiplier source, destination
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220308211936260

Getting this result is obvious though.

  • Detecting overflows

Every calculation can suffer overflows and that is inevitably will cost unless you detecting them before you using them.

Assembly Code:

# imultest2.s - An example of detecting an IMUL overflow
.section .text
.globl _start
_start:
   nop
   movw $680, %ax
   movw $100, %cx
   imulw %cx
   jo over
   movl $1, %eax
   movl $0, %ebx
   int $0x80
over:
   movl $1, %eax
   movl $1, %ebx
   int $0x80

Result:

image-20220308212744316

As is demonstrated in the figure above, the returned value is 1, which indicate the multiplication is overflow, and the jo over instruction is executed and then jump to ‘over’ section. ( The limit of 16-bit register is 65,535)

4. Division

  • Unsigned division

The format of the DIV instruction is
div divisor

where divisor is the value that is divided into the implied dividend, and can be an 8-, 16-, or 32-bit register or value in memory

The dividend must already be stored in the AX register (for a 16-bit value), the
DX:AX register pair (for a 32-bit value), or the EDX:EAX register pair (for a 64-bit value) before the DIV instruction is performed.

The result of the division is two separate numbers: the quotient and the remainder. Both values are stored in the same registers used for the dividend value.

The following table shows how this is set up.

image-20220308214703175

Assembly Code:

# divtest.s - An example of the DIV instruction
.section .data
dividend:
   .quad 8335
divisor:
   .int 25
quotient:
   .int 0
remainder:
   .int 0
output:
   .asciz "The quotient is %d, and the remainder is %d\n"
.section .text
.globl _start
_start:
   nop
   movl dividend, %eax
   movl dividend+4, %edx
   divw divisor                      # WRONG prefix!
   movl %eax, quotient
   movl %edx, remainder
   pushl remainder
   pushl quotient
   pushl $output
   call printf
   add  $12, %esp
   pushl $0
   call exit

Because the dividend is a 64-bit value, so it should be loaded into two registers with upper and lower bits respectively in EDX:EAX pair.

Result:

image-20220308214629441

B. Shift Instructions

1.Multiply by shifting

Multiplying integers by a power of 2, the following instructions can help.

SAL (shift arithmetic left) and SHL (shift logical left). Both of these instructions
perform the same operation, and are interchangeable. They have three different formats:

sal destination shifts the destination value left one position

sal %cl, destination shifts the destination value left by the number of times specified in the CL register.

sal shifter, destination shifts the destination value left the number of times indicated by the shifter value.

The destination operand can be an 8-, 16-, or 32-bit register or value in memory.

Assembly Code:

# saltest.s - An example of the SAL instruction
.section .data
value1:
   .int 25
.section .text
.globl _start
_start:
   nop
   movl $10, %ebx
   sall %ebx #sal destination 左移1位 乘2
   movb $2, %cl
   sall %cl, %ebx # sal %cl, destination 左移2位 乘4
   sall $2, %ebx # sal shifter, destination 左移2位 乘4
   sall value1 #sal destination 左移1位 乘2
   sall $2, value1 # sal shifter, destination 左移2位 乘4
   movl $1, %eax
   movl $0, %ebx
   int $0x80
   

Result:

image-20220308221138834

After all sall instructions were performed, the result in EBX and memory is displayed in the figure above.

C. Decimal Arithmetic

1.Unpacked BCD arithmetic

这里讨论的是非压缩型BCD码,它用一个字节表示一个一位十进制数。

而压缩型BCD码则压缩BCD码的每一位用4位二进制表示,一个字节表示两位十进制数。

Notice:

The AAA, AAS, and AAM instructions assume that the previous operation result is placed in the AL register, and converts that value to unpacked BCD format.

The AAD instruction assumes that the dividend value is placed in the AX register
in unpacked BCD format, and converts it to binary format for the DIV instruction to handle.

The result is a proper unpacked BCD value, the quotient in the AL register, and the remainder in the AH register (in unpacked BCD format).

Here is the figure for the following code.

image-20220308223142206

Assembly Code:

# aaatest.s - An example of using the AAA instruction
.section .data
value1:
   .byte 0x05, 0x02, 0x01, 0x08, 0x02  # 28125
value2:
   .byte 0x03, 0x03, 0x09, 0x02, 0x05  # 52933
.section .bss
   .lcomm sum, 6
.section .text
.globl _start
_start:
   nop
   xor %edi, %edi # 异或清零操作
   movl $5, %ecx # 设置循环次数的上限为5
   clc # CLC instruction before the loop to ensure that the carry flag is cleared
loop1:
   movb value1(, %edi, 1), %al #将value1中一子节大小的值移到AL(16-bit)中
   adcb value2(, %edi, 1), %al #将value2中的值与AL中的值相加,并且考虑低位的进位
   aaa # 对加法操作的结果转换成Unpacked BCD
   movb %al, sum(, %edi, 1)
   inc %edi
   loop loop1
   adcb $0, sum(, %edi, 4)
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

After the third time the ADC instruction is executed (the third value place), the AL register contains the value:

image-20220309091527533

这表示0x01+0x09 = 0xa(1010)

当aaa指令执行后:

image-20220309092937355

AL中的值被转换成Unpacked BCD的格式,并且产生了进位1

以下是加法操作的结果,可见sum中的值都为Unpacked BCD格式

image-20220309093331463

  • Packed BCD arithmetic

image-20220309094028025

Assembly Code:

# dastest.s - An example of using the DAS instruction
.section .data
value1:
   .byte 0x25, 0x81, 0x02
value2:
   .byte 0x33, 0x29, 0x05
.section .bss
   .lcomm result, 4
.section .text
.globl _start
_start:
   nop
   xor %edi, %edi
   movl $3, %ecx
loop1:
   movb value2(, %edi, 1), %al
   sbbb value1(, %edi, 1), %al
   das
   movb %al, result(, %edi, 1)
   inc %edi
   loop loop1
   sbbb $0, result(, %edi, 4)
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

For example, after the first subtraction, the EAX register has the following
value:

00110011 - 00100101 = 00001110 (0x0e)

image-20220309094407408

After das instruction, convert to packed BCD

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKTX9jdr-1646812975304)(/Users/maxwell/Library/Application Support/typora-user-images/image-20220309094434690.png)]

D. Logical Operations

  • Bit testing

Assembly Code:

# cpuidtest.s - An example of using the TEST instruction
.section .data
output_cpuid:
   .asciz "This processor supports the CPUID instruction\n"
output_nocpuid:
   .asciz "This processor does not support the CPUID instruction\n"
.section .text
.globl _start
_start:
   nop
   pushfl
   popl %eax
   movl %eax, %edx
   xor $0x00200000, %eax
   pushl %eax
   popfl
   pushfl
   popl %eax
   xor %edx, %eax
   test $0x00200000, %eax
   jnz cpuid
   pushl $output_nocpuid
   call printf
   add  $4, %esp
   pushl $0
   call exit
cpuid:
   pushl $output_cpuid
   call printf
   add  $4, %esp
   pushl $0
   call exit

Result:

image-20220309110441872

Chapter10 Using Strings

1.Moving Strings

movs instructions use implied source and destination operands

source operand: ESI register

destination operand: EDI register

The instruction
leal output, %edi loads the effective address of an object.
e.g. loads the 32-bit memory location of the output label to the EDI register

Assembly Code:

# movstest1.s - An example of the MOVS instructions
.section .data
value1:
   .ascii "This is a test string.\n"
.section .bss
   .lcomm output, 23
.section .text
.globl _start
_start:
   nop
   leal value1, %esi # source
   leal output, %edi # destination
   movsb # it moves 1 byte of data from the value1 location to the output location. 
   movsw # it moves 2 byte of data from the value1 location to the output location. 
   movsl # it moves 4 byte of data from the value1 location to the output location. 

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309120610012

一个字母为1Byte。 movsbmovsw、 ``movsl`分别move 1、2、4Byte, 也就是1、2、4个字母

  • Using DF Flags(操作方向标志位 DF(Direction Flag)

If the DF flag is cleared, the ESI and EDI registers are incremented after each MOVS instruction.

If the DF flag is set, the ESI and EDI registers are decremented after each MOVS instruction

Assembly Code:

# movstest2.s - A second example of the MOVS instructions
.section .data
value1:
   .ascii "This is a test string.\n"
.section .bss
   .lcomm output, 23
.section .text
.globl _start
_start:
   nop
   leal value1+22, %esi
   leal output+22, %edi

   std # set DF flag,ESI and EDI registers are decremented after each MOVS instruction
   movsb
   movsw
   movsl

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309121911333

这里需要注意的是,在代码中用std设定了DF,这时ESI、EDI寄存器在执行完 ·movs 后都会递减,这时寄存器应该从后往前移动,但是他们读取字符串的方向仍然是向前读取的,在执行完movsbmovsw、 ``movsl`后ESI、EDI寄存器分别递减1、2、4Byte,这也就是为什么output中有4个地址空间有值。

  • Copying a large string

Copying a large string can simply use a loop with the MOVL instruction, controlled by the ECX register set to the length of the string.

Assembly Code:

# movstest3.s - An example of moving an entire string
.section .data
value1:
   .ascii "This is a test string.\n"
.section .bss
   .lcomm output, 23
.section .text
.globl _start
_start:
   nop
   leal value1, %esi
   leal output, %edi
   movl $23, %ecx #设置循环上限
   cld  # clear the DF flag,incremented 
loop1:
   movsb  #移动单位为Byte。每执行完毕,ESI和EDI递增
   loop loop1

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309123348208

2. The REP prefix

  • Moving a string byte by byte

rep is used to repeat a string instruction a specific number of times, controlled by the value in the ECX register.

Assembly Code:

# reptest1.s - An example of the REP instruction
.section .data
value1:
   .ascii "This is a test string.\n"
.section .bss
   .lcomm output, 23
.section .text
.globl _start
_start:
   nop
   leal value1, %esi
   leal output, %edi
   movl $23, %ecx # The size of the string
   cld
   rep movsb # move a single byte of data 23 times
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309123941948

  • Moving strings block by block

    rep在指令movsl上同样适用,这样能够一次移动4个字符。

Assembly Code:

# reptest2.s - An incorrect example of using the REP instruction
.section .data
value1:
   .ascii "This is a test string.\n"
value2:
   .ascii "Oops"
.section .bss
   .lcomm output, 23
.section .text
.globl _start
_start:
   nop
   leal value1, %esi
   leal output, %edi
   movl $6, %ecx #  loop 6 times to move blocks of 4 bytes of data.
   cld # clear the DF flag,incremented  incremented
   rep movsl

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309124353828

It will erroneously pick up a byte from the next string defined.(the output end with ‘0’ )

(因为Value1中的字符串长为23,这里进行6次循环,每次移动4个字符,共24个字符,因此最后会有一个多余字符的出现)

  • Moving large strings

Assembly Code:

# reptest3.s - Moving a large string using MOVSL and MOVSB
.section .data
string1:
   .asciz "This is a test of the conversion program!\n"
length:
   .int 43
divisor:
   .int 4
.section .bss
   .lcomm buffer, 43
.section .text
.globl _start
_start:
   nop
   leal string1, %esi
   leal buffer, %edi
   movl length, %ecx
   shrl $2, %ecx # shift the length value right 2 bits, length divided by 4, then quotient value loaded into the ECX register.
   cld
   rep movsl # repeat (ECX) times,move 4bytes per time.
   movl length, %ecx
   andl $3, %ecx
   rep movsb

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309140658893

image-20220309140735914

  • Moving a string in reverse order

Assembly Code:

# reptest4.s - An example of using REP backwards
.section .data
value1:
   .asciz "This is a test string.\n"
.section .bss
   .lcomm output, 24
.section .text
.globl _start
_start:
   nop
   leal value1+22, %esi
   leal output+22, %edi
   movl $23, %ecx
   std  # sets the DF flag,decreased
   rep movsb 

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309141025003

从后往前进行move操作,

3. Storing and Loading Strings

  • The LODS instruction and the STOS instruction

Assembly Code:

# stostest1.s - An example of using the STOS instruction
.section .data
space:
   .ascii " "
.section .bss
   .lcomm buffer, 256
.section .text
.globl _start
_start:
   nop
   leal space, %esi
   leal buffer, %edi
   movl $256, %ecx
   cld
   lodsb # Loads a byte into the AL register
   rep stosb # repeat store

   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309141635024

从EAX的值可以看到,现在存储在EAX中的是space character,它的ASCII正是0x20,这正是指令lodsb将其move到EAX中的,随后执行指令 stosb , 将EAX中的值store到memory中,这里存到了 256-byte的 buffer中。

  • Building your own string functions

Assembly Code:

# convert.s - Converting lower to upper case
.section .data
string1:
   .asciz "This is a TEST, of the conversion program!\n"
length:
   .int 43
.section .text
.globl _start
_start:
   nop
   leal string1, %esi
   movl %esi, %edi
   movl length, %ecx
   cld
loop1:
   lodsb
   cmpb $'a', %al # 字符a与AL中的值比
   jl skip  # 如果(al)<'a'跳过
   cmpb $'z', %al
   jg skip
   subb $0x20, %al
skip:
   stosb # The STOSB instruction is run on each character, and then the code loops back for the next character until it runs out of characters in the string
   loop loop1
end:
   pushl $string1
   call printf
   addl $4, %esp # 栈指针上移,
   pushl $0
   call exit

Result:

这里通过 cmpb指令判断字符是否在a~z这个range里面,如果在此范围内,则将它减20,这相当于将它转换成大写字母。

image-20220309141938997

4. Comparing Strings

Assembly Code:

# cmpstest1.s - A simple example of the CMPS instruction
.section .data
value1:
   .ascii "Test" # 4个Bytes
value2:
   .ascii "Test"  # 4个Bytes
.section .text
.globl _start
_start:
   nop
   movl $1, %eax
   leal value1, %esi
   leal value2, %edi
   cld
   cmpsl
   je equal # Compares a doubleword (4 bytes) value
   movl $1, %ebx
   int $0x80
equal:
   movl $0, %ebx
   int $0x80

Result:

image-20220309142153022

这说明两个比较的字符串是相等的。

  • Using REP with CMPS

If non match has been detected, the ECX register will contain the position of the mismatched character (counting back from the end of the string).

Assembly Code:

# cmpstest2.s - An example of using the REPE CMPS instruction
.section .data
value1:
   .ascii "This is a test of the CMPS instructions"
value2:
   .ascii "This is a test of the CMPS Instructions"
.section .text
.globl _start
_start:
   nop
   movl $1, %eax
   lea value1, %esi
   leal value2, %edi
   movl $39, %ecx
   cld
   repe cmpsb
   je equal
   movl %ecx, %ebx
   int $0x80
equal:
   movl $0, %ebx
   int $0x80

Result:

这里进行逐字节的字符比较,value1和value2中只有i和I不同,cmpsb一旦检测到字符不同,会设定zero flag, 而je 指令检测zero flag,如果被set,则不执行jump。这里返回的是不匹配的第一个字符的index(index从后往前数)。

image-20220309142323612

  • String inequality

image-20220309153132346

Assembly Code:

# strcomp.s - An example of comparing strings
.section .data
string1:
   .ascii "test"
length1:
   .int 4
string2:
   .ascii "test1"
length2:
   .int 5
.section .text
.globl _start
_start:
   nop
   lea string1, %esi
   lea string2, %edi
   movl length1, %ecx
   movl length2, %eax
   cmpl %eax, %ecx
   ja longer
   xchg %ecx, %eax
longer:
   cld
   repe cmpsb
   je equal
   jg greater
less:
   movl $1, %eax
   movl $255, %ebx
   int $0x80
greater:
   movl $1, %eax
   movl $1, %ebx
   int $0x80
equal:
   movl length1, %ecx
   movl length2, %eax
   cmpl %ecx, %eax
   jg greater
   jl less
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

这里将"test"和"test1"用cmpl %eax, %ecx进行比较,显然,第一个字符串比第二个小,因此跳转到“less”。

从返回值即可看出。

image-20220309142458616

5. The SCAS instruction

The SCAS instructions use an implied destination operand of the EDI register. The EDI register must contain the memory address of the string to scan.

The ESI register contains the search character.

The ECX register contains the position from the end of the string that contains the search character.

❑ REPE: Scans the string characters looking for a character that does not match the search
character

❑ REPNE: Scans the string characters looking for a character that matches the search character.

Assembly Code:

# scastest1.s - An example of the SCAS instruction
.section .data
string1:
   .ascii "This is a test - a long text string to scan."
length:
   .int 44
string2:
   .ascii "-"
.section .text
.globl _start
_start:
   nop
   leal string1, %edi 
   leal string2, %esi # 当前要搜索的是'-'
   movl length, %ecx
   lodsb
   cld
   repne scasb
   jne notfound
   subw length, %cx
   neg %cx
   movl $1, %eax
   movl %ecx, %ebx
   int $0x80
notfound:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

返回目标字符的index 16。

image-20220309142654518

  • Scanning for multiple characters

SCASW和SCASL指令遍历字符串,在AX或EAX寄存器中查找字符序列,但它们不执行逐字符比较。相反,在每次比较之后,EDI寄存器增加2(用于SCASW)或4(用于SCASL),而不是增加1。

Assembly Code:

# scastest2.s - An example of incorrectly using the SCAS instruction
.section .data
string1:
   .ascii "This is a test - a long text string to scan."
length:
   .int 11
string2:
   .ascii "test"
.section .text
.globl _start
_start:
   nop
   leal string1, %edi
   leal string2, %esi
   movl length, %ecx
   lodsl
   cld
   repne scasl
   jne notfound
   subw length, %cx
   neg %cx
   movl $1, %eax
   movl %ecx, %ebx
   int $0x80
notfound:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309142752390

  • Finding a string length

利用寻找terminating zero来计算出字符串的长度。

Assembly Code:

# strsize.s - Finding the size of a string using the SCAS instruction
.section .data
string1:
   .asciz "Testing, one, two, three, testing.\n"
.section .text
.globl _start
_start:
   nop
   leal string1, %edi
   movl $0xffff, %ecx # The ECX register will keep track of how many iterations it takes to find the terminating zero in the string. 
   movb $0, %al
   cld
   repne scasb
   jne notfound
   subw $0xffff, %cx
   neg %cx
   dec %cx
   movl $1, %eax
   movl %ecx, %ebx
   int $0x80
notfound:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Result:

image-20220309142903394

最后,我们得到了字符串的长度为35,包含terminating zero。

举报

相关推荐

0 条评论