255
0

Unpacking Malware – מתאוריה ליישום

255
זמן קריאה: 14 דקות

תוכן עניינים

אז מה זה בעצם Packer-ים ו- Packing ?

כמו שהשם מרמז מדובר בדחיסה/אריזה/צמצום (או כל מילה נרדפת שתבחרו) שבהקשר שלנו מתבצע על Sample-ים של Malware(ים).
המניעים לביצוע Packing ל- Malware הם בעיקר השיקולים הבאים:

  1. צמצום הגודל של Malware Sample
  2. להקשות על החוקר לבצע מחקר מעמיק של ה-Malware ולגרום לו לבזבז זמן רב בביצוע ה-Unpacking.

מה ש – Packer-ים בעצם עושים, זה לקחת קובץ executable בתור קלט ולהוציא קובץ executable אחר בתור פלט (נשמע בקטנה לא?😄)
הקאץ’ שקובץ ה – exectuable שמתקבל בתור פלט, לרוב מאופיין באחד/כמה מהבאים:

  1. Compressed
  2. Encrypted
  3. Transformed – יכול (וכמעט תמיד יכיל) טכניקות של: Anti-Debugging, Anti-Disassembly ו/או Anti-VM.

Non Packed VS Packed Executables

קבצי exe שאינם Packed נטענים לזיכרון ע”י ה – loader של מערכת ההפעלה.
קבצי exe שעברו תהליך של Packing, יכילו את קובץ ה – exe המקורי בתור data (ב – section כלשהו/בכמה section-ים ב – PE Structure File) ויכילו Unpacking Stub.
בקבצי exe שהם Packed, ה – loader של מע’ ההפעלה טוען את ה – Unpacking Stub ואז הוא בתורו טוען את קובץ ה – exe המקורי.
ה – Entry Point של קובץ ה – Executable החדש יצביע ל – Unpacking Stub במקום ל – Entry Point של קובץ ה – executable המקורי.
ה – Unpacking Stub, תפקידו:

  1. לבצע Unpack לקובץ ה – exectuable המקורי לזיכרון.
  2. לבצע Resolve ל – imports של קובץ ה – executable המקורי.
  3. להעביר את ביצוע ריצת התוכנית ל – Original Entry Point (או בקצרה OEP).

כמו שצוין, ה – Unpacking Stub כל תפקידו, בגדול, זה לבצע Unpacking לקובץ ה – exe המקורי.
בתור Malware Analyst, אם ננסה לבצע Static Analysis על ה – Packed Executable – בעצם נמצא את עצמנו מבצעים חקירה על ה – Unpacking Stub ולא קובץ ה – exe המקורי —> מה שממש לא נרצה לעשות 😀

איך קובץ Executable מתחיל לרוץ…חזרה למקורות

קבצי exectuable (באופן כללי) נטענים לזיכרון ע”י ה – loader של מע’ ההפעלה.
ה – loader קורא את ה – PE header של הקובץ, ומקצה זיכרון לכל section של קובץ ה – executable.
לבסוף, ה – loader מעתיק את ה – section מה – disk למקום שהוקצה בזיכרון (RAM).

כשמדובר בקבצי Packed Executable, ה – loader, באותו אופן, יקצה זיכרון לכל section – רק שעכשיו, ה – section יכול להגיע מהקובץ המקורי או כזה שנוצר מה – Unpacking stub.
ה – Unpacking Stub מבצע Unpacking לקוד של כל section והוא בתורו מועתק למקום שהוקצה עבורו בזיכרון.

פרט לטעינת קובץ ה – exe לזיכרון (שמתבצע בעזרת קריאת וטעינת ה – section-ים שלו), יש גם את שלב ה – Resolving Imports.

