PowerShell أساسيات

إياد المقحم
13/01/2021


مُقدمة
ستتكلم هذه المدونة عن أساسيات لغة PowerShell. ستُعطي نظرة عامة عن اللغة، الغرض من استخداماتها، بالإضافة الى بعض الأوامر الأساسية (المعروفة بـ cmdlet، وتنطق"Commandlets") والمستخدمة بشكل متكرر بشرح مبسط. هذه المقالة ليست تغطية شاملة عن لغة PowerShell، ولكنها بداية جيدة للأشخاص المهتمين بتعلمها.

مَاهِي PowerShell؟
هي لغة برمجة نصية (Scripting Language) كائنية التوجة (Object-oriented) موجودة في أنظمة Windows بشكل افتراضي. الغرض منها هو أن تكون بديل لـ CMD.exe للتخاطب مع وإدارة أنظمة Windows من خلال موجه الأوامر (CLI). يمكن استخدامها بشكل تفاعلي من خلال الـ CLI عن طريق powershell.exe أو من خلال واجهة مستخدم رسومية (GUI) والتي تستخدم أوامر PowerShell في الخلفية. العديد من الأدوات المُستخدمة لإدارة أنظمة Windows هي فعليا تستخدم لغة PowerShell في الخلفية.


مَن هُم مُستخدمي اللغة؟
تُستخدم لغة PowerShell من ِقبل مُديري الأنظمة (System Admins) لإِدارة أنظمة Windows بالإضافة الى أنظمة Microsoft الأُخرى كـ Active Directory و MSSQL. تُستخدم اللغة أيضاً من ِقبل مُستجيبي الحوادث الرقمية والمُحققين الجنائيين (DFIR)، من ِقبل مُختبري الاختراق (PT)، وأيضا، من ِقبل المهاجمين. تُستخدم اللغة من ِقبل مُختبري الاختراق والمهاجمين في العادة في مرحلة مابعد الاختراق (Post-exploitation).

بشكل مُوجَز، يمكن لـ PowerShell القيام بجميع المهام التي يقوم بهاCMD.exe وأكثر.

الفرق بين Windows PowerShell و PowerShell Core
يوجد نوعين من الـ PowerShell:

Windows PowerShell
وهي مُخصصة لأنظمة Windows فقط. هذا النوع مغلق المصدر(Closed-source) وموجود في أنظمة Windows بشكل افتراضي. نُسخ Windows PowerShell هي 1, 2, 3, 4 والنسخة الأخيرة هي 5.

شكل Windows PowerShell النسخة 5 (powershell.exe) كالتالي:
PowerShell Core
هذا النوع يمكن استخدامه على أنظمة Windows، Linux، وأيضا macOS. هذا النوع مفتوح المصدر (Open-source) وغير موجود في أنظمة Windows بشكل افتراضي. نسخ PowerShell Core تبدأ من 6 وآخر نسخة الى اليوم هي نسخة 7.

شكل PowerShell Core النسخة 7 (pwsh.exe) كالتالي:
أساسيات PowerShell
بعد ماأصبح لدينا فكرة حول PowerShell واستخداماتها، لِنَتطرق الى اختلافها كونها كائنية التوجة (Object-oriented) واستكشاف بعض الأوامر (cmdlets) المهمة والي تُستخدم بشكل متكرر.

كائنية التوجه (Objects-oriented)
كما ذَكرنا، لغة PowerShell هي كائنية التوجه (Object-oriented). المُخرج من الأوامر هو عبارة عن Object واحد أو أكثر، ليس نَصاً مثل CMD.exe أو Bash—على الرُغم من أَن مُخرجات الأوامر على الشاشة هي على شكل نص (Text)، فعليا هي موجودة كـ Objects في ذاكرة الجهاز. كلObject يحتوي على خصائص (Properties) ووظائف (Methods)، ويُمكن تعريفها بشكل مبسط كالتالي:

الخاصية (Property): هي معلومة متغيرة عن الـObject. على سبيل المثال، الـ Object من نوع Process يحتوي على خصائص (Properties) مختلفة تقوم بتعريف كل Process، مثل: Process ID، Process Name، Memory Usage، الخ.

