الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

إستدعاء دوال libc من برنامج أسمبلي في لينكس

بدأه Mr.B في 24 نوفمبر 2012 · 1 رد · 1,055 مشاهدة · في لغة Assembly لأنظمة 16, 32, 64 بت
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله وبركاته

إذا أردت كتابة برنامج أسمبلي للينكس لطباعة رسالة مثلاً فربما تلجأ لإستدعاءات النظام system calls. مثال لبرنامج يطبع رسالة:

.data
    message:    .string "Hello world!\n"

.text
    .globl _start

_start:
    movl    $4, %eax            /* رقم مقاطعة الكتابة */
    movl    $1, %ebx            /* stdoutالكتابة لـ */
    movl    $message, %ecx      /* مؤشر للنص */
    movl    $13, %edx           /* طول النص */
    int     $0x80               /* إستدعاء النواة */
    movl    $1, %eax            /* رقم مقاطعة الخروج */
    movl    $0, %ebx            /* رقم خروج البرنامج */
    int     $0x80               /* إستدعاء النواة */

تشغيله:

$ as asmexample.asm -o asmexample.o   # قمنا بتجميع البرنامج لينتج ملف أوبجكت
$ ld asmexample.o -o asmexample       # قمنا بربط ملف الأوبجكت
$ ./asmexample                        # شغلنا البرنامج
Hello world!                          # طُبعت رسالتنا
$ echo $?                             # يفترض أن يكون رقم الخروج 0
0
$

جميل. لكن ماذا لو أردنا إستدعاء دالة من مكتبة خارجية بدلاً من إستخدام دوال النظام؟. مثلاً نريد إستدعاء الدالة printf من مكتبة libc (مكتبة الـC القياسية)؟ الموضوع لم يكن بديهي واجهت بعض الصعوبات عند محاولتي عمل هذا.

لننظر للبرنامج هذا:

.section .data
    message:    .string     "Hello world!"
    format:     .string     "%s %d\n"

.section .text
    .globl _start

_start:
    /* printf(format, message, 123); */

    movl    $123, %ebx          /* نقل الرقم 123 للمسجل */
    pushl   %ebx                /* دفع المتغير الثالث للدالة */
    pushl   $message            /* دفع المتغير الثاني للدالة */
    pushl   $format             /* دفع المتغير الأول للدالة */
    call    printf              /* إستدعاء الدالة */

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

برنامج يتدعي الدالة printf لطباعة رسالة. لنقم بتصريفه:

$ as asmexample.asm -o asmexample.o
$ ld asmexample.o -o asmexample
asmexample.o: In function `_start':
(.text+0x11): undefined reference to `printf'
$

الربط لم ينجح ويخبرك بأنه لم يجد printf. طبعاً هذا لأن printf موجودة في مكتبة libc لذا فنحتاج للربط معها. يمكن عمل هذا بتمرير الخيار lc- للـlinker:

$ as asmexample.asm -o asmexample.o
$ ld asmexample.o -o asmexample -lc
$ ./asmexample 
bash: ./asmexample: No such file or directory
$

التصريف نجح إلا أن هناك خطأ عن شيء مفقود. ولم أعرف مالذي ينقص. لنأخذ برنامج الـC هذا ونصرفه لمعرفة السبب:

$ cat cexample.c 
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("Hello world!\n");

    return 0;
}
$ gcc cexample.c -o cexample
$ ./cexample 
Hello world!
$

الآن "الخطوة الأهم" وهي معرفة مكتبات الـshared object التي يرتبط بها البرنامج بإستخدام أداة ldd. الـshared object في يونكس تكافئ مكتبات الـdll في ويندوز. وأداة ldd تشبه أداة dependency walker في ويندوز:

$ ldd cexample
	linux-gate.so.1 =>  (0xb788c000)
	libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb773a000)
	/lib/ld-linux.so.2 (0xb788d000)
$

قارن الناتج مع برنامجنا الأول:

$ ldd asmexample
	linux-gate.so.1 =>  (0xb788e000)
	libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb773c000)
	/usr/lib/libc.so.1 => /lib/ld-linux.so.2 (0xb788f000)
$

يلزمنا أن نربط برنامجنا مع المكتبة ld-linux.so.2 بعينها. لتحديد المكتبة التي تريد الربط معها فلن تستخدم الخيار l- بل الخيار I- (أو )متبوع بمسار المكتبة الكامل ستجده في دليل ld بكتابة man ld:

$ man ld
...
       -Ifile
       --dynamic-linker=file
           Set the name of the dynamic linker.  This is only meaningful when generating dynamically linked ELF executables.  The default dynamic linker
           is normally correct; don't use this unless you know what you are doing.

...
$

لنجرب:

$ as asmexample.asm -o asmexample.o
$ ld asmexample.o -o asmexample -lc -I /lib/ld-linux.so.2 
$ ./asmexample 
Hello world! 123
$

جميل. للنظر للمكتبات:

$ ldd asmexample
	linux-gate.so.1 =>  (0xb77a4000)
	libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7652000)
	/lib/ld-linux.so.2 (0xb77a5000)
$ ldd cexample
	linux-gate.so.1 =>  (0xb77e6000)
	libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7694000)
	/lib/ld-linux.so.2 (0xb77e7000)
$

كلا البرنامجين متطابقان الآن.

بعض تجاربي دوال C:

.section .data
    message:    .string     "Hello world!"
    format:     .string     "%c\n"

.section .text
    .globl _start

_start:
    /**
        edi = 0;
        ebx = 0;
    loop:
        ebx = *(message+edi);
        printf(format, edx);
        sleep(1);
        edi++;
        if( *(message+edi) != 0 )
            goto loop
        syscall(1, 0);
    **/

    xorl    %edi, %edi
    xorl    %ebx, %ebx
loop:
    movb    message( , %edi, 1), %bl
    pushl   %ebx
    pushl   $format
    call    printf

    pushl   $1
    call    sleep

    inc     %edi
    cmpb    $0, message( , %edi, 1)
    jne     loop

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

.section .data
    filename:   .string "file.txt"
    filemode:   .string "w"
    message :   .string "Hello world\n"
    errmsg  :   .string "Error"

.section .text
    .globl _start

_start:
    /**
        eax = fopen(filename, filemode);
        if( eax == 0 )
            goto error
        edi = eax;
        fprintf(edi, message);
        fclose(edi);
        syscall(1, 0);

    error:
        perror(errmsg);
        syscall(1, -1);
    **/

    pushl   $filemode
    pushl   $filename
    call    fopen
    cmpl    $0, %eax
    je      error

    movl    %eax, %edi
    pushl   $message
    pushl   %edi
    call    fprintf

    pushl   %edi
    call    fclose

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

error:
    pushl   $errmsg
    call    perror
    movl    $1, %eax
    movl    $-1, %ebx
    int     $0x80

بالتوفيق

3
#2

مميز كالعادة B

:)


﴿وَمَا كَانَ اللَّهُ لِيُعَذِّبَهُمْ وَأَنْتَ فِيهِمْ ۚ وَمَا كَانَ اللَّهُ مُعَذِّبَهُمْ وَهُمْ يَسْتَغْفِرُونَ﴾

الأنفال ۳۳                                  
 

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…