קבצי exe במצבם הטבעי (unpacked) מכילים section של Import Address Table.
בעזרת section זה ה – loader יודע לאיזה פונקציות צריך לבצע Import על מנת להבטיח ריצה תקינה של התוכנית.
ה – loader לא יודע לקרוא packed sections ולכן ה – Unpacking Stub הוא זה שיבצע resolve ל – Imports.
האופן בו שלב זה יתבצע, תלוי ב – Packer.
כמה מן הגישות הנפוצות הן:

  1. ה – Unpacking Stub יבצע import רק ל – LoadLibrary ו- GetProcAddress.
    לאחר ביצוע ה – Unpacking, תתבצע קריאה של ה – Import Table המקורי -> קריאה ל – LoadLibrary לכל ספרייה נחוצה -> קריאה ל – GetProcAddress לכל Import Function שיש בו שימוש.
  2. הגישה המחמירה – ה – Malware יבצע remove לכל ה – Imports הקיימים (כולל GetProcAddress ו- LoadLibrary). על מנת לרוץ, ה – Packer יצטרך למצוא את כל הפונקציות הנחוצות לו בדרכים אחרות – מבלי להשתמש ישירות ב – LoadLibrary ו- GetProcAddress.
    לחלופין, הוא יצטרך ראשית לחשוב על דרך למצוא את LoadLibrary ו- GetProcAddress ולאחר מכן לבצע import לפונקציות הנחוצות לו.
    הערה – לרוב Shellcodes ישתמשו בשיטה זו, שכן היא מאפשרת Import Function Table ריק לחלוטין. במקרים כאלו יהיה מדובר ב – Packer מורכב וכל זה במטרה להקשות על ה – Malware Analyst בביצוע החקירה.

מ – Packed ל – UnPacked: מהלכה

לאחר שה – Unpacking Stub סיים לבצע את שלב ה – Unpacking, קובץ ה – exe המקורי יכול להתחיל לרוץ.
תתבצע קפיצה מהחלק בקוד, לו ה – Unpacking Stub ביצע Unpacking ל – Original Entry Point (OEP) של הקוד המקורי – קפיצה זו נקראת: The Tail Jump.
ה – Tail Jump, לרוב יתבטא בפקודת jmp שמעבירה את הקוד למקום אחר. ברוב המקרים הקפיצה תהיה ל – section אחר לגמרי ולכן נראה קפיצה/שינוי גדול במרחב הכתובות בזיכרון אליו נקפוץ.
כיוון שפקודת jmp היא נורא אינדיקטיבית, תוקפים רבים יעשו שימוש גם בפקודות כמו ret, call או אפילו Kernel Function כמו NtContinue, ZwContinue במטרה להסתוות כפונקציות המשמשות למטרה אחרת.

אילוסטרציה קטנה על מה דיברנו:

לפני ואחרי Packing:

משמאל, אפשר לראות את המבנה של קובץ ה – Executable – לפני שהתבצע עליו תהליך של Packing.
ה – Entry Point מצביע אל ה – text Section. שכן שם נמצא הקוד של קובץ ה – PE שירוץ בתחילת ריצת התוכנית.
מימין, אפשר לראות את המבנה של קובץ ה – Executable אחרי שהתבצע עליו תהליך של Packing.
כמו שציינו, במצב כזה, ה – Entry Point מצביע אל ה – Unpacking Stub.
ה – Loader של מע’ ההפעלה, יטען את הכתובת בו הוא מתחיל לרוץ – לזיכרון, וזה בתורו יבצע את שלב ה – Unpacking ויריץ את קובץ ה – exe.

לפני ואחרי Unpacking:

משמאל, קובץ Packed Executable כשהוא נטען לזיכרון.
ה – Unpacking Stub, ביצע Unpacking לקוד המקורי, וחלק מה – Section-ים ההכרחיים לריצת התוכנית כבר במצב של Unpacked. כיוון שה – Unpacking Stub לא סיים לבצע את כל התהליך של ה – Unpacking (שיתבצע בזמן ריצה), ה – Import Table של התוכנית עדיין לא פוענח.

מימין, קובץ ה – Executable לאחר שעבר Unpacking מלא.
ה – Import Table כבר פוענח וניתן לקריאה וה – Entry Point מצביע ל – Original Entry Point של קובץ ה – exe.

אז איך בפועל נבצע Unpacking