الوظيفة (Method): هو كُود معرف مسبقا يقوم ببعض الأعمال على الـ Object. على سبيل المثال، الـObject من نوع Process يحتوي على وظائف (Methods) مثل: إيقاف الـ Process، بدء الـ Process، الخ.

مثال:

الأمر Get-Process يعرض لنا الـ Processes التي تعمل على الجهاز. مخرج الأمر هو خصائص (Properties) مختلفة لكل Process، مثلProcess Name و Process ID. مخرج الأمر هو مجموعة من الـ Processes، كل Process تعرض خصائصها بشكل أفقي. الـ Object المُخرج من هذا الأمر هو من نوع"Process" (أو بالإسم الكامل"System.Diagnostics.Process")

كل سطر من مُخرج الأمر Get-Process يمثل Object واحد والمكون من مجموعة من الخصائص (Properties):
كل Process تحتوي على وظائف (Methods) والتي تقوم بأعمال على الـ Process، مثل بدئها (Start) أو ايقافها (Kill). في المثال التالي، استخدمنا الوظيفة ()kill والتي قامت بإيقاف الـ Calculator Process. عند القيام بالبحث عن الـ Calculator Process مرة أخرى، يظهر لنا خطأ (Error) بأنه لا يوجد Process باسم Calculator وذلك لأنه تم إيقافها بعد استخدام الوظيفة ()kill:
الآن بعد ما عرفنا طريقة عمل الـ Objects في PowerShell، لنتطرق الى أكثر الأوامر (cmdlets) استخداماً وأهمها.

Get-Alias (gal)
الـ Alias هو عبارة عن اسم مستعار، او اختصار للأمر الأصلي. يمكن استخدام الـ Alias بدل من الأمر الأصلي لاختصار كتابة الأمر بشكل أسرع وأسهل كونه أبسط من الأمر الأصلي. كتابة الأمر الأصلي أو الـ Alias يعطي نفس المخرج.

الأمر التالي يقوم بجلب الـ Alias (الأمر المختصر) للأمر الأصلي Get-Process:
Get-Alias -Definition Get-Process
نرى أن الأمر Get-Process له اختصارين: gps و ps. استخدام اختصار الأمر يعطي نفس مخرج الأمر الأصلي:
يمكن أيضا معرفة الأمر الأصلي للـ Alias عن طريق التالي:
Get-Alias -Name ps
Get-Help (help)

Get-Help هو من أهم الأوامر في PowerShell، ووظيفته المُساعدة في التعرف على الأوامر من خلال توفير قائمة تشرح تفاصيل الأمر المطلوب وطريقة بنائُه. الأمر التالي يقوم بعرض تعليمات لكيفية استخدام الأمر Get-Process:
Get-Help -Name Get-Process –Full
لمعرفة تفاصيل أكثر، يُمكن استخدام المفتاح (Switch) Online لفتح صفحة Microsoft في المتصفح والتي تشرح تفاصيل أكثر عن الأمر Get-Process بالإضافة الى أمثلة لطريقة بناء الأوامر:
Get-Help –Name Get-Process -Online
Get-Member (gm)
Get-Member هو الأمر الثاني المهم تذكرة بالإضافة الى Get-Help. الـ Alias لهذا الأمر هو gm. يستخدم هذا الأمر لمعرفة الخصائص (Properties) والوظائف (Method) التي يمكن استخدامها للـ Object المستخدم.

مثال، يمكن معرفة الخصائص (Properties) والوظائف (Method) للـ Object من نوع Process عن طريق استخدام الـ Pipe (|) بالأمر التالي:
Get-Process | Get-Member
Variables and Arrays
يمكن حفظ مُخرجات الأوامر في مُتغيرات (Variables) لاستخدامها لاحقاً. المُتغيرات تقوم بحفظ مُخرجات الأوامر على هيئة Object وليس نص (Text). المتغيرات في PowerShell تبدأ بعلامة ($) متبوعة باسم المُتغير على هذا الشكل var_name$. يمكن للمُتغيرات حفظ مُخرج Object واحد أو مجموعة (Array) من الـ Objects.

مثال، الأمر التالي يقوم بتعيين المتغير signle_var$ بـ Object من نوع String بالقيمة "single_val":
$single_var ="single_val"
الأمر التالي يقوم بتعيين المُتغير array$ بمجموعة من الـ Objects بأنواع مختلفة، String وInteger:

$​array= "val_1", "val_2",1234
القيمة الأولى داخل المجموعة (Array) يمكن الوصول لها من خلال الـ Index رقم 0. القيمة الأخيرة يمكن الوصول لها من خلال الـ Index رقم 1-, وهكذا:
Typecasting
عند إنشاء Object جديد، PowerShell يقوم بتعيين نوع الـ Object إذا لم نقم بتحديد نوع الـ Object المُراد بشكل صريح (Explicit). على سبيل المثال، عن تعيين متغير برقم، PowerShell يقوم بتعيين نوع الـ Object بحسب نوع القيمة المعطاة. المُتغير التالي يحمل Object من نوع Integer لأن القيمة المعطاة هي عبارة عن رقم:
$var1 = 123
$var1 | Get-Member
عند تعيين مُتغير من نوع نص (أي قيمة مُحاطة بعلامات الاقتباس)، نوع الـ Object سيُحدد على أنه نص (String):
$var2 = "text"
$var2 | Get-Member
إذا قُمنا بإحاطة رقم بعلامات اقتباس (" ")، PowerShell سيقوم ببناء Object من نوع String وليس Integer بسبب وجود علامات الاقتباس:
$var3 = "123"
$var3 | Get-Member
في بعض الحالات، نحتاج الى تحديد نوع المُتغير بشكل صريح (Explicit) لتجنب قيام PowerShell بتعيين نوع مختلف عن المطلوب. يمكننا أيضا تغيير نوع الـ Object بعد تعيينه الى مُتغير وذلك باستخدام ما يسمى بـ Typecasting.

المثال التالي يوضح إحدى الحالات التي تُعطي ناتج غير متوقع بسبب اختلاف نوع الـ Object. قمنا بتعريف مُتغيرين بقيم رقمية ولكن القيم مُحاطة بعلامات اقتباس (" ") مما أدى الى قيام PowerShell بتعيينها على انها نص (String). عند محاولة القيام بجمع المُتغيرين باستخدام علامة الـجمع + ، قام PowerShell بعملية Concatenation وذلك بدمج ِقيم المُتغيرين على أنهم نص ولم يقم بجمع الأرقام. لتصحيح المُخرج المُتوقع، يلزمنا القيام بتغيير نوع المتغير (Typecasting) الى Integer حتى يقوم PowerShell بعملية الجمع وذلك بإضافة [Int] قبل المُتغير الأول لتغيير نوعه الى Integer:
$num1 = "3"
$num2 = "4"
$num1 + $num2
[Int]$num1 + $num2
Sort-Object (sort)
يقوم هذا الأمر بترتيب مُخرجات الأمر السابق، بترتيب تصاعدي (Ascending) بشكل افتراضي:
$array = 4, 7, 2, 4, 9, 11, 4, 15, 3, 1, 0, 9, 13, 4, 2, 9, 4
$array | Sort-Object
يُمكن تغيير الترتيب الى شكل تنازلي (Descending) بإضافة الـمفتاح (Switch) Descending– الى نهاية الأمر:
$array | Sort-Object -Descending
يُمكن أيضا عدم عرض المُخرجات المُتكررة بإضافة الـمفتاح (Switch) Unique- الى نهاية الأمر:
$array | Sort-Object –Descending -Unique
Comparison
يُوجد العديد من عوامل المقارنة (Comparison Operators) في PowerShell. مقارنة Objects بِبَعضها تُعطي ناتج بأن المقارنة صحيحة (True) أو خاطئة (False). عمليات المُقارنة في PowerShell مُهمة في حالة أردنا القيام بعمليات محددة بناء على شروط مُعينة. بعض عوامل المُقارنة في PowerShell:

(يساوي) eq: Equals-
(أقل من) lt: Less than-
(أقل من أو يساوي) le: Less than or equals-

أمثلة:
5 –eq 5
5 –lt 10
5 –lt 5
5 –le 5
like-: يقوم باستخدام علامة (*) لمُقارنة اذا كان الطرف الأول يحتوي على الطرف الثاني:

أمثلة:

هل الكلمة الأولى تنتهي بـ"sync"؟
"Cyber Sync" –like "*sync"
هل الكلمة الأولى تبدأ بـ"cyber"؟
"Cyber Sync" –like "cyber*"
هل الكلمة الأولى تحتوي على "ber"في أي مكان داخلها؟
"Cyber Sync" –like "*ber*"
لمعرفة المزيد عن عوامل المقارنة (Comparison Operators)

