作者共發了18篇帖子。 內容轉換:不轉換▼
 
點擊 回復
756 17
【方法】在Linux C程序中嵌入汇编语言
一派護法 十九級
1樓 發表于:2016-2-26 12:49
首先,编写一个简单的C程序:
#include <stdio.h>

void fun(int *num, int *num2);

int main()
{
    int a = 10, b = 32;
    fun(&a, &b);
    printf("a=%d, b=%d\n", a, b);
    return 0;
}
其中主函数调用了fun函数。
fun函数我们打算用汇编语言来写,其功能是把*num的值加15, *num2的值加5。
一派護法 十九級
3樓 發表于:2016-2-26 12:51
GLOBAL fun

fun:
    MOV ECX, [ESP+4] ; 把第一个参数的值送入ECX寄存器,其中存储的是变量a的地址
    ADD DWORD[ECX], 15 ; []表示取内存内容,DWORD就是类型int的大小,把ECX指向的内存区域的值加15
    
    MOV ECX, [ESP+8] ; 取第二个参数的值
    ADD DWORD[ECX], 5
    
    RET ; 返回
一派護法 十九級
4樓 發表于:2016-2-26 12:51
Makefile文件:
# 连接
test: test.o hello.o
    gcc -m32 test.o hello.o -o test

# 编译test.c,生成32位的目标文件
test.o: test.c
    gcc -m32 -c test.c

# 编译hello.asm,生成目标文件
hello.o: hello.asm
    nasm -f elf hello.asm
一派護法 十九級
5樓 發表于:2016-2-26 12:51
编译,运行程序:
octopus@pc3:~/Documents/Codes/C/link2$ make
gcc -m32 -c test.c
nasm -f elf hello.asm
gcc -m32 test.o hello.o -o test
octopus@pc3:~/Documents/Codes/C/link2$ ./test
a=25, b=37
octopus@pc3:~/Documents/Codes/C/link2$

一派護法 十九級
6樓 發表于:2016-2-26 12:58
为了能在64位系统下编译32位的程序,应该为gcc安装:
sudo apt-get install libc6-dev-i386
一派護法 十九級
7樓 發表于:2016-2-26 13:24
如果想要fun函数返回值,可以将其放入EAX寄存器里面:
MOV EAX, 12345678 ; 返回值
RET ; 返回

C程序部分:
#include <stdio.h>
int fun(int *num, int *num2);
int main()
{
    int a = 10, b = 32;
    int c = fun(&a, &b);
    printf("a=%d, b=%d\n", a, b);
    printf("c=%d\n", c);
    return 0;
}
运行结果:
$ ./test
a=25, b=37
c=12345678
一派護法 十九級
8樓 發表于:2016-2-26 13:31
返回64位值的方法:
GLOBAL fun
fun:
    MOV EDX, 0xabcdef01 ; 返回值的高32位
    MOV EAX, 0x12345678 ; 返回值的低32位
    RET

#include <stdio.h>
long long fun();
int main()
{
    long long v = fun();
    printf("返回值 = %lld\n", v);
    printf("高32位: %#08lx\n", (long)(v >> 32));
    printf("低32位: %#08lx\n", (long)(v & 0xffffffff));
    return 0;
}

运行结果:
返回值 = -6066930335118764424
高32位: 0xabcdef01
低32位: 0x12345678
一派護法 十九級
9樓 發表于:2016-2-26 13:37
返回字符串指针的方法:
GLOBAL fun
fun:
    MOV EAX, msg ; 把数据段的地址作为返回值返回
    RET

; 数据段
msg:
    DB "Hello, World!"
    DB 0


#include <stdio.h>
char *fun();
int main()
{
    char *str = fun();
    puts(str);
    return 0;
}

程序输出:
Hello, World!
一派護法 十九級
10樓 發表于:2016-2-26 13:53
其实,在C语言中可以直接通过引用外部变量的方法引用数据段msg:
【汇编部分】
GLOBAL msg
msg:
    DB "Hello, World!"
    DB 0
【C语言部分】
#include <stdio.h>
extern char msg;
int main()
{
    // 变量msg只是第一个字符
    printf("第1个字符: %c\n", msg);
   
    // 要想输出其他字符,必须先得到msg的地址,然后对这个地址进行加法运算
    printf("第2个字符: %c\n", *(&msg + 1));
    printf("第3个字符: %c\n", *(&msg + 2));
   
    // 可以用下面的方法输出整个字符串
    char *pMsg = &msg;
    puts(pMsg);
   
    return 0;
}

【输出】
$ ./test
第1个字符: H
第2个字符: e
第3个字符: l
Hello, World!
一派護法 十九級
11樓 發表于:2016-2-26 14:21
如果把msg变量声明为数组的话,也能直接输出字符串:
#include <stdio.h>
#include <string.h>
extern char msg[];
int main()
{
    puts(msg);
    printf("length: %d\n", strlen(msg));
    return 0;
}
运行结果:
Hello, World!
length: 13

