仓库链接
clone之后点开html文件即可使用。
效果如下图:

我的配置:VS2019,x86,Unicode字符集。

进程通信设计

为了方便将DLL中的输出信息显示到图形界面中,可以采用进程间通信。

DLL共享数据缓冲区的方式虽然简单,但是局限性太多,因此采用共享内存的方式。Windows的共享内存与Linux中有一些不同,在这里干脆统一介绍一下我了解到的一些IPC方式。

共享内存(Windows)

初始化共享内存

第一步,设计共享内存中的数据结构。(以本次实验为例)

struct shm_data {
int state[10] = { 0 }; // 如果state是1,则为有信息
char seg[10][1000] = { 0 }; // 共享内存的字符串缓冲区
};

第二步,连接或创建一块共享内存。
思路:OpenFileMapping打开共享内存,失败则用CreateFileMapping创建共享内存,得到内存映射对象句柄。然后用MapViewOfFile获取共享内存映射指针。这个指针可以强制转换成任何其他的指针,并直接使用。比如转换成char*或结构体指针struct shm_data*

HANDLE hMap;                        // 内存映射对象句柄
WCHAR nameShm[10] = L"dbgBuf"; // 共享内存名称
LPVOID pShm; // 共享内存指针
char outputStr[1000]; // 输出信息
WCHAR outputWStr[1000]; void InitShm()
{
// 打开或创建共享内存: Name:"dbgBuf", size:3000h
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, nameShm);
if (hMap == NULL) {
hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE | SEC_COMMIT, 0, 0x3000, nameShm);
if (hMap == NULL) {
OutputDebugString(L"共享内存创建失败!");
exit(-1);
}
}
pShm = MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
if (pShm == NULL)
{
OutputDebugString(L"获取共享内存映像失败!");
exit(-1);
}
// 初始化共享内存内容为0
struct shm_data* shmData = (struct shm_data*)pShm;
memset(shmData->state, 0, sizeof(shmData->state));
memset(shmData->seg, 0, sizeof(shmData->seg));
}

修改和读取共享内存的内容

思路:首先按结构体解析共享内存指针。
然后,修改是strcpymemcpy混合使用,自定义性强,不需要过多的说明;
读取是直接用结构体正常读取。

extern "C" __declspec(dllexport)void ChangeSeg(char* str)
{
// 按结构体解析共享内存
struct shm_data* shmData = (struct shm_data*)pShm;
// 写入数据和状态
while (shmData->state[count] == 1) Sleep(500); // 如果缓冲区的数据尚未输出完毕,则暂停写入
memcpy(shmData->seg[count], str, sizeof(shmData->seg[count]));
shmData->state[count] = 1;
// 循环递增序号
count = (count + 1) % 10;
}

共享内存(linux)

参考文章:
Linux 进程间通信(IPC)—大总结
共享内存无锁队列的实现
这两篇都写得非常好。

(尝试使用,但使用失败,暂时不推荐)共享数据缓冲区

(本方法只能在Dll中设置)(以下是老师的解释)
在Dll中设置一个进程间共享缓冲区,所有输出放入该缓冲区,注射程序通过读取该缓冲区获得调试输出:
注意做好数据操作的互斥操作:

再设计一个获取这个共享内存信息的函数:

在注射器程序中调用获得被截获的API的调试输出。
先定义函数指针:

然后读取消息:


Web通信设计

C++后端,Web前端。初步设想是websoket利用本地电脑的端口来使网页和桌面程序通信,桌面程序设置为服务端,网页设置为客户端。

Web端会收到由C++程序发送来的用bash64加密的json格式字符串数据,解密并解析成json后即可将相应的内容填入页面中。

websocket++库安装

我用的是vs2019的x86编译,安装的是32位的Boost库。

主要参考:WebSocket使用(C++环境)(一) — websocket++库的安装与使用
补充参考:VS2019配置BOOST-v1.70.0库(重点关注VS中命令行的使用以及boost库的添加方式)

