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

أصغر ملف تنفيذي, تجربة مثيرة!

بدأه Khaled.Alshaya في 3 نوفمبر 2011 · 13 رد · 2,701 مشاهدة · في الأخبار والنقاشات التقنية
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

في سالف العصر و الزمان, كان حجم Turbo Pascal المترجم و الـ IDE في نفس الوقت لا يتعدى 40 كيلوبايت! و للعجب فـ uTorrent لا يتعدى حجمه 650 كيلوبايت, و مع هذا فإنه يوفر وظائف لا تعد و لا تحصى في مجاله. كيف استطاع هؤلاء كتابة برامج بهذا الحجم الصغير؟ لو تكلمنا عن Windows حصرياً, فإن الفكرة كلها تكمن في معرفة على ماذا يعتمد ملفنا التنفيذي --> و بالطبع التخلص من تلك الاعتمادية. بالطبع, قليل من اللعب في الـ Linker لتخصيص الملف التنفيذي حتى آخر قطرة! سنأخذ عهداً على أنفسنا أن لا نفتح Hex editor و لا أن ننادي أي مبرمج x86. سنعمل فقط بلغة C و سنستخدم مترجم Microsoft و الأدوات الملحقة به!

بعد ليلة سهر مليئة بالتجارب, وصلت إلى التالي: برنامج يعرض نافذة فارغة بالشكل التالي:

post-89451-073152500 1320316220_thumb.pn

برنامجنا المكتوب بلغة C على الشكل التالي:

#include "smallexe.h"