קיימות שלוש גישות/דרכי פתרון שיכולות לתת לנו מענה כאן:

  • Automated Static Unpacking – במידה וזיהינו כי ה – Malware שלנו משתמש ב – Packer מוכר ואכן קיים עבורו כבר Unpacker אוטומטי, כמובן שנעדיף להשתמש בו – זה יחסוך לנו לבצע את ה – Unpacking ידנית, בעצמנו, ונוכל לצלול ישירות לחקירה האמיתית.
    ‘Automated Static Packers’ בעצם מבצעים decompress ו/או decrypt לקובץ – חזרה לקובץ ה – exe המקורי, מבלי להריץ את ה – Malware.
    אם אכן נפלנו על Malware שמשתמש ב – Packer שיש לו Automated Static Unpacker – נעדיף להשתמש בגישה זו שכן גישה זו המהירה והבטוחה מבין השלושה.
    כלי שימושי: PE Explorer.
    Packer-ים מוכרים: NSPack, UPack, UPX.
  • Automated Dynamic Analysis – בגישה זו, ה – Unpacker יריץ את הקובץ ה – Executable ויתן ל – Unpacking Stub לעשות את שלב ה – Unpacking.
    ברגע שה – Unpacker סיים את עבודתו, התכונית תיכתב ל – Disk וה – Unpacker יבצע reconstruct ל – Import Table של קובץ ה – exe המקורי.

בגישות אלו, ה – Automated Unpacker צריך לזהות את הכתובת בה ה – Unpacking Stub מסתיים ואיפה ה – OEP של קובץ ה-exe המקורי מתחיל – שזו מטלה לא פשוטה 😀

לכן במקרים רבים, ניווכח לראות כי Unpacker-ים אוטומטיים נכשלים בביצוע ה – Unpacking כי הם פשוט לא מצליחים למצוא את הכתובת/מקום הזה.
עקב העובדה כי אין מספיק Automated Unpackers שיעשו את העבודה עבור Malware-ים רציניים – זה מביא אותנו לגישה השלישית:

Manual Unpacking
בגישה זו בעצם ניתן ל – Unpacking Stub לרוץ.
כשזה בתורו יסיים, נבצע Dump ל – Process מהזיכרון.
לבסוף, נבצע “חיווט מחדש” של ה – PE Header כך שיצביע ל – OEP ול – Import Address Table שפוענח.
השלב ה-“ידני” בגישה זו הוא מציאת ה – OEP וביצוע ה – Reconstruct ידני ל – Import Table.
גם מציאת ה – OEP ושחזור ה – Import Table, בחלק מהמקרים יכול להתבצע אוטומטית ע”י כלים קיימים כגון OllyDbg או x64Dbg אך במרבית המקרים נצטרך למצוא אותם ידנית.

**שלב ה – Reconstruct של ה – Import Table יכול להתבצע במספר אופנים…ביניהם:
1. ממש חקירה סטטית של ה – Malware וביצוע Labeling לכל הפונקציות בהן הוא משתמש – בגישה זו נוכל לנתח סטטית את ה – Malware אך לא נוכל להריץ אותו שכן לא באמת ביצענו Reconstruct ל – Import Table.
2. מציאת הכתובת בזיכרון בו נמצא ה – Import Table –> מכאן נוכל להזין פונקציה-פונקציה בזמן הרצת הפוגען או שימוש בסקריפט שיעשה את זה עבורנו.

מ – Packing ל – Unpacking: למעשה

החלק הבא יציג נקודות מפנה במהלך החקירה שממחישות כי אכן מצאנו את ה – OEP המיוחל.
נבחן איך הם נראים בקוד ה – Assembly ומה רצף הפעולות שעשינו.

פקודת Jmp ומיד אחריה – רצף ארוך של Null Bytes

לאחר הרצה ב – PEiD זה נראה כי הקובץ לא עבר Packing (או יותר מדויק, PEiD לא זיהה נכונה כי מדובר בקובץ Packed).
מבט נוסף ב – Section-ים של קובץ ה – Executable מראה קיים Section העונה על השם UPX2 – מה שגורם לנו להבין כי מדובר ב – Packer UPX שפשוט לא זוהה כהלכה.
UPX הוא Packer דיי נפוץ ולכן העובדה כי לא זוהה – גורמת לנו להבין שהוא כנראה עבר מודיפיקציה מסוימת ומדובר ב – Custom Version של UPX (ניסיון כושל של ביצוע Unpacking עם UPX Unpacker מאמת זאת).

כשנעלה את הקובץ ל – IDA Pro ניתקל ב – Pop Up Message הבא:

אימות נוסף לכך שמדובר בקובץ שעבר Packing.
נסקור את קוד ה – Assembly בחיפוש אחר פקודת jmp (או פקודת ret/call) בניסיון למצוא את ה – Tail Jump. ב – Sample-ים יותר מורכבים לא ננקוט בגישה הזו שכן ייתכן ונצטרך לעבור על המון! שורות קוד…במקרה שלנו מדובר ב – Malware Sample יחסית דל ולכן נוכל לעשות זאת.
פקודה שתופסת את העין היא:

החל מפקודת jmp זו, IDA Pro לא הצליח לזהות לאן הקוד ממשיך.
בנוסף, אם נמשיך לסקור את המקום בזיכרון אליו מפנה פקודת ה – jmp נגיע לכתובת הרבה יותר רחוקה – מה שמחזק את ההשערה כי זה אכן ה – Tail Jump שיביא אותנו ל – OEP המיוחל.

בתור ולידציה אחרונה, נפתח את ה – Malware ב – OllyDbg ונלך לכתובת של פקודת ה – jmp:

ניווכח לראות כי אכן מדובר ב – Tail Jump המיוחל.
העובדה כי לאחר פקודה זו מגיעים הרבה Null Bytes מהווה ולידציה אחרונה להשערה שלנו – שכן מדובר בקפיצה ל – section אחר.
נגדיר Hardware Breakpoint בכתובת זו וניתן ל – OllyDbg להריץ את ה – Malware על מנת שיבצע Resolve ו- Unpack ל – Malware.
נבצע Single Step עם ה – Debugger שלנו ונגיע לקטע קוד הבא:

כפי שניתן לראות, זו ההתחלה של קובץ PE רגיל:

  • מתבצעת דחיפה של EBP למחסנית לצורך שמירת ה – Pointer ל – Stack Frame הקודם.
  • מתבצעת השמה של ESP ל- EBP לצורך reference מקומי למשתנים ב – Stack Frame הנוכחי.
  • בנוסף, ניתן לראות את הקריאה האופיינית ל – GetVersion שמתרחשת בקבצי Executable.

נבצע Dump ל – Process ב – OEP שעתה מצאנו:

כעת, נטען את קובץ ה – exe החדש ל – IDA Pro וניווכח לראות את הקוד המלא (לפני Packing) של ה – Malware שלנו:

ניתן לראות כי כל ה – Imports בהם ה – Sample משתמש כבר פוענחו, הפרמטרים אותם הפונקציה main מקבלת זוהו כהלכה ועוד שלל מזהים נוספים כי הגענו לקובץ ה – exe שחיפשנו.
מכאן ממשיכים בחקירה הרגילה, במטרה לגלות את הפונקציונאליות של ה – Malware בהיבטים השונים: Persistence, Stealth, Movement וכ’ו…

שימוש בפקודות PUSHAD ו- PUSHFD

לאחר סריקה ב – PEiD אפשר לראות כי מדובר ב – Packer מהמשפחה של: PECompact 1.68 – 1.84.
אם נבחן אותו ב – IDA Pro אכן נגלה הקובץ Packed (דל ב – Imports ודל בקוד בעל ערך):

נמשיך לחקור את הקובץ בזמן ריצה ב – OllyDbg.
לאחר טעינה, נשתמש ב-Plugin של OllyDbg הבא: Find OEP By Section Hop (Trace Into).

Find OEP By Section Hop על קצה המזלג

בקצרה, Find OEP By Section Hop, מסתמך על העובדה כי לרוב ה – Unpacking Stub נמצא ב – section אחד, בעוד קובץ ה – Packed Executable נמצא ב – section אחר.
OllyDbg יודע לזהות מתי מתבצע מעבר כזה בין section-ים ויבצע שם break לריצת התוכנית.
OllyDbg יכול לבצע את ה – break הזה בשני אופנים: Step Into או Step Over.
בגישת ה – Step Over, יתבצע Step-Over עבור כל פקודת call בה הוא יתקל.
פקודות call בהרבה מקרים משתמשות בפונקציות בקבצי DLL חיצוניים ואלו מן הסתם נמצאים ב – section-ים מחוץ לקובץ ה – exe…הדילוג על פקודות אלו – מטרתו למנוע הרבה false-positives במציאת ה – OEP.
פקודת ה – Step-Into כשמה כן היא, תבצע Step Into בכל פקודת call מה שיכול להאריך את תהליך מציאת ה – OEP (ולהעלות הרבה false-positives).
תוקפים מנוסים כבר מכירים איך OllyDbg וכלי חקירה נוספים עובדים ולכן יטמיעו מנגנונים לעקוף אותם.
למשל, תוקפים לרוב משתמשים בפקודות call שלא מחזירות שום ערך. במקרים האלו ,OllyDbg לא ידע “לאן לחזור” ויקפיץ הודעה כי הוא נכשל במציאת ה – OEP…

