ایجاد بایتکدهای ماشین با زبان اسمبلی برای Shell Code
برای ایجاد بایتکدهای ماشین، میتوانید از زبان اسمبلی استفاده کنید و سپس کد اسمبلی را به بایتکد ماشین تبدیل کنید. ابزارهایی مانند nasm (Netwide Assembler).....
آموزش ایجاد بایتکدهای ماشین با زبان اسمبلی برای Shell Code
برای ایجاد بایتکدهای ماشین، میتوانید از زبان اسمبلی استفاده کنید و سپس کد اسمبلی را به بایتکد ماشین تبدیل کنید. ابزارهایی مانند nasm
(Netwide Assembler) برای این کار بسیار مناسب هستند. در اینجا مثالی از کد اسمبلی که یک پیام باکس نمایش میدهد آورده شده است:
کد اسمبلی برای نمایش MessageBox
section .data
msg db 'Hello, World!', 0 ; Programmer: Ebrahim Shafiei (EbraSha)
section .text
global _start
_start:
; Write message to stdout
mov eax, 4 ; syscall number for sys_write
mov ebx, 1 ; file descriptor 1 is stdout
mov ecx, msg ; pointer to message
mov edx, 13 ; message length
int 0x80 ; call kernel
; Programmer: Ebrahim Shafiei (EbraSha)
; Exit program
mov eax, 1 ; syscall number for sys_exit
xor ebx, ebx ; exit code 0
int 0x80 ; call kernel
; Programmer: Ebrahim Shafiei (EbraSha)
تبدیل کد اسمبلی به بایتکد ماشین
برای تبدیل این کد اسمبلی به بایتکد ماشین، میتوانید از اسمبلر nasm
استفاده کنید:
nasm -f bin -o shellcode.bin messagebox.asm
پس از تولید فایل shellcode.bin
، میتوانید بایتکدهای ماشین را با استفاده از یک ابزار مانند xxd
به آرایه بایتها در C++ تبدیل کنید:
xxd -i shellcode.bin
استفاده از بایتکدهای ماشین در برنامه C++
آرایه بایتهای تولید شده را میتوانید مستقیماً در برنامه C++ خود قرار دهید:
unsigned char shellcode[] = {
// Bytecodes generated by xxd
};
این بایتکدها همان کدهای ماشین هستند که مستقیماً توسط پردازنده اجرا میشوند. با این روش، میتوانید هر کدی را که میخواهید در برنامه C++ خود اجرا کنید، به شرطی که بایتکدهای آن را داشته باشید.
تحلیل و توضیح کد اسمبلی برای نمایش MessageBox
این کد اسمبلی با استفاده از دستورالعملهای سطح پایین و API های ویندوز، یک پیام باکس با متن "Hello, World!" نمایش میدهد و سپس برنامه را خاتمه میدهد. کد شامل سه بخش اصلی است: .data
، .text
و .idata
.
بخش .data
section .data
msg db 'Hello, World!', 0
section .data
: این بخش دادههای ایستا (ثابت) برنامه را ذخیره میکند.msg db 'Hello, World!', 0
: این خط یک رشتهای از کاراکترها که متن "Hello, World!" را شامل میشود و با بایت صفر (null terminator) خاتمه مییابد، تعریف میکند.
بخش .text
section .text
global _start
_start:
; Load user32.dll
xor eax, eax
push eax
push 0x646c6c61 ; 'alla'
push 0x642e3233 ; 'd.32'
push esp
mov ebx, esp
call dword ptr [LoadLibraryA]
; Load MessageBoxA
xor eax, eax
push eax
push 0x41786541 ; 'Aexe'
push 0x42656761 ; 'Bega'
push 0x7373654d ; 'sseM'
push esp
mov ebx, esp
call dword ptr [GetProcAddress]
; Call MessageBoxA
xor ecx, ecx
push ecx
push ecx
push msg
push ecx
call eax
; Exit process
xor eax, eax
inc eax
push eax
call dword ptr [ExitProcess]
section .text
: این بخش کدهای اجرایی (دستورات) برنامه را شامل میشود.global _start
: این خط نشان میدهد که_start
نقطه ورودی برنامه است._start:
: نقطه ورودی برنامه.
بارگذاری user32.dll
xor eax, eax
push eax
push 0x646c6c61 ; 'alla'
push 0x642e3233 ; 'd.32'
push esp
mov ebx, esp
call dword ptr [LoadLibraryA]
xor eax, eax
: مقدار رجیسترeax
را صفر میکند.push eax
: مقدار صفر (نشاندهنده پایان رشته) را به پشته فشار میدهد.push 0x646c6c61
وpush 0x642e3233
: حروف ASCII برای "alla" و "d.32" را به صورت برعکس (به دلیل لیتل اندین) به پشته فشار میدهد، که در نهایتuser32.dll
میسازد.push esp
: آدرس استک پویینتر (ESP) که حاوی آدرس نام DLL است را به پشته فشار میدهد.mov ebx, esp
: مقدار رجیسترebx
را با آدرس نام DLL تنظیم میکند.call dword ptr [LoadLibraryA]
: تابعLoadLibraryA
را فراخوانی میکند تاuser32.dll
را بارگذاری کند.
بارگذاری MessageBoxA
xor eax, eax
push eax
push 0x41786541 ; 'Aexe'
push 0x42656761 ; 'Bega'
push 0x7373654d ; 'sseM'
push esp
mov ebx, esp
call dword ptr [GetProcAddress]
xor eax, eax
: مقدار رجیسترeax
را صفر میکند.push eax
: مقدار صفر (نشاندهنده پایان رشته) را به پشته فشار میدهد.push 0x41786541
،push 0x42656761
وpush 0x7373654d
: حروف ASCII برای "Aexe"، "Bega" و "sseM" را به صورت برعکس به پشته فشار میدهد که در نهایتMessageBoxA
را میسازد.push esp
: آدرس استک پویینتر (ESP) که حاوی آدرس نام تابع است را به پشته فشار میدهد.mov ebx, esp
: مقدار رجیسترebx
را با آدرس نام تابع تنظیم میکند.call dword ptr [GetProcAddress]
: تابعGetProcAddress
را فراخوانی میکند تا آدرس تابعMessageBoxA
را بگیرد.
فراخوانی MessageBoxA
xor ecx, ecx
push ecx
push ecx
push msg
push ecx
call eax
xor ecx, ecx
: مقدار رجیسترecx
را صفر میکند.push ecx
: مقدار صفر را به پشته فشار میدهد (به عنوان آخرین پارامترMessageBoxA
کهuType
است).push ecx
: دوباره مقدار صفر را به پشته فشار میدهد (به عنوان پارامترhWnd
).push msg
: آدرس پیغام "Hello, World!" را به پشته فشار میدهد (به عنوان پارامترlpText
).push ecx
: مقدار صفر را به پشته فشار میدهد (به عنوان پارامترlpCaption
).call eax
: فراخوانی تابعMessageBoxA
.
خاتمه برنامه
xor eax, eax
inc eax
push eax
call dword ptr [ExitProcess]
xor eax, eax
: مقدار رجیسترeax
را صفر میکند.inc eax
: مقدارeax
را به 1 افزایش میدهد.push eax
: مقدار 1 (کد خروج) را به پشته فشار میدهد.call dword ptr [ExitProcess]
: تابعExitProcess
را فراخوانی میکند تا برنامه را خاتمه دهد.
بخش .idata
section .idata
dd 0,0,0,RVA kernel32_str,RVA kernel32_table
dd 0,0,0,RVA user32_str,RVA user32_table
dd 0,0,0,0,0
kernel32_table:
ExitProcess dd RVA ExitProcess_str
LoadLibraryA dd RVA LoadLibraryA_str
GetProcAddress dd RVA GetProcAddress_str
dd 0
user32_table:
MessageBoxA dd RVA MessageBoxA_str
dd 0
kernel32_str db 'KERNEL32.DLL',0
user32_str db 'USER32.DLL',0
ExitProcess_str db 'ExitProcess',0
LoadLibraryA_str db 'LoadLibraryA',0
GetProcAddress_str db 'GetProcAddress',0
MessageBoxA_str db 'MessageBoxA',0
section .idata
: این بخش دادههای ایمپورت (وارداتی) برنامه را شامل میشود.- این بخش شامل جداول و رشتههایی است که نام توابع و DLLهای مورد استفاده را ذخیره میکنند.
با استفاده از این کد، یک برنامه اسمبلی ساده ایجاد میشود که با استفاده از APIهای ویندوز، یک پیام باکس نمایش میدهد و سپس برنامه را خاتمه میدهد. این کد نشاندهنده چگونگی بارگذاری DLLها، پیدا کردن توابع و استفاده از آنها در سطح پایین است.
دیدگاه های مربوط به این مقاله (برای ارسال دیدگاه در سایت حتما باید عضو باشید و پروفایل کاربری شما تکمیل شده باشد)