Select-Object (select)
Select-Object يقوم باختيار خصائص (Properties) محددة من الـ Objects—مشابه بـ SELECT في لغة التخاطب مع قواعد البيانات (SQL).

عند تشغيل الأمر Get-Process، يظهر لنا خصائص مُتعددة من الـ Processes:
يمكننا اختيار خصائص محددة فقط، مثل ProcessName و ID، بدل من عرض جميع الخصائص من خلال الأمر التالي:
Get-Process | Select-Object ProcessName, Id
Formatters
يُمكن تغيير طريقة عرض مُخرج الأمر عن طريق استخدام المُنسقَات (Formatters): جدول (Table)، قائمة (List)، واجهة رسوم (GUI)، الخ. التنسيق على شكل جدول (Table) Format-Table هو التنسيق الافتراضي في PowerShell إذا لم نقم باختيار تنسيق معين بشكل صريح (Explicit).

Format-Table هو المُستخدم بشكل افتراضي اذا لم نقم باختيار مُنسق (Formatter)
بشكل صريح (Explicit):
Get-Process
استخدام Format-Table بشكل صريح (Explicit) يُعطي نفس الناتج بما أنه هو الافتراضي:
Get-Process | Format-Table
تنسيق مخرجات الأمر على شكلك قائمة (List):
Get-Process | Format-List
لمعرفة المزيد عن المُنسقَات (Formatters)

Outputters
يمكن التحكم بمكان اخراج نتائج الأوامر من خلال المُخرِجَات (Outputters). Out-Host هو المُخرِج الافتراضي اذا لم نقم باختيار مكان لإخراج ناتج الأمر بشكل صريح (Explicit). Host تعني أن مُخرجات الأمر ستُعرض على الـ Process التي قامت بتشغيل الأمر—وهو نافذة PowerShell نفسها.

Out-Host هو المُستخدم بشكل افتراضي اذا لم نقم باختيار مُخرِج (Outputter) بشكل صريح (Explicit):
Get-Process
استخدام Out-Host بشكل صريح (Explicit) يُعطي نفس الناتج بما أنه هو المُخرِج الافتراضي:
Get-Process | Out-Host
يُمكننا القيام بحفظ مُخرج الأمر الى ملف نص (Text File) باستخدام المُخرِج Out-File ومن ثم قراءة محتوى الملف باستخدام الأمر Get-Content:
Get-Process | Out-File process_list.txt
Get-Content .\process_list.txt
يُمكن أيضا اخراج ناتج الأمر الى نافذة رسومية (GUI) والقيام بالبحث والفلترة لمُخرَج الأمر بطريقة سهلة الاستخدام عن طريق المُخرِج Out-GridView:
Get-Process | Out-GridView
(?) Where-Object
يُستخدم هذا الأمر لاختيار الـ Objects التي تُطابق شُروط معينة وذلك باستخدام عوامل المقارنة (Comparison Operators). الـ Alias لهذا الأمر هو عبارة عن علامة استفهام ?. كل Object مُطابق للشروط يتم تعيينه في المُتغير مؤقت (Temporary Variable) _$ ويمكن الوصول الى خصائص الـ Object عن طريق إضافة نقطة (.) الى هذا المُتغير متبوعة باسم الخاصية (Property) المراد الوصول لها. عملية المُقارنة يجب ان تكون محاطة بعلامات الأقواس {}.

مثال، الأمر التالي يقوم بعرض الـ Processes التي تملك اسم (Name) يساوي لـ"Calculator":
Get-Process | Where-Object {$_.Name –eq "Calculator"}
(%) ForEach-Object
هذا الأمر يقوم بتطبيق عملية مُعينة على جميع الـ Objects واحداً تلو الآخر وبنفس الترتيب. الـ Alias لهذا الأمر هو عبارة عن علامة النسبة %. مُشابه للأمر السابق، كل Object يتم تعيينه في المتغير المؤقت (Temporary Variable) _$ ويمكن الوُصول الى خصائص الـ Object عن طريق إضافة نقطة (.) الى هذا المتغير متبوعة باسم الخاصية (Property) المراد الوصول لها. العملية يجب ان تكون محاطة بعلامات الأقواس {} أيضاً.