ה – Section Hop מביא אותנו לקוד ה – Assembly הבא:

נראה כי נתקלנו ב – False-Positive שכן אנחנו לא רואים רצף של Null Bytes או פקודת jmp ל – Section אחר.
נשתמש שוב בפונקציית ה – Section Hop, הפעם ב Step-Over, נגיע לקוד ה – Assembly הבא:

נראה כי שוב לא מדובר ב – Tail Jump המיוחל, עם זאת אנחנו רואים כי כמה פקודות קודם לכן, קיים רצף ארוך של Null Bytes.
נגלול במעלה פקודות ה – Null Bytes שכן ייתכן ונמצא את ה – Tail Jump ממש לפניהן.
במהלך הסקירה כלפי מעלה, אנחנו נתקלים ברצף הפקודות הבא:

כיוון שמדובר במצב דיי חריג, נמשיך לחקור בכיוון הזה.
נזכור שכל החקירה שעשינו עד כה היא על ה – Unpacking Stub והמטרה שלנו היא למצוא את המקום בו מתבצע המעבר ל – OEP המקורי של קובץ ה – Executable…לכן כל תרחיש בו אנו נתקלים במשהו מוזר/חריג/חשוד, מה שנקרא – It’s gonna worth your while לבדוק את זה.

בעקבות ההבחנה ב – POPAD ו- PUSHAD, נראה אם מתבצעות פקודות PUSH למחסנית – לפני הריצה של ה – Unpacking Stub.

לאחר הרצה מחדש של התוכנית וחיפוש אחר פקודות PUSHAD, אנחנו נתקלים בקטע קוד הבא:

בשימוש בפקודות PUSHFD ו- PUSHAD בעצם מתבצעת שמירה של ערכי ה – General Purpose Registers וה- ELAGS Registers במחסנית.
פעולה זו מתבצעת לפני ההרצה של ה – Unpacking Stub.
נוכל לשים Hardware Breakpoint על הרגע בו ה – Register-ים הללו נדחפים החוצה מהמחסנית – זה קרוב לוודאי יהיה הרגע בוא נגיע ל – OEP המקורי.

ברגע שה – Breakpoint שלנו עוצר, מוצג בפנינו קטע ה – Assembly הבא:

מצאנו את הרגע בו ה – Register-ים המדוברים נדחפים החוצה מהמחסנית, הנה ה – Tail Jump אל ה – OEP המיוחל.
נבצע Step-Into לפקודת ה – Push ומיד לאחר מכן נבצע Dump כמו שביצענו בדוגמה הקודמת.
כעת כשנטען את ה – Sample ל – IDA Pro, נראה כי אכן מדובר בקובץ Executable המקורי, לפני Packing:

תובנות, תהיות ושאר ירקות

אז ראינו בכתבה הזו לא מעט

  1. סקרנו בפן התאורטי איך נראה קובץ Packed Executable לעומת Unpacked Executable על הנדבחים השונים.
  2. למדנו מה הן הגישות לביצוע Unpacking – אוטומטיות וידניות.
  3. ראינו כי בפועל, ביצוע ה – Unpacking, בין אם מתבצע ידנית ובין אם מתבצע אוטומטית, מורכב משני שלבים מרכזיים:
    3.1 מציאת ה – OEP שזה המקום בזיכרון בו הקובץ המקורי מתחיל לרוץ ברגע ששלב ה – Unpacking הסתיים.
    3.2 ביצוע Resolving ל – Import Address Table – על מנת לוודא ריצה תקינה של הקובץ לאחר שלב ה – Unpacking.
  4. ראינו דוגמאות מעשיות לביצוע Unpacking של קבצי exe וסקרנו את צורת המחשבה בעת החקירה, לאיזה נקודות נרצה לשים לב בקוד ה – Assembly ועוד.