但是,不能声明成字符串指针:
extern char *msg;
否则会出现段错误。
一派護法 十九級
12樓 發表于:2016-2-26 14:43
汇编语言中的除法指令:
【汇编部分】
GLOBAL div
div:
    MOV AX, [ESP+4]
    DIV BYTE[ESP+8]
    RET
【C语言部分】
#include <stdio.h>
short div(unsigned char a, unsigned char b);
int main()
{
    short num = div(10, 3);
    printf("%d ... %d\n", num & 0xff, num >> 8);
    return 0;
}
运行结果:
3 ... 1
一派護法 十九級
13樓 發表于:2016-2-26 15:22
在汇编语言中调用C语言函数:
【汇编部分】
GLOBAL fun
fun:
    CALL [ESP+4]
    RET
【C语言部分】
#include <stdio.h>
void fun(void (*f)());
void test()
{
    printf("This is a string.\n");
}
int main()
{
    fun(test);
    return 0;
}
输出:
This is a string.
一派護法 十九級
14樓 發表于:2016-2-26 16:14
汇编语言创建C语言可写的数组的方法:
【汇编部分】
SECTION .bss
GLOBAL arr
arr:
    RESB 20
【C语言部分】
#include <stdio.h>
#include <string.h>
extern char arr[20];
int main()
{
    strcpy(arr, "This is a string.");
    puts(arr);
    return 0;
}

输出:
This is a string.
一派護法 十九級
15樓 發表于:2016-2-26 16:27
关于汇编语言中的三个段:
【汇编部分】
; BSS段中专门存放未初始化的变量
SECTION .bss
GLOBAL arr
arr:
    RESB 20

; 这个段中不可以定义函数
;fun3:
;    RET

; DATA段中专门存放已初始化的变量
; 其中的内容可读可写
SECTION .data
GLOBAL msg
msg:
    DB "Hello, World!"
    DB 0

GLOBAL fun2
fun2:
    RET

; TEXT段中的内容是只读的
SECTION .text
GLOBAL str
GLOBAL fun
str:
    DB "abcdef"
    DB 0
fun:
    MOV EAX, 14
    RET
【C语言部分】
#include <stdio.h>
#include <string.h>
extern char arr[20];
extern char msg[];
extern char str[];
int fun();
void fun2();
int main()
{
    strcpy(arr, "This is a string.");
    puts(arr);
   
    msg[0] = 'I';
    puts(msg);
    printf("%d\n", fun());
   
    //str[0] = 'm'; // 只读!将会引发段错误
    puts(str);
   
    fun2();   
    return 0;
}
【输出】
This is a string.
Iello, World!
14
abcdef
一派護法 十九級
16樓 發表于:2016-2-26 16:41
接下来我们来一点刺激的!
把一个C语言数组拿来执行!

【C语言部分】
#include <stdio.h>
#include <string.h>

extern char fun, end; // fun函数的开始和结束位置
char codes[200];

int main()
{
    int size = &end - &fun; // fun函数的机器代码大小
    printf("size=%d\n", size);
    memcpy(codes, &fun, size); // 把fun函数的机器代码复制到codes数组中
   
    // 执行codes数组中所存放的机器代码,并读取代码执行完毕后EAX寄存器中的值
    int (*fun)() = (int (*)())codes;
    int v = fun();
    printf("%d\n", v);
   
    return 0;
}
【汇编部分】
GLOBAL fun
GLOBAL end
fun:
    MOV EAX, 48
    RET
end:

【运行结果】
size=6
48
一派護法 十九級
17樓 發表于:2016-2-26 16:43
int (*fun)() = (int (*)())codes;
这句话的意思就是:先定义一个fun变量,变量的类型为函数指针。
然后把codes变量(本来是一个数组)强行转换成函数指针,赋给fun变量。
一派護法 十九級
18樓 發表于:2016-2-26 16:48
16楼所示的代码有点没写好,因为main函数中有两个fun,所以干脆改一个名字吧,更容易理解:
#include <stdio.h>
#include <string.h>

extern char fun, end; // fun函数的开始和结束位置
char codes[200];

int main()
{
    int (*fff)(); // 定义一个函数指针
    int v;
    int size = &end - &fun; // fun函数的机器代码大小
    printf("size=%d\n", size);
    memcpy(codes, &fun, size); // 把fun函数的机器代码复制到codes数组中
   
    // 执行codes数组中所存放的机器代码,并读取代码执行完毕后EAX寄存器中的值
    fff = (int (*)())codes;
    v = fff();
    printf("%d\n", v);
   
    return 0;
}
一派護法 十九級
19樓 發表于:2016-2-26 16:55
实际上,不需要定义函数指针,就能直接执行codes数组:
v = ((int (*)())codes)();
printf("%d\n", v);
只不过括号比较多而已。

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:756 回複數:17
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-2-26 16:55
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用共享創意姓名標示-相同方式分享 3.0 Unported許可協議進行許可。