注: 第二篇博客中提到的bjamv1.72.0之后已经被修改为b2.exe,直接运行b2.exe即可,此处应参考第一篇博客。

websocket实现通信

参考: HTML5 WebSocket_菜鸟教程
官方的样例程序echo_server.cpp

#include <websocketpp/config/asio_no_tls.hpp>

#include <websocketpp/server.hpp>

#include <iostream>

typedef websocketpp::server<websocketpp::config::asio> server;

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind; // pull out the type of messages sent by our config
typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl; // check for a special command to instruct the server to stop listening so
// it can be cleanly exited.
if (msg->get_payload() == "stop-listening") {
s->stop_listening();
return;
} try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (websocketpp::exception const & e) {
std::cout << "Echo failed because: "
<< "(" << e.what() << ")" << std::endl;
}
} int main() {
// Create a server endpoint
server echo_server; try {
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload); // Initialize Asio
echo_server.init_asio(); // Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2)); // Listen on port 9002
echo_server.listen(9002); // Start the server accept loop
echo_server.start_accept(); // Start the ASIO io_service run loop
echo_server.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}

我仅仅修改了其中的on_message函数,也就是消息处理函数,便实现了我的服务端。

其中唯一需要特别提到的是,作为服务端,如何主动向客户端发送数据:

/* 传递s、hdl参数,即可向客户端发送数据
* 发送失败时,应该及时catch错误信息,并输出对应的错误提示
*/
void send_message(server* s, websocketpp::connection_hdl hdl, char* str)
{
try {
s->send(hdl, str, websocketpp::frame::opcode::text);
}
catch (websocketpp::exception const& e) {
if (!strcmp(e.what(), "Bad Connection")) {
is_hooking = 0;
is_sending = 0;
MessageBoxW(NULL,L"意外断连!请用Web控制台断开连接。",L"提示",NULL);
char order[30];
if (processId != 0) {
sprintf_s(order, sizeof(order), "taskkill /PID %lu /F", processId);
system(order);
}
}
else {
std::cout << "Echo failed because: "
<< "(" << e.what() << ")" << std::endl;
}
}
}

(可拓展)通信过程中交换的数据及处理方式

交换数据之前均使用Base64对数据加密,既提高了数据传输的安全性,又保证了中文字符能够正常被解析。

1. C++向Web发送的数据

只有两种json格式的数据:

  1. 进程信息
{
"name": "YourEXEName.exe"
"id":进程id,
"priority":进程优先级,
"thread":{"handle":线程句柄,"id":线程id},
"EXE":{"size":被Hook的EXE的大小,"drive":被Hook的EXE所在的磁盘}
}
  1. Hook信息
{
"type": 被Hook的函数的类型编号,
"name": 被Hook的函数名,
"arg":{"arg1":"argValue1",...}, //参数列表
"st":时间戳字符串
}

Web端对上述两种数据分类处理,分别用来渲染不同的组件。

2. Web向C++发送数据

  1. 网页端填写的EXE绝对路径HookServer绝对路径json字符串:
{
"exePath": 被Hook的EXE的绝对路径,
"hookPath": HookServer的绝对路径
}

C++接收到这类数据,就会读取其中的路径,并填入程序中用来运行。
2. 其他的控制信息:

start hook // 用来开启C++中的Hook功能
stop hook // 用来关闭C++中的Hook功能
pause // 让C++中的数据停止向Web端发送

前端页面设计

出于轻量级考虑,采用LayUI的库,在这里我对这个库的作者贤心致以最大的谢意!我很喜欢这个库,虽然2021年10月13号它的官网就要关掉了。
并顺便部署到了Gitee Page上。
这是前端链接。

LayUI的使用

参考:(这里本来应该是官网的Demo文档,但是官网要关了,所以可能只能从源代码慢慢研究了)
LayUI码云源代码
附一个非官方的教学:LayUI学习笔记-傻瓜教程,我主要参考的是官网的代码,但是没办法。

