模板配置

跟着网上的教程使用evilashz师傅的模板,下载模板解压至vs的模板目录:

%UserProfile%\Documents\Visual Studio 2022\Templates\ProjectTemplates

创建新项目选择刚刚新增的类型:Beacon Object File​。

环境适配

生成时报错,我使用的是2022版本的,模板有点老了他这里的是vs2019。

根据底下的提示从项目​ -> 重定目标解决方案​, 接着确定更新即可

但是一进来模板会报错没有引入库, 干脆就用最小测试代码:将 Source.cpp​重名为Source.c​并修改为如下:

#include <stdio.h>
#include <Windows.h>
#include "beacon.h"
void go(char* buff, int len) {
BeaconPrintf(CALLBACK_OUTPUT, "Hello BOF");
}

编译配置

在上方的生成中勾选BOF配置, 配置管理器的编译环境也一样的,就可以生成64位版本的。

但最好还是使用批生成同时生成32位和64位版本:生成​ -> 批生成​ 在BOF那两项勾选Win32​和x64​。

创建项目时没有勾选将解决方案和项目放在同一目录下​,那么生成的.obj​文件(编译未链接的目标文件)就在/bin/BOF​中。

测试如果用cs的话可以使用inline-execute E:\TARGET\timestamp.obj​。我这里执行成功但发现有乱码:

乱码问题

尝试了加上\n​换行来终止字符串刷新缓冲区但是不行,找到使用格式化输出宏的办法,将可变参数展开。比如这里的INFO_FORMAT("Hello BOF");​会被展开成BeaconPrintf(CALLBACK_OUTPUT, "[*] Hello BOF\n");​。

#include <stdio.h>
#include <Windows.h>
#include "beacon.h" #define INFO_FORMAT(fmt, ...) BeaconPrintf(CALLBACK_OUTPUT, "[*] " fmt "\n", ##__VA_ARGS__) void go(char* buff, int len) {
INFO_FORMAT("Hello BOF");
}

原先的内存中可能是这样:"Hello BOF" <未知内存内容>​,但使用宏之后就是这样的:"[*] Hello BOF\n" <确定的字符串终止>​,最后测试也没有乱码了。

功能实现

实现一个修改文件时间戳的功能, BOF不能直接调用Windows API, 而是通过cs提供的函数来交互。但我这里并不是为cs编写,所以要使用Windows API函数的话,首先需要进行声明:

Windows API声明

要修改文件时间戳, 就要用到SetFileTime​。它用于设置文件的创建时间、访问时间和修改时间。文档中原型如下:

BOOL SetFileTime(
[in] HANDLE hFile,
[in, optional] const FILETIME *lpCreationTime,
[in, optional] const FILETIME *lpLastAccessTime,
[in, optional] const FILETIME *lpLastWriteTime
);

  • hFile: 文件句柄,必须有FILE_WRITE_ATTRIBUTES访问权限
  • lpCreationTime: 文件的创建时间
  • lpLastAccessTime: 文件的最后访问时间
  • lpLastWriteTime: 文件的最后修改时间

那么在bof的声明中要注意这个函数是属于哪个dll, 比如这里是kernel32.dll​的话那要定义和调用它时就写成KERNEL32$SetFileTime​,完整如下:

DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetFileTime(HANDLE, const FILETIME*, const FILETIME*, const FILETIME*);

cs使用这种前缀可以让BOF直接调用DLL中的原生函数, 就不需要再在导入表中声明了,这样也可以缩小BOF体积。类似的使用CreateFileA​来创建或打开文件时, 其原型如下:

HANDLE CreateFileA(
[in] LPCSTR lpFileName, // 文件名
[in] DWORD dwDesiredAccess, // 访问模式
[in] DWORD dwShareMode, // 共享模式
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符
[in] DWORD dwCreationDisposition, // 创建方式
[in] DWORD dwFlagsAndAttributes, // 文件属性
[in, optional] HANDLE hTemplateFile // 模板文件句柄
);

BOF中声明则如下:

DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileA(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);

以及其他要用到的api可以这样声明:

// 其他必要的API
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle(HANDLE); // 关闭一个内核对象(如文件)的句柄
DECLSPEC_IMPORT VOID WINAPI KERNEL32$GetSystemTime(LPSYSTEMTIME); // 获取当前系统时间(UTC时间)
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SystemTimeToFileTime(LPSYSTEMTIME, LPFILETIME); // 将SYSTEMTIME结构转换为FILETIME结构。

参数处理

BOF的入口函数就是这里的go, inline-execute​执行BOF时先调用这个。其中先定义并初始化一个解析器来解析传入的参数,timestamp​这个至少也是要一个参数路径的,先从一个来:

void go(char* buff, int len) {
datap parser;
char* filepath; // 解析Beacon传入的参数
BeaconDataParse(&parser, buff, len);
filepath = BeaconDataExtract(&parser, NULL); // 参数验证
if (!filepath) {
BeaconPrintf(CALLBACK_ERROR, "[-] please provide file path");
return;
}
}

那解析多个参数呢, 一样的:

BeaconDataParse(&parser, buff, len);
sourceFile = BeaconDataExtract(&parser, NULL);
targetFile = BeaconDataExtract(&parser, NULL); if (!sourceFile || !targetFile) {
BeaconPrintf(CALLBACK_ERROR, "[!] Error: Two file paths required\n");
BeaconPrintf(CALLBACK_ERROR, "[-] Usage: inline-execute timestamp.o \"source_file\" \"target_file\"\n");
return;
} BeaconPrintf(CALLBACK_OUTPUT, "[-] Source: %s\n", sourceFile);
BeaconPrintf(CALLBACK_OUTPUT, "[-] Target: %s\n", targetFile);

时间处理

接着继续,获取系统时间然后修改成我们希望的时间,比如2020年1月1日 00:00:00​。然后把他转换为文件时间格式:

SYSTEMTIME st;
FILETIME ft;
KERNEL32$GetSystemTime(&st); st.wYear = 2020;
st.wMonth = 1;
st.wDay = 1;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0; KERNEL32$SystemTimeToFileTime(&st, &ft);

文件操作

准备好了要修改的时间后就尝试打开文件获取句柄:

HANDLE hFile = KERNEL32$CreateFileA(
filepath, // 文件路径
FILE_WRITE_ATTRIBUTES, // 只需要写属性权限
FILE_SHARE_READ | FILE_SHARE_WRITE, // 允许其他进程读写
NULL, // 默认安全属性
OPEN_EXISTING, // 只打开已存在的文件
FILE_ATTRIBUTE_NORMAL, // 使用标准属性
NULL // 不使用模板
); if (hFile == INVALID_HANDLE_VALUE) {
BeaconPrintf(CALLBACK_ERROR, "[-] can not open file: %s", filepath);
return;
}

时间戳修改

最后使用SetFileTime​修改三个时间属性:创建时间、访问时间、修改时间。结束后关闭句柄。

if (!KERNEL32$SetFileTime(hFile, &ft, &ft, &ft)) {
BeaconPrintf(CALLBACK_ERROR, "[-] failed to change timestamp");
} else {
BeaconPrintf(CALLBACK_OUTPUT, "[+] success: %s", filepath);
} KERNEL32$CloseHandle(hFile);

这样就简单完成了修改一个文件时间戳的功能,完整代码如下:

#include <stdio.h>
#include <Windows.h>
#include "beacon.h" // 声明Windows API函数
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetFileTime(HANDLE, const FILETIME*, const FILETIME*, const FILETIME*);
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileA(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle(HANDLE);
DECLSPEC_IMPORT VOID WINAPI KERNEL32$GetSystemTime(LPSYSTEMTIME);
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SystemTimeToFileTime(LPSYSTEMTIME, LPFILETIME); void go(char* buff, int len) {
datap parser;
char* filepath; BeaconDataParse(&parser, buff, len);
filepath = BeaconDataExtract(&parser, NULL); if (!filepath) {
BeaconPrintf(CALLBACK_ERROR, "[-] please provide file path");
return;
} SYSTEMTIME st;
FILETIME ft;
KERNEL32$GetSystemTime(&st); st.wYear = 2020;
st.wMonth = 1;
st.wDay = 1;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0; KERNEL32$SystemTimeToFileTime(&st, &ft); HANDLE hFile = KERNEL32$CreateFileA(
filepath,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
); if (hFile == INVALID_HANDLE_VALUE) {
BeaconPrintf(CALLBACK_ERROR, "[-] can not open file: %s", filepath);
return;
} if (!KERNEL32$SetFileTime(hFile, &ft, &ft, &ft)) {
BeaconPrintf(CALLBACK_ERROR, "[-] failed to change timestamp");
}
else {
BeaconPrintf(CALLBACK_OUTPUT, "[+] sunccess: %s", filepath);
} KERNEL32$CloseHandle(hFile);
}

测试

编译还是同上使用批生成,我这里测试的可以成功修改:

优化编译

为了更好的在苛刻环境下使用,我想继续压缩体积,找到的参数以及解释如下:

  • -Os: 优化大小(比-O2生成更小的代码)
  • -fno-asynchronous-unwind-tables: 禁用异常展开表
  • -fno-ident: 删除编译器版本信息
  • -fpack-struct=8: 结构体8字节对齐
  • -falign-functions=1: 函数1字节对齐
  • -s: 删除符号表
  • -ffunction-sections: 每个函数放入单独的段
  • -fdata-sections: 每个数据项放入单独的段
  • -fno-exceptions: 禁用异常处理
  • -fno-stack-protector: 禁用栈保护
  • -mno-stack-arg-probe: 禁用栈探测

64位使用的编译命令如下:

x86_64-w64-mingw32-gcc-8.1.0.exe -c .\Source.c -o timestamp.o -Os -fno-asynchronous-unwind-tables -fno-ident -fpack-struct=8 -falign-functions=1 -s -ffunction-sections -fdata-sections -fno-exceptions -fno-stack-protector -mno-stack-arg-probe

针对于编译32位版本的命令( 如果没有就用批生成, 重命名即可):

i686-w64-mingw32-gcc-8.1.0.exe -c .\Source.c -o timestamp.x86.o -Os -fno-asynchronous-unwind-tables -fno-ident -fpack-struct=8 -falign-functions=1 -s -ffunction-sections -fdata-sections -fno-exceptions -fno-stack-protector -mno-stack-arg-probe

注:这里生成的是.o而不是.obj只是自己的需求为了统一一下,obj是Windows平台的默认目标文件扩展名,而.o是Unix/Linux平台的扩展名。它们本质和功能上是一样的,只是命名习惯不同。

最后

这里只是简单的示例,要使用最好要有一个锚定文件,以他的时间作为目标来修改。细节不赘述,详细请跳转Github。最终版本的使用测试如下:

参考

BOF编写-修改时间戳的更多相关文章

  1. 学习Shell脚本编程(第2期)_编写修改权限及执行Shell程序的步骤

    编写Shell程序 执行Shell程序 Shell程序有很多类似C语言和其他程序设计语言的特征,但是又没有程序语言那样复杂.Shell程序是指放在一个文件中的一系列Linux命令和实用程序.在执行的时 ...

  2. linux下修改时间戳

    Linux下touch是一个非常有用的命令. touch语法结构如下: touch [-acfm][-d <日期时间>][-r <参考文件或目录>][-t <日期时间&g ...

  3. 创建文件和修改时间戳——touch

    linux的touch命令不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件. 1.命令格式: touch [选项]... 文件... 2.命令参数: -a    ...

  4. 2、编写/修改权限及执行Shell程序的步骤

    学习目标编写Shell程序执行Shell程序 正文Shell程序有很多类似C语言和其他程序设计语言的特征,但是又没有程序语言那样复杂.Shell程序是指放在一个文件中的一系列Linux命令和实用程序. ...

  5. Nginx修改时间戳

    1.安装nginx,注意不要安装nginx-common或者nginx-full sudo apt-get install nginx sudo apt-get install nginx-commo ...

  6. 安卓脱壳&&协议分析&&burp辅助分析插件编写

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 前言 本文以一个 app 为例,演示对 app脱壳,然后分析其 协 ...

  7. 学习 Linux,101: 自定义或编写简单脚本【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-lpic1-105-2/index.html 学习如何使用标准的 shell 语法.循环和控制结构,以及 ...

  8. 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference AtomicReference无法解决上述问题的根 ...

  9. SQL时间戳的使用

    SQL时间戳的使用 一直对时间戳这个概念比较模糊,相信有很多朋友也都会误认为:时间戳是一个时间字段,每次增加数据时,填入当前的时间值.其实这误导了很多朋友. 1.基本概念 时间戳:数据库中自动生成的唯 ...

  10. ADO.NET(完整修改和查询、实体类,数据访问类)

    一.完整修改和查询 在编写c#语句时需考虑到用户体验,例如在编写修改语句时,需要考虑到输入的内容在数据库中是否能够找到. 中间变量运用. 1.先查 2.执行操作 完整修改语句: bool has = ...

随机推荐

  1. 函数(C语言)

    目录 1. 函数的概念 2. 库函数 2.1 标准库和头文件 2.2 库函数的使用方法 3. 自定义函数 3.1 函数的语法形式 3.2 函数的举例 4. 形参和实参 4.1 实参 4.2 形参 4. ...

  2. Power BI 通过输入数据新建表后重新进入编辑状态

    在使用Power BI时,有时候我们会直接通过输入数据构建一些简单的表,但是构建好后我们可能还需要对表格进行增删改的操作,这时候我们需要怎么才会恢复到表格的编辑状态呢?其实很简单,我们回到PQ里面,双 ...

  3. 谷歌chrome浏览器大量书签消失,怎么恢复历史?

    作者:jdjdjdh链接:https://www.zhihu.com/question/400424237/answer/1604383205来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非 ...

  4. 全球最大分类广告商的Karpenter实践:减负运维、减少中断、每月省21万(上)

    原文链接: https://medium.com/adevinta-tech-blog/the-karpenter-effect-redefining-our-kubernetes-operation ...

  5. PythonDay3Advance

    PythonDay3Advance 运算符 位运算符 进制: 将整数分了几种进制表示法 二进制:由0,1构成,逢2进1,以0b开头 八进制:由0,1,2,3,4,5,6,7构成,逢8进1,以0开头 十 ...

  6. uni-app 坑

    1.fixed定位 在H5中,tabbar,顶部导航栏,系统状态栏(手机信号,电量显示等)包含在内容区,H5在定位时,需要算上这些高度(如果页面中存在这个元素的话) 解决办法:使用条件编译,针对不同的 ...

  7. DTL事务控制语言--sql事务

    DTL事务控制语言体格sql语句就是一个事务事务可以保证 一组sql语句要么都成功,要么都失败默认自动提交一可以关闭 set autocommit=0关闭自动提交最后 插入或者修改时 只有commit ...

  8. SEEDLab —— 环境变量与 Set-UID 实验

    [软件安全]实验1--环境变量与 Set-UID 实验 Task 1:配置环境变量 使用printenv或env指令来打印环境变量: ​ 如果只想打印特定的环境变量,如PWD变量,可以使用printe ...

  9. 纯JS+CSS实现羊了个羊

    前言 省流 gitee上扒的,感觉还不错,拿下来玩玩. https://gitee.com/kenxq/ylgy.git 技术说明 纯JS+CSS实现羊了个羊,包含部分特效,响应式手机.电脑.ipad ...

  10. windows下fopen,fclose断电数据丢失解决办法

    #include <io.h> //在fclose之前调用fflush和_commit,可以有效防止断电数据丢失 fflush(file); _commit(fileno(file)); ...