LPCTSTR class_name = TEXT("smallexe");

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
    switch(msg){
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI win_main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Step 1: Registering the Window Class
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = class_name;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc)){
        MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error!"),
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        class_name,
        TEXT("Small EXE"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL){
        MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error!"),
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while(GetMessage(&Msg, NULL, 0, 0) > 0){
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

للوصول إلى أصغر حجم, كان لابد من التخلي عن المكتبات القياسية و بالطبع التي تعتمد على الـ C Runtime Library. لذلك, لا يمكننا افتراض أي شي, و كان لابد من كتابة الدالة الأولى في البرنامج يدوياً و التي ستنادي win_main. كل ما في الأمر أنني قمت بكتابة entry function في smallexe.h و هذه الدالة هي أول دالة سوف تنادى في برنامجنا!

#define UNICODE
#include <windows.h>

int WINAPI win_main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);

void custom_main(){
    HMODULE hInstance = GetModuleHandle(NULL);
    HINSTANCE hPrevInstance = NULL;
    LPTSTR lpCmdLine = GetCommandLine();
    STARTUPINFO startup_info;
    int nCmdShow;
    int return_value;    
    GetStartupInfo(&startup_info);
    nCmdShow = startup_info.wShowWindow;
    return_value = win_main(hInstance, NULL, lpCmdLine, nCmdShow);
    ExitProcess(return_value);
}

ماذا بعد؟ سنحتاج إلى ترجمة و ربط البرنامج بحيث لا نعتمد على أي مكتبة خارجية(بالطبع سنستدعي دوال Windows من خلال الربط الديناميكي!). قمت بكتابة batch لتبسيط المهمة!

@ECHO off

set compiler_options= /O2 /Ox /Os

cl /nologo /c %compiler_options% %*


set merged_sections= /MERGE:.rdata=.data /MERGE:.text=.data
set disabled_warnings= /ignore:4254 /ignore:4108
set exe_obj= *.obj
set basic_libs= kernel32.lib gdi32.lib user32.lib 
set additional_libs= advapi32.lib comdlg32.lib comctl32.lib shell32.lib

link /nologo /ENTRY:custom_main /NODEFAULTLIB /SUBSYSTEM:WINDOWS /ALIGN:4 ^
     %merged_sections% %disabled_warnings% %exe_obj% %basic_libs% %additional_libs%

@ECHO on

و النتيجة؟ ملف تنفيذي بحجم 1688 bytes, نعم كما ترى كيلو و نصف من البايتات الجميلة!

ستجد في المرفقات, المشروع كاملاً. و لتجربته لديك, لابد أن يتوفر Visual Studio, بحيث تقوم بفتح الـ Visual Studio Command Prompt:

Start > All Programs > Microsoft Visual Studio 2010 > Visual Studio Tools > Visual Studio Command Prompt

ثم قم بتمرير اسم الملف الذي تريد ترجمته للـ batch file, كما في مثالنا التالي:

c:\test>build window.c

سينتج الملف التنفيذي مباشرة!

لمن يحب المشاركة, اطرح طريقة لإنتاج ملفات تنفيذية صغيرة جداً بلغة البرمجة و البيئة التي تفضلها! بالتاكيد, سأكون أكثر من سعيد لو التفت الأسمبلاوية لهذا الموضوع و طرحوا ما لديهم, فلاقدرة لي على مجابهتهم :lol: و أرحب بأي استفسار حول أي نقطة في الطريقة.

تحياتي,

============

Minicrt

Small Programs

Tiny PE

Techniques for reducing Executable size

test.zip

المرفقات
smallexe.PNG

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 3 نوفمبر 2011 في 13:51

9
#2

ألف وستمائة وثمانية وثمانون بايتا = 1000 + 600 + 88 ، ضخم جدا! tongue_smile.gif

لكن تحت شروطك فعلا الحجم صغير ورائع، وعلى فكرة تصغير الحجم عامل مهم جدا خصوصا في مجال الأنظمة المضمنة Embedded Systems، علاوة على أنه "هدف" لمبرمجي الفيروسات وبرامج التجسس فغالبا ما تكون الشفرات الخبيثة و "الكارثية" هي بضعة بايتات لطيفة وناعمة!

مررت سابقا بمحاولة نجحت في كتابة ملف Hello World وبحجم 142 Bytes فقط ! لكن تحت بنية ملفات الـELF الخاصة بتوزيعات اللنكس.

راجع هذا الرابط : هنا.

بنية PE الخاصة بويندوز بها كثير من backwards compatibility issues وهذه تلعب دور في إضافة حجم إضافي لوظيفة برنامجنا الأصل.

مثلا في برنامجك الحالي توجد قرابة 38 bytes للعبارة العتيقة التي تقول This program can not be run in DOS mode وهي عبارة يفترض أن تظهر متى؟ إذا كان هناك شخص ما في هذا العالم، وفي وقت ما جاء يحاول تشغيل ملف ويندوز التنفيذي على نظام DOS! ياه! لحنان ميكروسوفت الفياض على أبنائها! كل هذه السنين ولا زالت لم تنس إبنها "البكر" DOS.

وكذلك الـ section alignment له متطلبات كثيرة تقضي بإضافة أحجام لبنية الملف في كثير من الأحيان نحن في غنى عنها؟

والحقيقة أنه لا توجد طريقة سهلة لإزالة أمور مثل الـ backwards compatibility دون كسر بنية PE وتنظيمها الإفتراضي.

وهذا أمر متعب جدا حتى بالنسبة للأسمبلاوية - كما نعتهم - . sleep.gif

تم تعديل هذه المشاركة بواسطة A.S Hack في 3 نوفمبر 2011 في 15:20

2

" إن الله كتب الإحسان على كل شيء"

::

الإرادة ... تحقق السيادة.

#3

استعطت الوصول إلى 648 bytes, في حالة التطبيق الذي لا يقوم بأي شيء مطلقاً! عدا عن كونه استلم القيادة من الـ loader في Windows!

اقتباس
لكن تحت شروطك فعلا الحجم صغير ورائع، وعلى فكرة تصغير الحجم عامل مهم جدا خصوصا في مجال الأنظمة المضمنة Embedded Systems، علاوة على أنه "هدف" لمبرمجي الفيروسات وبرامج التجسس فغالبا ما تكون الشفرات الخبيثة و "الكارثية" هي بضعة بايتات لطيفة وناعمة!

بصراحة نعم تحتاج إلى تصغير حجم الكود, و لكن ليس بأهمية ذاكرة البرنامج نفسه. عملت مرة على البطاقات الذكية, و كان هناك الكثير من الروم, بضعة مئات من البايتات في الـ RAM للخربشة, و القليل من EEPROM لكي تحفظ ما تبقى من عقل البرنامج فيها.

اقتباس
مررت سابقا بمحاولة نجحت في كتابة ملف Hello World وبحجم 142 Bytes فقط ! لكن تحت بنية ملفات الـELF الخاصة بتوزيعات اللنكس.

جميل سأحاول كتابة script لـ gcc :blush:

اطلع على الرابط الثالث في آخر الموضوع فهو يقوم بتعديل الملف يدوياً, مثل الموضوع الذي أشرت إليه و يصل إلى أحجام خرافية!

اقتباس
وكذلك الـ section alignment له متطلبات كثيرة تقضي بإضافة أحجام لبنية الملف في كثير من الأحيان نحن في غنى عنها؟

السكربت الذي طرحته يستخدم أصغر Alignment استعطت عمل linking بها.

#4

هل من الممكن ان يرفع احدكم الملف التفيذي مباشرة؟ لانه لا يوجد لدي C Compiler

#5

جميل خالد .. عمل متعب حقا ..

لكن من جانب اخر .. هل عملك او تجربتك تسير باتجاه واحد .. ؟ اقصد انك كلما خصصت الشروط وبنيت اشياء يدوية وتخلصت من الاشياء التي قد حددتها مسبقا لكي تحصل على ملف اصغر حجما دائما تخدم ؟

البرنامج السابق مجرد عرض نافذة اصبح 648 بايت .. طيب لو اردت ان تضيف دوال وعمليات عليه .. هل ستصبح المسألة طردية ؟ كلما اضفت كود ازداد الحجم؟ لانه لا اعتقد ان البرنامج سيبقى بحجم بسيط كهذا ..

جرب ان ان تضيف دوال معادلات رياضية وشاهد النتيجة ( بدون ادخال واخراج ) .. !!

تحياتي العطرة ,

يَارَبُ إِن ضَاقَت قُلُوُب الْنَّاسٍ عَنْ مّافِي .. مِنْ خَيْرٍٍ فَعَفْوكَ لَا يَضِيْقْ ..

#6

الملف التنفيذي في المرفقات,

أخ سنان, الفكرة كلها أنني أردت التجربة و معرفة فائدة الـ CRT. لا أنوي و أنا بكامل قواي العقلية أن أستمر لكتابة أي شيء بهذه الطريقة. على العموم, مبرمجي uTorrent كانو مجانين كفاية لكي يقوموا بكتابة تطبيق بالطريقة اليدوية!

اقتباس
جرب ان ان تضيف دوال معادلات رياضية وشاهد النتيجة ( بدون ادخال واخراج ) .. !!

مادام المترجم سيرى حسابات لا تستخدمها في برنامج فسيقوم بإزالتها في مرحلة الـ Optimization, ربما إن قمت بهذا الأمر تذكر أن تجعل المتغيرات volatile :)

window.zip

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 4 نوفمبر 2011 في 06:02

#7

بالنسبة للموضوع ، أول ما خطر ببالي هي تطبيقات حجمها 4 كيلوبايت ، و المصيبة أنها لا تعرض نافذة فارغة ، بل تعرض عروض ثلاثية البعد " مدهشة " ، لذلك يمكن نقول 1.5 نافذة خالد الفارغة و 2.5 عروض رسومية :-) ،