数据结构设计

对于一类函数,以堆函数举例,它的数据结构是一个字典。

 var heap = {
"HeapCreate": [],
"HeapDestory": [],
"HeapFree": [],
"about_analysis": {
"about": {
"name": "涉及的堆地址"
},
"analysis": {
"heapSet": [],
"[HeapCreate]重复创建堆": [],
"[Unknown]释放或销毁了未知的堆": [],
"[HeapDestory]销毁了未创建的堆": [],
"[HeapFree]释放了未创建的堆": []
},
"num":0
},
"num":0
};

字典中包含三个必要组成部分:

{
"FuncName":[],
"about_analysis":{},
"num":0
}

其中FuncName就是这类函数的函数名称,用数组记录每一条被Hook到的函数记录。
about_analysis的结构如下:

{
"about":{
"name":"",
},
"analysis":{
"Warning1":[],
"Warning2":[],...
}
"num":0
}

它用来存储一类函数的分析结果。

num是记录的数量,有新的Hook信息/分析信息时,数量增加;查看信息后,数量清零。

设计与交互

主体如下,实质是三种页面的渲染,在撰写过程中我收获了不少新方法,如果现在我重新写,会写得好更多。

分开介绍一下这些页面的结构。

进程信息

实质就是一个卡片,内含一个树状组件,组件中包含表格,表格中包含一些信息。

这些信息由C++发送的json字符串解析得到:

{"name": "WindowsProject1.exe", "id": 44328,"priority": "NORMAL", "thread":{"handle": "00000184", "id": 9624}, "EXE":{"size":109568,"drive":"E"}}

json的格式是:

{
"name": "YourEXEName.exe"
"id":进程id,
"priority":进程优先级,
"thread":{"handle":线程句柄,"id":线程id},
"EXE":{"size":被Hook的EXE的大小,"drive":被Hook的EXE所在的磁盘}
}

Hook 函数信息

其实质是一个标题、3个按钮以及一个卡片,卡片中包含一个树状组件。

这些信息由C++发送的json字符串解析得到:

{"type":5,"name":"ReadFile","arg":{"hFile":"000001BC","lpBuffer":"0133C4C8","nNumberOfBytesToRead":"0000003C","lpNumberOfBytesRead":"0133C4C4","lpOverlapped":"00000000","lpFileType":"dat","FilePath":"C:\\Windows\\Fonts\\StaticCache.dat"},"st":"2021-9-24 20:27 52s758ms"}

json的格式是:

{
"type": 被Hook的函数的类型编号,
"name": 被Hook的函数名,
"arg":{"arg1":"argValue1",...}, //参数列表
"st":时间戳字符串
}

这些结果会存入上面设计的数据结构的FuncName中。

异常分析

实质是一个标题、三个按钮以及一个卡片,卡片中有两个树状组件,一个是分析的关键参数,另一个是分析的行为。

该部分的数据,由用户(程序员)自己根据获取到的Hook函数参数信息,设定自己的分析逻辑而按格式生成。

这些结果会存入上面设计的数据结构的about_analysis中。

如何拓展或在该基础上新增功能?

非!常!简!单!

  1. 假设你只要多新Hook一个函数,这个函数属于heap类,那么,你要做的就是,在heap这个字典中,添加一个"FuncName":[]

  2. 假设你需要新增一类函数,只要按照相同的数据结构,多加一个变量,然后在var page_info = [process, MessageBox, heap, socket, file, Registry, memory]数组中追加这个变量。
    然后在html的左侧导航栏中添加一行nav标签,就依葫芦画瓢。(PS: 这一块可以更加简化,是我自己没优化到位,导致复杂了)然后把你添加的标签内容追加到var pageName = ["进程信息", "MessageBox", "堆操作", "网络通信", "文件操作", "注册表"];里面,就可以了。

  3. 假如新增一类分析,也是一样的,只是追加的标签内容要放到var pageName_2 = ["堆操作问题", "网络通信行为", "文件操作异常", "注册表可疑操作", "内存拷贝监测与关联"]中。
    至于分析的逻辑,就在checkUnexpected函数中写。

  4. 要新增Hook信息,只要参照其他的Hook函数的样子,修改DLL的代码然后重新生成DLL就行了。其中DLL中传出来的参数决定了最终展现的内容。自定义即可。