مثال، الأمر التالي يقوم بعملية الضرب * لكل Object:
$array = 1, 2, 3, 4, 5
$array | ForEach-Object {$_ * 5}
Functions
الدالة (Function) هي عبارة عن مجموعة من الأوامر يتم تعيينها باسم خاص بحيث يمكن استخدام الاسم المُعين لتشغيل هذه الأوامر. يمكن للدالة (Function) أن تحتوي على مُتغيرات (Parameters) بحيث تأخذ قيم (Arguments) تُستخدم داخل الدالة، ويمكن أيضاً ألا تحتوي الدالة على أي مُتغيرات.

المثال التالي هو عبارة عن تعريف للدالة (Function) باسم Find-Flag. عند تشغيل الدالة المُعرفة باسمها Find-Flag، ستُنفذ الأوامر بداخلها، والتي تقوم بالبحث بداية من \:C عن ملف يحتوي على كملة flag كجزء من اسم الملف ومُنتهي بصيغة txt:
function Find-Flag(){
Get-ChildItem -Path C:\ -Recurse -File -ErrorAction Ignore| Where-Object{$_.Name -like "*flag*.txt"}
}
بعد تعريف الدالة والقيام بتشغيلها، نرى الناتج بأن ملف flag.txt موجُود في مُجلد System32:
يُمكن أيضا إنشاء دالة (Function) تقوم بأخذ قيم (Arguments) عن طريق مُتغيرات (Parameters) بحيث تُستخدم القيم المُعطاة كجزء من سلسة الأوامر الموجودة داخل الدالة. المتغيرات (Parameters) تُعرف في الدالة بنفس طريقة تعريف المتغيرات العادية، علامة ($) متبُوعة باسم المُتغير بهذا الشكل Param$.

المثال التالي هو عبارة عن تعريف لدالة (Function) يقبل قيمة (Argument) للمتغير (Parameter) Text– ويقوم بتحويل هذا المتغير المُعطى الى قيمة Base64:
function Base64-Encode($Text){
$Text + " --> " + [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Text))
}

Base64-Encode –Text "Cyber Sync"
بالإضافة الى استخدام متغيرات (Parameters)، يمكننا أيضا استخدام مفاتيح (Switches) كجزء من مُدخلات الدالة. المفاتيح هي مشابهة للمُتغيرات ولكنها لا تقبل أي قيمة (Argument). المفاتيح هي اختيارية الاستخدام عند استخدام الدالة، إذا تم استخدام المفتاح، قيمة المفتاح تكون True داخل الدالة. إذا لم يَقُم استخدامه، قيمته ستكون False داخل الدالة. يمكن الاستفادة من المفاتيح كجزء من الـ if Statement لتشغيل أوامر إضافية اذا كانت قيمة المفتاح True (اذا كان المفتاح مستخدم اثناء استخدام الدالة).

المثال التالي يقوم بتعريف دالة تقوم بتحويل قيمة Base64 الى قيمتها الأساسية من خلال المتغير (Parameter) B64. بشكل اختياري، يمكن استخدام المفتاح (Switch) Execute أيضا للقيام بتشغيل قيمة الـ Base64 كأمر PowerShell وإخراج القيمة الناتجة من تشغيل الأمر:

function Base64-Decode($B64, [Switch]$Execute){
$B64 + " --> " + [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($B64))
#This will only execute if -Execute switch is used
if($Execute){
"Output ==> " + (powershell -EncodedCommand $B64)
}
}

Base64-Decode -B64 aABvAHMAdABuAGEAbQBlAA==
Base64-Decode -B64 aABvAHMAdABuAGEAbQBlAA== -Execute
خاتِمة
كَون PowerShell تتعامل مع Objects يجعل تعلمها واستخدامها أسهل. غَطت هذه المقالة أساسيات PowerShell وبعض أوامرها المهمة والتي يتم استخدامها بشكل متكرر. يمكن العثور على المزيد حول PowerShell بتفاصيل أكثر على موقع Microsoft من خلال الأمر التالي:

Get-Help Get-Help –Online

لمشاركة هذه المدونة
تابعنا
طور مهاراتك من خلال متابعة آخر منشورات فريقنا
مدونات آخرى