时移世易,篡改天机:吾以 Go 语令 Windows 文件“返老还童“记
文曰:
时维乙巳年秋,有程序员夜不能寐,忽见文件夹之创建时间,竟露马脚,惶惶如临大敌。遂执 Go 语为剑,调 Windows 之秘术,篡改天机,令旧物焕新,恍若昨日初生。此事奇诡,不可不记。
一、缘起:文件夹"露馅"了!
话说某日,某君正在调试一"压测提单工具"(此乃江湖秘器,专治系统慢如龟),忽觉不妥——
文件夹创建时间竟为今日!
此乃大忌!
若被 QA 看见,必问:“汝昨日方写此工具?何以昨日已用之?”
若被老板察觉,恐遭诘问:“此工具尚未完成,何以三日前已有日志?”
呜呼!时间即证据,证据即破绽。
欲掩其迹,非改其时不可!
然,Linux 之下,touch -t 一令可解;
Windows 之境,却无此等神技。
无奈,只得亲自动手,以代码为笔,重写天命。
二、探秘:Windows 之"三时"真言
Windows 文件,藏有三时,谓之:
CreationTime:诞生之刻,如婴儿初啼;
LastAccessTime:最近一瞥,似故人回眸;
LastWriteTime:最后修改,若匠人收工。
此三时,皆存于 Win32FileAttributeData 之中,非寻常 os.Stat 可窥全貌。
若欲篡之,须得:
打开文件之"魂"(句柄);
转时间于 FILETIME(此乃 Windows 独门格式,自 1601 年起算,单位百纳秒,玄之又玄);
调 SetFileTime 之秘术,一举改写三时。
此术凶险,稍有不慎,轻则无效,重则系统怒而拒之。
然吾有 Go 语言为盾,syscall 为矛,何惧之有?
三、铸剑:Go 语炼成"时之刃"
于是,吾挥毫泼墨,写下三式神功:
第一式:遍历万文件(GetAllFiles)
// 递归入幽谷,遍历子子孙孙,凡文件者,皆录其名。
func GetAllFiles(dirPath string) ([]string, error) { ... }
此式如扫地僧,默默无闻,却为后文铺路。
第二式:窥天机(PrintFileTime)
// 仰观文件之三时,如观星象,知其生辰八字。
func PrintFileTime(filePath string) { ... }
初看文件,三时毕现,心中有数,方敢下手。
第三式:篡天命(SetFileTime)
// 执时间之笔,重写 Creation、Access、Write 三命。
func SetFileTime(filePath string, ctime, atime, mtime time.Time) error { ... }
此乃核心!调 CreateFile 得其魂,转 time.Time 为 FILETIME,终以 SetFileTime 一锤定音。
注:此术仅通于 Windows。若于 Linux 行之,恐如对牛弹琴,徒增笑耳。
四、施法:令"压测工具"返老还童
主函数中,吾设三处要地:
主目录:D:\tools\压测提单工具 → 设为 2022-02-28 10:27:16
日志目录 runlog → 设为 2022-02-28 11:27:16
通信日志 socketlog → 设为 2022-02-28 11:27:13
先观其旧时,再施法篡改,复观其新时——
果然!三时皆如吾所愿,分毫不差!
文件夹静立如初,却已"穿越"三年,恍若昨日方建。
QA 再查,只见"2022年旧物",必叹曰:“此乃古董级工具,稳定可靠!”
老板见之,亦抚掌笑曰:“此君早有准备,深谋远虑!”
五、警世恒言
然,吾虽得意,亦不敢忘形。
篡改时间,乃双刃之剑:
若为测试、演示、归档所用,善莫大焉;
若为欺瞒、伪造、逃责所图,则天理难容!
且此术仅限 Windows,Linux 诸君莫急,自有 touch、debugfs 等妙法,另文再叙。
六、结语:代码即道,慎用神通
太史公曰:
时者,天地之过客;文件者,代码之遗迹。
能窥其时,改其命,非为逆天,实为顺事。
然顺事者,亦当守其道——技术无善恶,人心定乾坤。
今录此 Go 语真经,非教人作伪,实授人以"可控之权"。
愿诸君执此剑,不为诡道,而为清道。
附:完整代码
package mainimport ("fmt""io/ioutil""os""syscall""time"
)// GetAllFiles 递归获取指定目录下所有文件的完整路径(不包括子目录本身)
func GetAllFiles(dirPath string) ([]string, error) {var files []string// 读取目录内容dirEntries, err := ioutil.ReadDir(dirPath)if err != nil {return nil, fmt.Errorf("无法读取目录 %q: %w", dirPath, err)}for _, entry := range dirEntries {fullPath := dirPath + "/" + entry.Name()if entry.IsDir() {// 递归处理子目录subFiles, err := GetAllFiles(fullPath)if err != nil {return nil, fmt.Errorf("递归读取子目录 %q 失败: %w", fullPath, err)}files = append(files, subFiles...)} else {// 是普通文件,加入结果列表files = append(files, fullPath)}}return files, nil
}// SetFileTime 修改文件的创建时间(CreationTime)、最后访问时间(LastAccessTime)和最后修改时间(LastWriteTime)
// 注意:此函数仅适用于 Windows 系统
func SetFileTime(filePath string, creationTime, lastAccessTime, lastWriteTime time.Time) error {// 获取文件的绝对路径absPath, err := syscall.FullPath(filePath)if err != nil {return fmt.Errorf("无法获取文件绝对路径 %q: %w", filePath, err)}// 转换为 UTF-16 指针(Windows API 要求)pathPtr, err := syscall.UTF16PtrFromString(absPath)if err != nil {return fmt.Errorf("路径转 UTF-16 失败: %w", err)}// 打开文件句柄(仅用于修改属性)handle, err := syscall.CreateFile(pathPtr,syscall.FILE_WRITE_ATTRIBUTES,syscall.FILE_SHARE_WRITE,nil,syscall.OPEN_EXISTING,syscall.FILE_FLAG_BACKUP_SEMANTICS,0,)if err != nil {return fmt.Errorf("无法打开文件 %q: %w", absPath, err)}defer syscall.Close(handle)// 将 time.Time 转换为 Windows FILETIME 格式toFiletime := func(t time.Time) syscall.Filetime {return syscall.NsecToFiletime(t.UnixNano())}ct := toFiletime(creationTime)at := toFiletime(lastAccessTime)mt := toFiletime(lastWriteTime)// 调用 Windows API 设置时间戳if err := syscall.SetFileTime(handle, &ct, &at, &mt); err != nil {return fmt.Errorf("设置文件时间失败 %q: %w", absPath, err)}return nil
}// PrintFileTime 打印 Windows 系统下文件的创建时间、最后访问时间和最后修改时间
func PrintFileTime(filePath string) {fileInfo, err := os.Stat(filePath)if err != nil {fmt.Printf("无法获取文件信息 %q: %v\n", filePath, err)return}// 类型断言为 Windows 特有的文件属性结构winAttr, ok := fileInfo.Sys().(*syscall.Win32FileAttributeData)if !ok {fmt.Printf("不支持的系统或文件类型: %q\n", filePath)return}// FILETIME 是从 1601-01-01 起的 100 纳秒单位,转换为 Unix 时间creationTime := time.Unix(0, winAttr.CreationTime.Nanoseconds())lastAccessTime := time.Unix(0, winAttr.LastAccessTime.Nanoseconds())lastWriteTime := time.Unix(0, winAttr.LastWriteTime.Nanoseconds())fmt.Printf("文件路径: %s\n", filePath)fmt.Printf(" 创建时间: %v\n", creationTime)fmt.Printf(" 最后访问时间: %v\n", lastAccessTime)fmt.Printf(" 最后修改时间: %v\n", lastWriteTime)
}// parseLocalTime 安全地将字符串解析为本地时区的 time.Time
func parseLocalTime(layout, value string) (time.Time, error) {return time.ParseInLocation(layout, value, time.Local)
}func main() {// 示例:修改目录及其子目录中特定文件夹的时间戳// 1. 修改主目录时间dirPath := `D:\tools\压测提单工具`PrintFileTime(dirPath)t1, err := parseLocalTime("2006-01-02 15:04:05", "2022-02-28 10:27:16")if err != nil {fmt.Printf("时间解析失败: %v\n", err)return}if err := SetFileTime(dirPath, t1, t1, t1); err != nil {fmt.Printf("设置目录时间失败: %v\n", err)return}PrintFileTime(dirPath)// 2. 修改 runlog 子目录时间runlogPath := `D:\tools\压测提单工具\runlog`PrintFileTime(runlogPath)t2, err := parseLocalTime("2006-01-02 15:04:05", "2022-02-28 11:27:16")if err != nil {fmt.Printf("时间解析失败: %v\n", err)return}if err := SetFileTime(runlogPath, t2, t2, t2); err != nil {fmt.Printf("设置 runlog 目录时间失败: %v\n", err)return}PrintFileTime(runlogPath)// 3. 修改 socketlog 子目录时间socketlogPath := `D:\tools\压测提单工具\socketlog`PrintFileTime(socketlogPath)t3, err := parseLocalTime("2006-01-02 15:04:05", "2022-02-28 11:27:13")if err != nil {fmt.Printf("时间解析失败: %v\n", err)return}if err := SetFileTime(socketlogPath, t3, t3, t3); err != nil {fmt.Printf("设置 socketlog 目录时间失败: %v\n", err)return}PrintFileTime(socketlogPath)
}
运行前,请三思:汝,真需"返老还童"乎?
乙巳年霜月朔日,某程序员焚香沐浴,记于键盘之侧。
往期部分文章列表
- golang圆阵列图记:天灵灵地灵灵图标排圆形
- golang解图记
- 从 4.8 秒到 0.25 秒:我是如何把 Go 正则匹配提速 19 倍的?
- 用 Go 手搓一个内网 DNS 服务器:从此告别 IP 地址,用域名畅游家庭网络!
- 我用Go写了个华容道游戏,曹操终于不用再求关羽了!
- 用 Go 接口把 Excel 变成数据库:一个疯狂但可行的想法
- 穿墙术大揭秘:用 Go 手搓一个"内网穿透"神器!
- 布隆过滤器(go):一个可能犯错但从不撒谎的内存大师
- 自由通讯的魔法:Go从零实现UDP/P2P 聊天工具
- Go语言实现的简易远程传屏工具:让你的屏幕「飞」起来
- 当你的程序学会了"诈尸":Go 实现 Windows 进程守护术
- 验证码识别API:告别收费接口,迎接免费午餐
- 用 Go 给 Windows 装个"顺风耳":两分钟写个录音小工具
- 无奈!我用go写了个MySQL服务
- 使用 Go + govcl 实现 Windows 资源管理器快捷方式管理器
- 用 Go 手搓一个 NTP 服务:从"时间混乱"到"精准同步"的奇幻之旅
- 用 Go 手搓一个 Java 构建工具:当 IDE 不在身边时的自救指南
- 深入理解 Windows 全局键盘钩子(Hook):拦截 Win 键的 Go 实现
- 用 Go 语言实现《周易》大衍筮法起卦程序
- Go 语言400行代码实现 INI 配置文件解析器:支持注释、转义与类型推断
- 高性能 Go 语言带 TTL 的内存缓存实现:精确过期、自动刷新、并发安全
- Golang + OpenSSL 实现 TLS 安全通信:从私有 CA 到动态证书加载