所以在这个模板上细化内容,甚至进一步完善分析逻辑,那是非常简单的。(前提是有现成的分析逻辑……比方说怎样判断文件自我复制,这一块我实现得就很糟糕)


技术难点及特点

1. C++函数设置任意类型和个数的参数

参考:泛化之美–C++11可变模版参数的妙用
我使用了逗号展开参数包的方式。
思路:先用可变模版参数定义可变参数类型的原函数,再用逗号展开函数使参数个数任意。
下面是一个完整的小demo。执行expand程序,可以让被格式化的arg[i]字符串存入dst[i]中。

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "stdarg.h"
#include <iostream>
#include <cstdio>
#include <WinSock2.h>
#include<typeinfo> template<typename Tx, typename T2, typename Ty>
// 按格式写入变量dst[i]
void sprintfWithType(Tx dst, T2 i, Ty arg)
{
if(typeid(arg)==typeid(int))
sprintf(dst[i], "%08X", (UINT32)arg);
else {
sprintf(dst[i], "Unknown arg.");
}
} template <typename Args1, typename ...Args2>
void expand(Args1 args1, Args2... args2)
{
int i=0;
int arr[] = {(sprintfWithType(args1, i++, args2), 0)...};
} int main()
{
char tes[2][100];
//Sexpand(1,2,3,4);
int hand=0,hand2=1;
expand(tes, hand, hand2);
printf("%s\n%s",tes[0],tes[1]);
return 0;
}

…待补充

