使⽤汇编语⾔实现memcpy
把内核放⼊内存,究竟需做什么
写满实现内核功能的代码的⽂件会被编译成⼀个ELF⽂件。这个ELF⽂件不同于LOADER BIN⽂件。后者实质是⼀个没有使⽤DOS命令的COM⽂件。因此,只需将它原封不动地从存储设备读⼊到内存中,然后跳转到这个内存区域的开始,就将CPU的控制权交给了LOADER。ELF⽂件是当前Linux系统上的可执⾏⽂件格式。写⼀个C程序,然后编译成可执⾏⽂件,使⽤ file 查看这个⽂件,能看到这个⽂件是ELF⽂件。
ELF⽂件由program header table、elf header、section header table、section组成。只有elf header的位置是固定的,在elf⽂件的开始位置。其他⼏个成员的位置不固定。
将内核指令重新放置到内存中,需做两件事情:⼀、把内核⽂件读⼊内存中。⼆、把内存中的内核的程序段全部复制到规划好的内存位置。
第⼀件事情,熟悉FAT12⽂件系统,就能做到。我已经独⽴写代码完成了这个功能并通过了测试。在本⽂不想再赘⾔。
第⼆件事情,要想完成它,需了解elf结构,需知道如何把数据从内存A位置复制到内存B位置,也就是说,
需要实现⼀个函数,memcpy(int dest, int off, int size)。三个参数分别是:在内存中的虚拟地址、程序段在⽂件中的偏移量、程序段的长度。三个参数都能从ELF⽂件的elf头和程序头中获取。
参照位置是elf⽂件的开头。偏移量28个字节的内存位置,给它起个标记叫P,从P开始的若⼲个字节(忘记了具体数字)的内存存储的是程序段的偏移量。⽂件开头加上这个偏移量,是第⼀个程序头的内存初始位置。
程序头也存储在⼀⽚内存中。⽤C语⾔中的struct帮助描述。程序头是⼀个struct结构,成员变量有程序段的长度、程序段的偏移量、程序段在内存中的虚拟位置(也就是这个程序段将要被重新放置在内存中的位置)。这三个成员变量,就是函数memcpy需要的三个参数。
memcpy的实现
memcpy,有三个参数,分别是:数据要被复制到的内存地址dst,数据的原始地址src,数据的长度size。这个函数的功能是,把src处的size个字节的数据复制到dst处,返回值是src。
直接上代码。
mempcy:
push ebp
mov ebp, esp
push esi
push edi
push ecx
mov esi, [ebp+12]  ; src
mov edi, [ebp+8]  ; dst
mov ecx, [ebp+16]  ; size
.1
cmp ecx, 0
jz .2
mov al, [ds:esi]
mov [es:edi], al
inc esi
inc edi
dec ecx
jmp .1
.2
mov eax, [ebp+8]
pop ecx
pop edi
pop esi
pop ebp
ret
详细解读这个函数的实现。
在汇编中实现⼀个函数,模板是:
functionName:
; some code
; some code
ret
汇编函数必须⽤ret结尾。它的作⽤是在函数执⾏结束后,返回调⽤函数的上层代码的下⼀条指令。
调⽤函数时,使⽤栈传递参数给函数。在函数内部,获取参数时,再从栈中获取参数。
mov esi, [ebp+12]  ; src
mov edi, [ebp+8]  ; dst
mov ecx, [ebp+16]  ; size
ebp指向栈的开始位置栈顶,偏离栈顶4个字节的位置存储的是调⽤函数指令的下⼀条指令的位置(⼤概就是这个意思),它是执⾏ call 指令时⼊栈的,是函数调⽤过程中最后⼀个⼊栈的数据。在它之前依次是函数的第⼀个、第⼆个、第三个参数⼊栈(在本函数中),相对于栈顶的偏移量依次是8个字节、12个字节、16个字节。
由于上⾯的代码修改了esi、edi、ecx中的值,需要在修改之前将它们保存起来,在函数结束时再恢复它们原来的值,所以有下⾯的代码:push esi
push edi
push ecx
; some code
; some code
pop ecx
pop edi
pop esi
内存的最⼩单位是字节,本函数也按字节来复制数据,这不是必须的。复制数据使⽤
mov al, [ds:esi]
mov [es:edi], al
eax存储2个字,4个字节;ax存储1个字,2个字节;al存储1个字节。ds是数据段,es是什么?有多少个字节,就需要重复多少次上⾯的复制操作。因此,需要⼀个循环。
.1
cmp ecx, 0
jz .2
mov al, [ds:esi]
mov [es:edi], al
inc esi
inc edi汇编table指令什么意思
dec ecx
jmp .1
在汇编中,loop指令能实现循环功能,本函数却并未使⽤。这是为了规避恐怖的ecx陷阱。使⽤loop指令时,需ecx配合。在循环过程
中,ecx的值会⾃动减少。当ecx的值是0时,循环结束。陷阱就出现在这⾥。具体是咋回事,我忘记了。但我遇到过,再加上在函数体中,可能会修改ecx。为了避免种种诡异的问题,本函数⼀般使⽤jmp指令,再配合⼿⼯递减的ecx来实现循环功能。作者的其他汇编代码都会如此,尽量不使⽤loop指令。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。