http://www.farbrausch.com/prod.py?which=184

قد يعترض مكافح الفيروسات عند تثبيت التطبيق ، لا تعيره اهتماماً ، و قد تحتاج لكرت شاشة جيد.

على الهامش : بينما أقرأ كلمات خالد ، ** أحياناً يحزنني عندما أرى موضوع بدون تفاعل حقيقي :-) ، حيث أن خالد يتكلم عن أمور كبيرة ولكن لا يجد حوله الكثير من الأعضاء يشاركونه نفس الاهتمام أو نفس " المستوى العلمي العالي " ، أنا من هؤلاء الأعضاء للأسف ، و مشاركتي مثال :-( .

تم تعديل هذه المشاركة بواسطة Abdullah.Alshammeri في 6 نوفمبر 2011 في 01:48 — السبب: تهذيب المشاركة

1

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

بالفعل تجربه رائعه ...

لقد قمت بتطبيق عمل Optimizations لل Linker لل GCC تحت لينوكس ...

و الحمد لله ... قمت بتقليل حجم الملف التنفيذي من 4000 بايت إلى 800 بايت ... :)

البرنامج هو Hello World :

int main(void)
{
	puts("Hello World!\n");
	return 0;
}

كل شئ يعمل تمام سوى أن الملف الناتج من كل هذه عمليات التخريب لا يريد أن يعمل :blush:

اقتباس
bash: ./helloWorld: cannot execute binary file
1
#9

هذا اصغر منه، ويقوم بنفس العمل :huh:

SmallApp.jar

تم تعديل هذه المشاركة بواسطة فيصل الردادي في 13 نوفمبر 2011 في 23:35

2

اسف سوف اقطعك من كتابة الكود، ادخل هنا لدقيقتين فقط

http://www.shbab1.com/2minutes.htm

#10
اقتباس
هذا اصغر منه، ويقوم بنفس العمل

هل أنت متأكد؟

أستطيع كتابة آلة افتراضية لويندوز تقوم بإنشاء نافذة لملفات ذات امتداد معين و تقريباً هذا يعني أن برنامجي عبارة عن صفر من البايتات, طبعاً سيملأ sector عند تخزينه و لكن لا علاقة لنا بالحجم في هذه الطبقة!

الطريقة الوحيدة لتصغير الحجم بعد هذه الطريقة, هي أن يكون أحد أفضل أصدقائك مبرمج x86, مع جعبة أدواته يقوم بتحليل الـ PE header و ربما الملف كاملاً, و رمي الحقول الغير مهمة حسب خبرته في ما يحتاجه Windows لإقلاع الملفات التنفيذة. هذه هي الطريقة الوحيدة :wink:

#11

@خالد @فيصل

أنا لا أفهم كتيرا في برمجة الويندوز ...

ولكن أليس البرنامج الذي قمت بكتابة يا أخ خالد في المشاركة الأولى يعتمد على some runtime environment ... و هى ال WIN32 أو أي كان إسمها؟

بالنسبه لحالة فيصل ... فأنا معك ... حجم برنامجه = حجم ال JRE + حجم ملف الجار ....

و في حالتك ... WIN 32 runtime + حجم ال PE

يعنى لو إفترضنا أنك تقوم بتنفيذ هذا الملف الثنائى على أله ليس عليها هذا ال runtime ... هل سيعمل ؟

1
#12
اقتباس
يعنى لو إفترضنا أنك تقوم بتنفيذ هذا الملف الثنائى على أله ليس عليها هذا ال runtime

عندها لن يكون نظام Windows هو الشيء الذي نعمل عليه, لأننا استخدمنا المكتبات الأساسية التي يقدمها. ربما حتى إن استخدمنا نسخ الـ ANSI القديمة من الدوال, فأتوقع أن يعمل البرنامج من Windows 95 إلى Windows 8 :wacko:

الفكرة باختصار, أنه حتى في الحالة الطبيعية لترجمة البرنامج في ++C, فإن هناك مكتبات دعم مثل C Run Time الملحقة بـ VC. و لكن في الطريقة المطروحة استغنينا حتى عن هذه المكتبة الصغيرة.

#13

ذكرتني بإيام الماضي, إيام السهر و التعب :D

[نقاش] أصغر ملف تنفيذي يمكن كتابته

أصغر برنامج على الإطلاق مكتوب بدلفي

موضوعين سابقين لدعم المعنوي.

1

رب اجعلني مقيم الصلاة ومن ذريتي ربنا وتقبل دعاء.

لا تنسى: "العقل مثل العضلة كلما استخدمته أكثر كلما ازدادت قوته"

#14

عاش من شافك أخي وائل :cool:

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