【HUST】网安|软件安全课设|记录的更多相关文章

  1. JAVA课设---五子棋

    1.团队博客链接 JAVA课设-五子棋-团队博客 2.个人负责模块: ①对鼠标事件的处理 , 此模块需处理五子棋的放置问题.颜色转换问题.以及当五子连线时弹出窗口显示结果. ②对MainFrame中主 ...

  2. JAVA课设个人博客--多源数据教学管理系统

    JAVA课设个人博客--多源数据教学管理系统 1.团队课程设计博客链接 https://www.cnblogs.com/hq9-/p/10278470.html 2. 个人负责模块或任务说明 主要模块 ...

  3. C语言课设——电影院选票系统

    C语言课设--电影院选票系统 1.课题介绍 大家都爱看电影,现请参考一个熟悉电影票预订系统,实现C语言版的订票系统.了解订票如何实现的.系统主要有2类用户:管理员用户和顾客用户. 管理员用户 1.电影 ...

  4. 2018-2019学年第一学期Java课设--魔塔

    目录 Magic-Towers 一.团队名称.团队成员介绍.任务分配 团队名称:MoTa 团队成员介绍 任务分配 二.项目简介 三.项目采用技术 四.项目亮点 主界面显示主要信息功能 游戏动画 五.项 ...

  5. ATM取款机模拟——数据结构课设

    今天帮人写的第二篇课设 . ;-) 机智的窝 要求:大概说一下吧,就是要创建一个用户(初始化一账户),模拟ATM的业务(取款,100的整数倍,改密               码,查剩余金额.等等,各 ...

  6. JAVA课设——中药古籍《太平圣惠方》数据处理与分析系统

    一.配置JAVA环境 本次课设是在Windows 10(64bit)平台上实现的,所以首先得配置下JAVA环境. 步骤一:先下载一个JDK(1.7)安装包,安装好JDK: 步骤二:JDK环境配置(由于 ...

  7. 轻松月薪过万,NISP证书含金量有多重|NISP管理中心|网安伴|nisp

    nisp一级证书含金量 NISP一级证书是面向各个行业工作人员信息安全意识普及化和网络信息安全基础培训的国家级验证.持NISP一级证书可以从信息安全保密较高的单位得到加分.证书由中国信息安全测评中心授 ...

  8. 【python学习】PyQt基础学习以及一个信息论与编码课设实例

    这学期的信息论与编码的课设需要用编程语言实现霍夫曼.费诺以及香农编码,要具备在windows下的可视化操作界面,因此就选用PyQt作为开发工具,本篇博客记录一下PyQt的基础以及课设的实例 参考: & ...

  9. C语课设心得分享(二)

    咱们今儿说说IDE的事儿. IDE是「集成开发环境」的意思,比如咱们常用的VC6.0,就是开发C语言所用的IDE的一种.对于IDE的认识,可能有些朋友有点儿模糊,咱们捋一捋,我也会给出一些IDE方面的 ...

  10. C语课设心得分享(三)

    调试. 以前咱们写课后习题,一般也不需要使用调试,如果程序编译error,根据错误信息就可以改好:如果是结果错误,那么在稿纸上过几遍基本也可以得出结果. 但咱们这个课设比较大,就需要很多调试的过程,尤 ...

随机推荐

  1. 从 Windows Forms 到微服务的经验教训

    Photo by Dan Counsell on Unsplash 如果说软件开发中有什么不变的东西,那就是变化. 在 .NET 生态系统中摸爬滚打的这二十年里,我见证了各种框架的起起落落,目睹了容器 ...

  2. 在 GitLab CI/CD 中使用内置的容器镜像库

    配置 Docker-in-Docker Docker-in-Docker (dind) means: 你应该注册一个 Docker executor 或 Kubernetes executor 执行器 ...

  3. WPF 事件实现MVVM中的Command绑定

    1. 在ViewModel中弹出消息提示框,需要添加下面的代码块: <dxmvvm:Interaction.Behaviors> <dx:DXMessageBoxService /& ...

  4. 图片的 rgb信息 byte[] 直接转换为bmp文件

    方法1: /// <summary> /// rgb像素值转换为bmp文件 /// </summary> /// <param name="buffer&quo ...

  5. Vulnhub-DC-9靶机-SQL注入拿到账户+利用端口敲门连接ssh+信息泄露利用root脚本追加提权

    一.环境搭建 选择扫描虚拟机 选择靶机路径 如果出现以下信息 如下修改,修改和虚拟机一样的版本 二.信息收集 扫ip nmap -sn 192.168.108.0/24 得到靶机ip:192.168. ...

  6. 基于注意力机制与改进TF-IDF的推荐算法

    前言 本篇文章是2020年8月发表于<计算机工程>的一篇期刊论文,文章名称<基于注意力机制与改进TF-IDF的推荐算法>. 文章针对传统推荐系统主要依赖用户对物品的评分数据而无 ...

  7. 面试题54. 二叉搜索树的第k大节点

    地址:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/ <?php /** 面试题54. ...

  8. Dify 的核心技术栈

    Dify 的技术栈涵盖多个层次,结合了前沿的 AI 框架.成熟的开发工具及高效的部署方案. 以下是其核心组成: 一.基础架构与后端技术 编程语言与框架 Python + Flask:后端服务主要基于 ...

  9. mongodb 数据库操作——备份 还原 导出 导入

    mongodump备份数据库 命令格式 mongodump -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -o 文件存在路径 如果没有用户,可以去掉-u和-p. 如果导出本机 ...

  10. 手把手教你如何给 Docker 开启 IPv6 网络支持

    Docker 默认是不开启 IPv6 支持的,但是我们某些业务往往又需要 IPv6 的支持,特别是 IPv6 普及大势所趋,本文主要介绍的是如何开启 Docker 桥接网络 IPv6 支持,这篇文章具 ...