Khaled Alshaya

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

14 ردود في هذا الموضوع

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

في سالف العصر و الزمان, كان حجم 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

تم تعديل بواسطه Khaled.Alshaya
9

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ألف وستمائة وثمانية وثمانون بايتا = 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
2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

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

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

window.zip

تم تعديل بواسطه Khaled.Alshaya
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

تم تعديل بواسطه Abdullah.Alshammeri
تهذيب المشاركة
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

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

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

bash: ./helloWorld: cannot execute binary file
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

SmallApp.jar

تم تعديل بواسطه فيصل الردادي
2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
هذا اصغر منه، ويقوم بنفس العمل

هل أنت متأكد؟

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

@خالد @فيصل

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

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

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

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

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

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
يعنى لو إفترضنا أنك تقوم بتنفيذ هذا الملف الثنائى على أله ليس عليها هذا ال runtime

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

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

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

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .