仓库链接
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. Arduino语法--运算符

    本节介绍最常用的一些Arduino运算符,包括赋值运算符.算数运算符.关系运算符.逻辑运算符和递增/减运算符. 一. 赋值运算符 =(等于)为指定某个变量的值,例如:A=x,将x变量的值放入A变量. ...

  2. 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(9)

    1.问题描述: 通过push token向鸿蒙手机推送一条通知,收到通知后,通知右侧不展示图片. 解决方案: 检查一下是否存在图片风控:https://developer.huawei.com/con ...

  3. Hi3516EV200 编译环境配置及交叉编译软件包

    基础信息 OS: Ubuntu 16.04 xenial SDK 版本: Hi3516EV200R001C01SPC012 - Hi3516EV200_SDK_V1.0.1.1 SDK 包路径:Hi3 ...

  4. C# TCP/IP通信,Socket通信例子

    1.服务端建立监听,等待客户端连接 class Program { static void Main(string[] args) { TcpListener listener = new TcpLi ...

  5. 互联网寒冬下,如何写好一份.NET求职简历?附带简洁免费的简历模板!!!

    前言 在当今互联网行业的寒冬时期,每一位求职者都面临着更为激烈的竞争环境,如何在众多.NET候选人中脱颖而出,成为企业心仪的对象,用心准备一份简历显得尤为重要.简历不仅是个人职业经历的简要概述,更是向 ...

  6. 【Matlab】基于KDtree的最近邻搜索和范围搜索

    摘要:介绍Matlab的rangesearch()函数和knnsearch()函数. rangesearch() -- 根据给定k-维数据集,返回指定距离范围内的所有数据点 knnsearch() - ...

  7. 【SfM】Colmap和openMVG尝试记录

    数据集来源:http://www.maths.lth.se/matematiklth/personal/calle/dataset/dataset.html Colmap与openMVG关于SfM稀疏 ...

  8. 鸿蒙WebSocket的使用竟如此简单

    使用WebSocket建立服务器与客户端的双向连接,需要先通过createWebSocket()方法创建WebSocket对象,然后通过connect()方法连接到服务器.当连接成功后,客户端会收到o ...

  9. [tldr] GO泛型编程

    最少的内容简述如何在GO中使用泛型编程 函数泛型 func f[T any](s Set[T]) { } 在函数声明的时候添加一个[]作为泛型的说明, 在使用的时候是可以自动推断 很多时候, any的 ...

  10. dxTabbedMDIManager1关闭窗体

    procedure TfrmJianKongXinXi.FormClose(Sender: TObject; var Action: TCloseAction);begin Action:=caFre ...