מה לא ראינו וכדאי לכם לחקור עוד

  1. בהרבה מקרים, כשמדובר ב – Malware Sample מורכב, יהיה לנו הרבה יותר קשה למצוא את ה – OEP או לבצע Resolve מלא ל – Import Address Table.
    לעיתים, ניתקל ב – Packer לא מוכר המשתמש בטכניקות חדשות או אף Unpacker המבצע Unpacking למקבצים קבועים מהקוד בזמן ריצה, שוב כדי להקשות על שלב ה – Malware Analysis.
    אנחנו ננסה כיוונים שונים בחקירה, ניתקע ונתחיל מההתחלה – בקיצור, נשרוף זמן יקר!
    במקרים כאלו, שווה לשקול לבצע Unpacking חלקי של ה – Malware ולו רק כדי להתקדם בחקירה ולתפוס קצת כיוון אודות הפונקציונאליות של ה – Malware (שימושי בעיקר בתרחישים של מתקפת Ransomware או IR קשוח).
    במקרים כאלו, נוכל לטעון את הקוד או חלקים מהקוד שהצלחנו לבצע להם Unpacking בזמן ריצה ל – IDA Pro או להריץ עליהם Strings ולו רק כדי לקבל תמונה קצת יותר ברורה אודות הפונקציונאליות.
  2. ביצוע Unpacking לקבצי DLL.
    OllyDbg מאפשר טעינת ודיבוג של קבצי DLL בעזרת Process של OllyDbg הקרוי: loadDll.exe.
    הקאץ’ הוא, שה – Starting Address של קבצי DLL הקרוי DllMain יקרא לפני ה – Break שמתרחש ב – OllyDbg – מה שיגרום למצב בו – עד שיתרחש ה – Breakpoint הראשוני, ה – Unpacking Stub כבר סיים לרוץ – מה שיקשה על מציאת ה – OEP המקורי.
    במקרים כאלו, נשנה את ה – Header של קובץ ה – DLL, כך ש – OllyDbg יטען אותו בתור קובץ EXE.
    כשנמצא את ה – OEP, נשנה את ה – flag חזרה למצבו הראשוני, ונחזור להתייחס לקובץ בתור קובץ DLL.
  3. ביצוע Resolving ידני ל – Import Address Table
    שלב נורא חשוב שלא הצגנו כאן.
    ברוב המקרים OllyDump ו/או כלים נוספים כמו: ImpRec יצליחו לבצע Restore ל – Import Table פשוט ע”י מעבר סדרתי אחר התוכנית בזיכרון בחיפוש אחר רשימה של Imported Functions.
    במקרים בהם הכלים העומדים לרשותנו לא מצליחים לבצע זאת – נצטרך לדעת לעשות זאת בצורה ידנית.
    בגישה הידנית, לרוב נריץ את ה – Malware, נשים Breakpoint על הקריאה ל – GetProcAddress שכן פונקציה זו אחראית לקריאה לפונקציה המדוברת בהתאם לספרייה לה התבצעה קריאה עם LoadLibrary.
    צעד צעד, נבחין לאיזה פונקציות ה – Malware קורא עד שהוא מסיים לבצע Rebuild ל – Import Address Table.
    לאחר ביצוע ה – Rebuild לעיתים קרובות יגיע המעבר ל – Tail Jump שמוביל ל – OEP המיוחל.

לסיכום, אני אצא באמירה קצת יומרנית, אך כזאת שלדעתי ממחישה נקודה חשובה:

היכולת שלך לבצע Unpacking בצורה מהירה וממוקדת היא זו שתקבע עד כמה טוב תהיה בתור
Malware Analyst.

מקווה שהייתה לכם קריאה מהנה חבר’ה והצלחתי לעורר בכם עניין להעמיק באחד הנושאים שסקרנו פה.
כרגיל, מוזמנים לתת חוות דעת, הערות והארות, או סתם לכתוב לי על מה הייתם רוצים שהפוסט הבא ידבר😄

דן דורפמן
WRITEN BY

דן דורפמן

סטודנט למדעי המחשב באוניברסיטה הפתוחה.
SecOps Engineer & Automation Developer.
בעל תשוקה לתחום ה- Malware Analysis & Reverse Engineering.
בזמני הפנוי אני: לומד דברים חדשים, משחק כדורסל, שומע מוזיקה, ומדבר על החיים עם החבר'ה ;)

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *