现在的位置: 首页 > 综合 > 正文

gas学习

2013年06月22日 ⁄ 综合 ⁄ 共 5118字 ⁄ 字号 评论关闭

1. 入门

利用 cpuid 取得 intel 集容cpu的厂商信息。

.section .data
output1:
        .ascii "当前CPU厂商ID是:"
output2:
        .ascii "xxxxxxxxxxxx\n"
.section .text
.global _start
_start:
        movl $0,%eax
        cpuid

        movl $output2,%edi
        movl %ebx,(%edi)
        movl %edx,4(%edi)
        movl %ecx,8(%edi)

        movl $4,%eax
        movl $1,%ebx
        movl $output1,%ecx
        movl $(output2-output1+14),%edx
        int $0x80

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

编译和链接上面的源代码并运行:

as -o cpuid.o cpuid.s # 汇编源代码为目标文件
ld -o cpuid cpuid.o # 链接目标文件为linux可执行的文件
./cpuid # 执行

1.1 基本gas程序模板

gas程序分为几个部分,通常有data段和text段。data段存放数据,其 内容会被放在最终的可执行程序中。text段是汇编指令。通常还有 bss段,这里相当于缓存,临时创建。

.section .data
    ...
.section .text
    ...

1.2 gas汇编程序伪指令

一般汇编程序本身会提供一些方便的"指令",但是这些"指令"不是 cpu指令,它们只是方便汇编程序开发者,解释权归汇编程序本身。

gas中的“伪指令”都是以 "." 开头,如上面示例中的一些伪指令:

.section     定义一个段
.data        同.section一起定义数据段
.ascii       定义一个字符串
.text        同.section一起定义程序段
.global      定义全局标签

1.3 gas的标签

在gas中的标签等价于一个内存地址,相当于C语言中的指针。不过C语 言的指针还有数据类型属性,这里的数据类型需要自己指定了。

gas中的标签是以冒号结尾的字符串。如示例中的:

output1:
output2:

既然gas中的标签是内存地址,这是一个数值。那么,我们可以在gas 程序中对标签进行算术运算,且对于“立即数”能出现的地方,标签也 都可以出现:

movl $(output2-output1+14),%edx

1.4 gas的开始标签

如果用 ld 手动链接程序,则需要在汇编程序中设置一个为 _start: 的开始标签,相当于C程序中的main函数。如果用gcc编译汇编程序, 那么要将 "_start:" 换成 "main:"

如果没有指定 "_start" 标签,可以在运新 ld 命令的时候指定开始标签:

ld -e

1.5 gas中的系统调用

Linux下的汇编程序可以使用系统调用,这样可以避免很多复杂的工作。 在gas里使用系统调用就是在相关寄存器存入系统调用号、调用函数的 参数,再执行 "int $0x80" 命令就可以了。

上例使用了两个系统调用函数: write 和 exit

1.6 基本 AT&T 汇编风格

  • 立即数 表示立即数要用"$"符号
  • 寄存器 表示寄存器要用"%"符号
  • 内存寻址 内存地址要放在寄存器里,才能被寻址,这是intel的cpu指令集规定的。
movl %ebx,(%edi)  # 将ebx里面的内容放到edi指向的内存地址单元处。
movl %edx,4(%edi) # 将edx里面的内容放到比edi指向的内存地址高4个值的内存单元处。

2. 使用标准C库函数

使用标准C库函数的 cpuid2.s 程序:

.section .data
output:
        .asciz "当前CPU厂商信息是:%s \n"
.section .bss
        .lcomm buffer,12

.section .text
.global _start
_start:
        movl $0,%eax
        cpuid

        movl $buffer,%edi
        movl %ebx,(%edi)
        movl %edx,4(%edi)
        movl %ecx,8(%edi)

        pushl $buffer
        pushl $output
        call printf

        addl $8,%esp
        pushl $0
        call exit

汇编、链接并运行:

as -o cpuid2.o cpuid2.s
ld -dynamic-linker /lib/ld-linux.so.2 -o cpuid2 -lc cpuid2.o

参数说明:

-lc 链接C库/lib/libc.so;如果是-lx,默认链接/lib/libx.so。
-dynamic-linker /lib/ld-linux.so.2 使用/lib/ld-linux.so.2加载共享库。

3. 定义数据元素

3.1 定义数据元素的命令

.ascii     文本字符串
.asciz     带零结束符的文本字符串
.byte      字节值
.double    双精度值
.float     单精度值
.int       32位整数
.long      同.int
.octa      16字节长度整数
.quad      8字节长整数
.short     16位整数
.single    同.float

有几种数据段:

.section .data    定义通常的数据段,数据被包含在最终程序中
.section .rodata  同.data,但是这里定义的数据的值不可修改
.section .bss     相当于缓冲

数据定义可以一个标签一个命令一个数据的定义:

output:
    .asciz "这是一个.asciz定义的字符串"
value:
    .int  100

也可以,一个标签一个命令定义一堆数据:

values:
    .int 15,20,25,30,35,40,45,50,55,60

无论是哪种形式定义的,数据在内存中都是一个挨着一个的存放的。 这样我们可以用索引来引用它们。

3.2 赋值命令

gas中也可以用一个符号代表一个值,但是符号只不可修改:

.equ factor,3
.equ LINUX_SYS_CALL,0x80

3.3 bss段

这个段无须声明特定的类型,只要声明大小:

.comm  声明未初始化数据的通用内存区域
.lcomm 声明未初始化数据的"本地"通用内存区域

例如,声明一个1000字节的缓冲区,通过buffer引用这个区域的基址:

.section .bss
  .lcomm buffer,1000

3.4 .fill 命令

这个命令让汇编器自动创建一段内存区域,并用0填充:

.section .data
buffer:
    .fill 1000

4. MOV命令

gas中把mov命令加了不同后缀,每个后缀表示操作不同的数据大小:

movl  32位
movw  16
movb  8

下面一个例子把一个值移到ecx寄存器,然后调用linux系统的exit正 常退出。用gdb可以看见寄存器的变化。

# 程序 movetest1.s
.section .data
value:
        .int 1
.section .text
.global _start
_start:
        nop
        movl value,%ecx
        movl $1,%eax
        movl $0,%ebx
        int $0x80

用-gtabs参数汇编程序并链接:

as -gtabs -o movetest1.o movetest1.s
ld -o movetest1 movetest1.o

使用gdb调试程序:

root@jianlee:~/lab/asm# gdb -q movetest1
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file movetest1.s, line 8.
(gdb) run
Starting program: /root/lab/asm/movetest1

Breakpoint 1, _start () at movetest1.s:8
8	        movl value,%ecx
Current language:  auto; currently asm
(gdb) print/x %ecx
A syntax error in expression, near `%ecx'.
(gdb) print/x $ecx
$1 = 0x0
(gdb) next
_start () at movetest1.s:9
9	        movl $1,%eax
(gdb) print/x $ecx
$2 = 0x1
(gdb) s
_start () at movetest1.s:10
10	        movl $0,%ebx
(gdb) cont
Continuing.

Program exited normally.
(gdb)

把寄存器的值传到内存里:

# movetest2.s 把寄存器的值传到内存中
.section .data
value:
        .int 1
.section .text
.global _start
_start:
        nop
        movl $100,%eax
        movl %eax,value

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

调试程序,查看程序执行过程:

root@jianlee:~/lab/asm# as -gtabs -o movetest2.o movetest2.s
movetest2.s: Assembler messages:
movetest2.s:0: Warning: end of file not at end of a line; newline inserted
root@jianlee:~/lab/asm# ld -o movetest2 movetest2.o
root@jianlee:~/lab/asm# gdb -q movetest2
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file movetest2.s, line 9.
(gdb) run
Starting program: /root/lab/asm/movetest2

Breakpoint 1, _start () at movetest2.s:9
9	        movl $100,%eax
Current language:  auto; currently asm
(gdb) print/x $eax
$1 = 0x0
(gdb) x/d &value
0x804908c <value>:	1
(gdb) s
_start () at movetest2.s:10
10	        movl %eax,value
(gdb) print/x $eax
$2 = 0x64
(gdb) x/d &value
0x804908c <value>:	1
(gdb) s
_start () at movetest2.s:12
12	        movl $1,%eax
(gdb) x/d &value
0x804908c <value>:	100
(gdb) cont
Continuing.

Program exited normally.
(gdb)

original link:http://jianlee.ylinux.org/Computer/%E6%B1%89%E5%AD%97%E7%B3%BB%E7%BB%9F/gas%E5%AD%A6%E4%B9%A0.html

write

eax 调用号4
ebx 将要写入的文件的文件描述符
ecx 需要写的字符串的内存起始地址
edx 需要写的字符串的长度
movl $4,%eax
movl $1,%ebx
movl $output,%ecx
movl $14,%edx
int $0x80

这个例子是将内存地址为output长度为14字节的字符串写到标准输出 (linux下标准输出的文件描述符是1)。

exit

eax 1
ebx 退出状态数字
movl $1,%eax
movl $0,%ebx
int 0x80

这个例子是返回退出状态号0。通常这代表程序正常结束。

MBR原理

结构

偏移值    内容
0000      MBR程序代码
01BE      分区表(4个分区,每个16字节)
01FE      结束标志(aa55)

分区表结构

每个分区表都有16字节的大小。它的结构如下:

单位:字节
1     如果是引导分区,值为80H;如果不是,值为00H
2-4   该分区的起始扇区号
5     标志字节
      05  扩展分区
      82  Linux交换分区
      83  ext3分区
      0c  fat32分区
6-8   该分区的终止扇区号
9-12  该分区已经使用的扇区数
13-16 该分区总共占用的扇区数

Linux下备份修复mbr

备份

dd if=/dev/sda of=mbr.img bs=512 count=1

修复

dd if=mbr.img of=/dev/sda bs=512 count=1

修复分区表

dd if=mbr.img of=/dev/sda bs=512 skip=446 count=66

抱歉!评论已关闭.