news 2026/9/15 17:14:18

Garnet 专属命令完全指南:ETag 条件更新、成员级过期与运维扩展命令详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Garnet 专属命令完全指南:ETag 条件更新、成员级过期与运维扩展命令详解

Garnet 专属命令完全指南:ETag 条件更新、成员级过期与运维扩展命令详解

【免费下载链接】garnetGarnet is a remote cache-store from Microsoft Research that offers strong performance (throughput and latency), scalability, storage, recovery, cluster sharding, key migration, and replication features. Garnet can work with existing Redis clients.项目地址: https://gitcode.com/GitHub_Trending/garnet4/garnet

本指南系统讲解微软 Garnet 中独有的、标准 RESP 服务器不具备的专属命令(Garnet-specific commands),涵盖 AOF 手动提交(COMMITAOF)、按需过期清理(HCOLLECT/ZCOLLECT/EXPDELSCAN)、自定义对象扫描(COSCAN)、自定义命令与事务注册(REGISTERCS/RUNTXP/MODULE LOADCS)、ETag 条件读写命令族(SETWITHETAG/SETIFMATCH等)以及 Sorted Set 成员级过期命令族(ZEXPIRE/ZTTL/ZPERSIST等)。读完本文,你将掌握这些命令的完整语法、返回语义、源码级实现原理与正确使用边界,可直接在现有 Redis 客户端接入的 Garnet 实例中实战使用。

说明:本文以 garnet-specific.md 为骨架,结合仓库源码与测试(如 BasicEtagCommands.cs、AdminCommands.cs、SortedSetCommands.cs)进行原理级扩充。除 服务器扩展 机制外,Garnet 还支持大量其他 RESP 服务器没有的 API 调用,下面逐一介绍。

一、运维与持久化类命令

COMMITAOF:手动提交追加日志

语法
COMMITAOF [DBID]

COMMITAOF对 Garnet 实例中所有活跃数据库的追加式文件(append-only-file, AOF)发起一次手动提交。当自动提交被关闭时,若你需要在特定时间点强制系统落盘,此命令非常有用。如果指定了DBID,则只对该指定数据库的 AOF 发起手动提交。

Resp 回复

Simple string reply:OK(实现层面实际写回的是AOF file committed简单字符串)。

实现细节

从源码看,NetworkCOMMITAOF 的逻辑非常直接:参数最多接受 1 个,默认dbId = -1表示提交所有活跃数据库;若指定了 DBID 则通过TryParseDatabaseId解析。由于该操作在网络线程上执行,源码中通过AsyncUtils.BlockingWait(CommitAofAsync(dbId))阻塞等待异步提交完成后再返回。因此,COMMITAOF返回即代表对应 AOF 提交已真正完成,适合在测试、备份或关闭前的确定性落盘场景使用。

EXPDELSCAN:按需扫描并删除过期键

语法
EXPDELSCAN [dbid]

EXPDELSCAN扫描存储的 mutable region,并按需删除所有已过期键。它的典型价值在于:你可以自己掌控过期删除的时机(例如接入一个与你的工作负载模式匹配的调度器),而不必依赖后台扫描线程。

  • dbid选择要扫描的逻辑数据库(默认0)。

重要限制:当后台过期键删除扫描被启用时,本命令会被拒绝——即当ExpiredKeyDeletionScanFrequencySecs(命令行参数--expired-key-deletion-scan-freq)大于0时不可用。也就是说,EXPDELSCAN与后台过期扫描是互斥的两种模式。

Resp 回复
  • Array reply:两个整数——第一个是已删除的过期记录数,第二个是被扫描的记录数。
  • Error reply:ERR Cannot execute EXPDELSCAN with background expired key deletion scan enabled
实现细节

源码 NetworkEXPDELSCAN 首先检查运行时配置ServerConfigType.EXPIRED_KEY_DELETION_SCAN_FREQ是否大于 0,大于 0 则直接以RESP_ERR_EXPDELSCAN_INVALID报错;否则调用storeWrapper.ExpiredKeyDeletionScan(dbId)执行扫描,并将(recordsExpired, recordsScanned)以长度 2 的数组回复。该命令与配置项ExpiredKeyDeletionScanFrequencySecs--expired-key-deletion-scan-freq)的联动行为,在 ExpiredKeyDeletionTests.cs 中有完整测试覆盖。

SECONDARYOF:配置主从关系

SECONDARYOF将一个服务器配置为另一台服务器的 secondary(从),或将其提升为主节点(primary)。其语义与标准命令REPLICAOF完全一致,详见 server.md。在主从复制或集群运维脚本中,两者可互换使用。

二、集合过期成员清理命令

Garnet 的 Hash 与 Sorted Set 对象支持字段/成员级过期,因此提供了按需清理过期字段/成员的专属命令。

HCOLLECT:手动清理 Hash 中过期字段

语法
HCOLLECT key [key ...]

手动触发对指定 Hash 键中过期字段(field)的内存清理。使用*作为 key 可以对所有 Hash 键执行收集清理。

Resp 回复
  • Simple reply:OK
  • Error reply:ERR HCOLLECT scan already in progress
实现细节

从 NetworkHCOLLECT 可以看出,该命令构建RespInputHeader(GarnetObjectType.Hash) { HashOp = HashOperation.HCOLLECT }输入,并调用storageApi.HashCollect(keys, ref input)。根据返回状态分别回复OKWRONGTYPE或“扫描已在进行中”错误。它本质上是利用 Hash 对象的 RMW 逻辑对过期 field 做批量收集,与HSCAN等扫描命令配合可用于维护大 Hash 键的内存占用。

ZCOLLECT:手动清理 Sorted Set 中过期成员

语法
ZCOLLECT key [key ...]

手动触发对指定 Sorted Set 键中过期成员(member)的内存清理。使用*作为 key 可以对所有 Sorted Set 键执行收集清理。

Resp 回复
  • Simple reply:OK
  • Error reply:ERR ZCOLLECT scan already in progress
实现细节

HCOLLECT对称,NetworkZCOLLECT 使用SortedSetOperation.ZCOLLECT操作码并调用storageApi.SortedSetCollect(keys, ref input)。对应的成员过期元数据管理逻辑位于 SortedSetObject.cs,在 SortedSetOps.cs 中完成过期成员的批量移除。注意HCOLLECT/ZCOLLECT的文档原文对两种集合的说明存在笔误(互相复制了描述),实际行为与命令名一一对应:H*处理 Hash,Z*处理 Sorted Set。

三、COSCAN:自定义对象扫描

语法
COSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]

COSCAN(Custom Object Scan)与 HSCAN、SSCAN 类似,用于迭代遍历存储在指定 key 下的自定义对象(custom object)的字段与值

  • MATCH pattern:对从集合中取回的元素应用模式过滤。
  • COUNT count:限制服务器单次返回给客户端的最大元素数量;该限制还会与全局服务器设置中的object-scan-count-limit一起共同生效(即取两者中的约束)。
  • NOVALUES:只返回字段/键而不返回对应的值。

该命令与 Garnet 的自定义对象扩展体系紧密相关:自定义对象通过 CustomObjectBase.cs 及CustomObjectCommand注册后,即可被COSCAN迭代。其解析逻辑可在 RespCommandHashLookupData.cs 与 RespCommand.cs 的命令哈希查找表中查到COSCAN的注册信息。

四、自定义命令与事务扩展命令

Garnet 允许以 C# 实现自定义命令(custom commands)与自定义事务(custom transactions),并以服务器端 DLL 形式动态加载,这一族命令与扩展机制配套使用,详见 自定义命令开发指南。

REGISTERCS:注册 C# 实现的命令与事务

REGISTERCS用于把实现在 C# 中的特定自定义命令和事务注册进服务器端的 DLL 库中。开发者在扩展程序集中用CustomRawStringCommandCustomObjectCommandCustomTransactionProcedure等基类实现逻辑后,通过REGISTERCS加载注册,之后即可像内置命令一样被 RESP 客户端调用。具体流程与示例见 custom-commands.md。

RUNTXP:按 ID 运行自定义事务

语法
RUNTXP txid [args]

运行由txid标识的特定自定义事务过程(transactional procedure)。txid即注册时分配的事务 ID,后续参数args会透传给事务过程。在 RespServerSession.cs 中可以看到RespCommand.RUNTXP => NetworkRUNTXP()的调度入口,事务过程最终由事务管理器 TransactionManager.cs 执行。RUNTXP是构建多键原子操作(如样例扩展 ReadWriteTxn.cs、SampleUpdateTxn.cs)的运行时调用入口。

MODULE LOADCS:加载自包含模块

MODULE LOADCS是 RESP 协议中MODULE LOAD的 Garnet 等价物。它加载一个自包含模块(self-contained module),模块的初始化代码会自动注册其中所有相关的命令与事务,无需再逐个REGISTERCS。从 AdminCommands.cs 的实现可以看到,加载流程为:ModuleUtils.LoadAssemblies加载程序集 →ModuleRegistrar.Instance.LoadModule触发模块初始化并自动注册命令,成功后返回OK。模块化开发细节同样见 custom-commands.md 与 ModuleRegistrar.cs。

五、事务辅助命令:WATCHMS 与 WATCHOS

Garnet 为WATCH提供了两种类型化变体,用于在事务(MULTI/EXEC)中显式声明被监视键的存储类型,让存储层做出更精确的优化:

WATCHMS

语法
WATCHMS key [key ...]

与 WATCH 语义相同,但声明这些 key 持有的是原始字符串值(raw string)。

WATCHOS

语法
WATCHOS key [key ...]

与 WATCH 语义相同,但声明这些 key 持有的是对象值(object,如 Hash、Sorted Set 等)。两者的实现通过监视键版本机制(见 WatchVersionMap.cs 与 WatchedKeySlice.cs)在事务执行前校验版本一致性,避免脏写。

六、ASYNC:大数据访问的异步接口

ASYNC是 Garnet 面向大于内存数据(larger-than-memory data)访问场景提供的异步接口机制。当数据集超过内存容量、需要从磁盘存储层(Tsavorite)按需读取时,传统同步阻塞模型会浪费网络线程,Garnet 通过异步处理管线(见 AsyncProcessor.cs)实现非阻塞的读写,提升大内存数据集场景下的吞吐与延迟表现。它更多是一种客户端/服务器协同的接口约定,适合大 Value 与冷热数据分层场景。

七、Native ETag 支持:字符串键值对的条件更新协议

Garnet 为原始字符串提供了原生的 ETag 支持:任何以字符串为基础结构的 key-value 对插入后,都可以关联一个自动更新的 ETag(初始为 1,此后每次覆盖写入递增)。ETag 的核心价值是实现无锁的乐观并发控制(optimistic concurrency control)——客户端先读取 value 与 ETag,再携带该 ETag 做条件更新,只有 ETag 匹配时才写成功,从而避免“读取-修改-写入”期间的竞态。

初始化方式:使用SETWITHETAG初始化带 ETag 的键值对;也可以对任意已存在的键值对调用SETIFMATCH,并传入 ETag 参数0(任何未显式关联 ETag 的键值对,其隐式 ETag 都是 0)。关于 ETag 的使用场景与设计模式(缓存失效、乐观锁、Web 语义映射等),可参阅博客 etags-when-and-how。

7.1 SETWITHETAG:带 ETag 写入

语法
SETWITHETAG key value [EX seconds | PX milliseconds]

key设为字符串value并关联一个 ETag。若 key 已存在,则覆盖原值:若原值已有 ETag,则ETag 自增 1;否则赋予新 ETag1

选项:

  • EX seconds:以秒为单位设置过期时间(正整数)。
  • PX milliseconds:以毫秒为单位设置过期时间(正整数)。
Resp 回复
  • Integer reply:与该 value 关联的 ETag。
实现细节

NetworkSETWITHETAG 对参数个数有严格校验(2~4 个),EX/PX过期值必须为正整数,否则分别返回语法错误、非整数错误或RESP_ERR_GENERIC_INVALIDEXP_IN_SET。随后由共享实现 ExecuteETagSetCommand 完成写入:过期时间为 0 表示不过期,否则按DateTimeOffset.UtcNow.Ticks加秒/毫秒换算成绝对过期时刻;底层通过storageApi.SET_ETagConditional落库。若目标 key 已被对象类型占用(WRONGTYPE),实现会先用事务提升到排他锁、删除旧对象再执行条件写入,保证类型正确性。

7.2 GETWITHETAG:带 ETag 读取

语法
GETWITHETAG key

返回指定 key 的 value 及其关联的 ETag。

回复
  • Array reply:成功时返回两个元素——第一个是整数 ETag,第二个是 key 的 bulk string value。若该键值对未关联过 ETag,则 ETag 为0
  • Nil reply:key 不存在。

7.3 SETIFMATCH:ETag 匹配才更新

语法
SETIFMATCH key value etag [EX seconds | PX milliseconds] [NOGET]

仅当满足以下任一条件时才以给定的etag写入/更新键值对:(1) 请求中的 etag 与已存在的 etag 匹配;(2) 原本不存在该值;(3) 已存在的值未关联任何 etag 且请求 etag 为0。这正是“比较并交换”(compare-and-swap)语义,是乐观锁的核心原语。

选项:

  • EX seconds:以秒为单位设置过期时间(正整数)。
  • PX milliseconds:以毫秒为单位设置过期时间(正整数)。
  • NOGET:即使 etag 不匹配,也不返回当前值(节省带宽)。
回复
  • Array reply:若请求 etag 与现有 etag 匹配,返回数组,第一个元素是更新后的 etag,第二个元素为 nil;若不匹配,数组依次为最新 etag 与最新 value。

7.4 SETIFGREATER:ETag 更大才更新

语法
SETIFGREATER key value etag [EX seconds | PX milliseconds] [NOGET]

仅当以下任一条件成立时才写入:(1) 请求 etag大于已存在的 etag;(2) 原本不存在该值;(3) 已存在的值未关联 etag 且请求 etag 大于0。这提供了单调递增的“版本推进”语义,适合时钟或序号驱动的同步场景。

选项:

  • EX seconds/PX milliseconds:同上。
  • NOGET:即使请求 etag 不大于现有 etag,也不返回当前值。
回复
  • Array reply:若请求 etag 大于现有 etag,返回数组,第一个元素是更新后的 etag,第二个为 nil;若小于等于现有 etag,则数组为最新 etag 与最新 value。

条件更新族(SETIFMATCH/SETIFGREATER)在 NetworkSetETagConditional 中统一实现:允许 3~6 个参数,依次解析NOGETEX/PX组合,且 etag 必须为不小于 0 的长整数(否则返回RESP_ERR_INVALID_ETAG)。两个命令共用ExecuteETagSetCommandSET_ETagConditional存储路径,仅条件判定方向不同。

7.5 GETIFNOTMATCH:ETag 变化才返回

语法
GETIFNOTMATCH key etag

若 key 关联的 ETag 已变化则返回 value;否则返回“无变化”指示。这是高效的“拉取变更”原语:客户端携带已知 ETag,服务端只在版本推进时回传数据,避免重复传输未变更内容。

回复
  • Array reply:若 ETag 不匹配,返回两个元素——第一个是最新 ETag,第二个是该 key 的 value;若 ETag 匹配(未变化),第一个元素为当前 etag,第二个为 nil。
  • Nil reply:key 不存在。

7.6 DELIFGREATER:ETag 更大才删除

语法
DELIFGREATER key etag

仅当提供的 ETag严格大于该 key 现有 ETag 时才删除该 key。

回复
  • Integer reply:删除成功返回1,否则返回0
实现细节

NetworkDELIFGREATER 中,条件删除并非存储层原生能力,而是借助StringInput构造条件 DEL 请求,调用storageApi.DEL_ETagConditional走 RMW(Read-Modify-Write)路径,在读取阶段完成 etag 比较后再决定是否删除,返回删除计数 1 或 0。etag 为负数时返回RESP_ERR_INVALID_ETAG

7.7 与普通命令的兼容性约束(重要)

当前限制:Cluster(集群)模式下暂不支持 ETag 命令,官方计划后续支持。

:::warning 关键:必须做键分区 所有非 ETag 命令(SETGETAPPENDINCRMSETBITOPRENAME等)对 ETag 是完全无感知的:它们不读取、不校验、不更新、也不保留 ETag。

用户必须对键做分区管理:ETag 管理的键上只能使用ETag 命令(SETWITHETAGGETWITHETAGSETIFMATCHSETIFGREATERGETIFNOTMATCHDELIFGREATER)。

对同一个键混用 ETag 与非 ETag 命令(例如对SETWITHETAG创建的键再执行SET)会导致未定义的 ETag 行为——ETag 可能丢失、过期或损坏。这是出于极致性能的设计取舍:非 ETag 命令为 ETag 功能付出零开销。 :::

正确用法:

# ETag 键 —— 只用 ETag 命令 SETWITHETAG etag:user:1 "data" # 初始化并写入 ETag GETWITHETAG etag:user:1 # 带 ETag 读取 SETIFMATCH etag:user:1 "new" 1 # 基于 ETag 的条件更新 # 非 ETag 键 —— 用普通命令 SET user:2 "data" GET user:2 APPEND user:2 " more"

错误用法(未定义行为):

SETWITHETAG mykey "data" # 写入 ETag SET mykey "other" # ETag 行为未定义! GETWITHETAG mykey # 可能返回过期/缺失的 ETag

建议以明确的键前缀(如etag:)隔离两类键空间。ETag 命令族的完整行为在 RespEtagTests.cs 中有系统化测试,涉及读写、条件匹配/不匹配、过期、类型错误等场景。

八、Sorted Set 成员级过期命令族

Garnet 为 Sorted Set成员提供独立的过期控制(区别于键级过期),可以精确到单个 member。所有命令都以MEMBERS关键字声明后续成员数量与成员列表,一次可批量操作多个成员。

8.1 ZEXPIRE / ZPEXPIRE / ZEXPIREAT / ZPEXPIREAT:设置成员过期

语法
ZEXPIRE key seconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...] ZEXPIREAT key unix-time-seconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...] ZPEXPIRE key milliseconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...] ZPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...]

为 Sorted Set key 的一个或多个成员设置超时:ZEXPIRE/ZPEXPIRE为相对时间(秒/毫秒),ZEXPIREAT/ZPEXPIREAT为绝对时间(Unix 时间戳,秒/毫秒)。超时到期后,成员会被自动删除。

条件选项(四者互斥):

  • NX:仅对没有过期时间的成员设置过期。
  • XX:仅对已有过期时间的成员设置过期。
  • GT:仅当新过期时间大于当前过期时间时设置。
  • LT:仅当新过期时间小于当前过期时间时设置。
Resp 回复

Array reply:对每个成员分别返回——

  • 1:成功设置超时;
  • 0:成员不存在;
  • -1:因条件不满足而未设置超时。
实现细节

SortedSetExpire 是这四个命令的统一实现:严格校验参数结构(MEMBERS关键字必须出现、nummembers必须与后续成员数量一致),将相对时间换算为绝对过期时刻(ZEXPIREUtcNow.AddSecondsZPEXPIREAddMillisecondsZEXPIREAT/ZPEXPIREAT用 Unix 时间戳转 ticks),连同条件选项一起编码进ExpirationWithOption,最终通过SortedSetOp.ZEXPIRE操作码与storageApi.SortedSetExpire写入对象存储。成员不存在(NOTFOUND)时按成员数量返回对应个数的-2

8.2 ZTTL / ZPTTL:查询成员剩余存活时间

语法
ZTTL key MEMBERS nummembers member [member ...] ZPTTL key MEMBERS nummembers member [member ...]

返回指定 Sorted Set 成员的剩余存活时间:ZTTL以秒为单位,ZPTTL以毫秒为单位。

Resp 回复

Array reply:对每个成员返回——

  • 剩余 TTL(秒或毫秒):成员存在且有过期时间;
  • -1:成员存在但未设置过期时间;
  • -2:成员不存在。

8.3 ZEXPIRETIME / ZPEXPIRETIME:查询成员过期时刻

语法
ZEXPIRETIME key MEMBERS nummembers member [member ...] ZPEXPIRETIME key MEMBERS nummembers member [member ...]

返回指定 Sorted Set 成员的绝对过期时间(Unix 时间戳):ZEXPIRETIME以秒为单位,ZPEXPIRETIME以毫秒为单位。

Resp 回复

Array reply:对每个成员返回——

  • 过期时刻的 Unix 时间戳(秒或毫秒);
  • -1:成员存在但未设置过期时间;
  • -2:成员不存在。

8.4 ZPERSIST:移除成员过期时间

语法
ZPERSIST key MEMBERS nummembers member [member ...]

移除指定 Sorted Set 成员的过期时间,使其持久化(永不过期)。

Resp 回复

Array reply:对每个成员返回——

  • 1:成功移除超时;
  • 0:成员存在但本就没有超时;
  • -1:成员不存在。

ZTTL/ZPTTL/ZEXPIRETIME/ZPEXPIRETIME由 SortedSetTimeToLive 统一实现:通过参数arg1(是否毫秒)与arg2(是否返回时间戳)区分四种语义,底层走SortedSetOp.ZTTL操作码;ZPERSIST则由 SortedSetPersist 实现,使用独立的SortedSetOp.ZPERSIST。这组命令与 Hash 对象上的HEXPIRE/HTTL/HPERSIST等命令族相对应,是构建带成员级 TTL 的排行榜、缓存、任务队列的利器。

九、源码与测试索引

如果你想深入验证或扩展这些命令,可在仓库中关注以下路径:

主题源码/测试位置
ETag 命令族实现BasicEtagCommands.cs、ReadMethods.Etags.cs、RMWMethods.Etags.cs
ETag 行为测试RespEtagTests.cs
运维/扫描命令实现AdminCommands.cs(COMMITAOFHCOLLECTZCOLLECTEXPDELSCANMODULE LOADCS
过期键删除测试ExpiredKeyDeletionTests.cs
Sorted Set 成员过期实现SortedSetCommands.cs、SortedSetOps.cs
自定义命令/事务与模块custom-commands.md、ModuleRegistrar.cs
命令注册表RespCommand.cs、RespCommandHashLookupData.cs

十、总结

Garnet 的专属命令从四个维度扩展了标准 RESP 的能力:确定性运维COMMITAOFEXPDELSCANSECONDARYOF)、按需内存治理HCOLLECTZCOLLECTCOSCAN)、扩展体系REGISTERCSRUNTXPMODULE LOADCSWATCHMS/WATCHOS)以及协议级新语义(ETag 条件读写族与 Sorted Set 成员级过期族)。其中 ETag 命令族提供了零额外开销(对非 ETag 命令)的原生乐观并发控制,而成员级过期命令族把 TTL 的粒度从键推进到了集合成员。使用这些命令时,务必遵守键分区原则(ETag 键与非 ETag 键隔离)以及EXPDELSCAN与后台过期扫描互斥的约束,即可在兼容现有 Redis 客户端的前提下,获得远超标准 RESP 服务器的精细化控制能力。

【免费下载链接】garnetGarnet is a remote cache-store from Microsoft Research that offers strong performance (throughput and latency), scalability, storage, recovery, cluster sharding, key migration, and replication features. Garnet can work with existing Redis clients.项目地址: https://gitcode.com/GitHub_Trending/garnet4/garnet

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 17:14:13

gRPC开发流程实战:从基础概念到完整服务搭建

1. 为什么我建议你一定要搞懂gRPC开发流程如果你这几年一直在写后端服务,肯定能感受到微服务架构已经把单体应用拆得越来越细,服务之间的通信方式也从简单的HTTP JSON调用,慢慢转向了高性能、强契约的RPC框架。gRPC就是其中绕不开的一个。我最…

作者头像 李华
网站建设 2026/9/15 17:13:36

DeepSeek V4专家模式:MoE架构与AI编程实践

1. DeepSeek V4专家模式的技术解析最近DeepSeek V4推出的"专家模式"在技术圈引发了热烈讨论。作为一个长期跟踪AI技术发展的从业者,我认为这个功能背后蕴含着MoE(Mixture of Experts)架构的深度应用。与传统的dense模型不同&#x…

作者头像 李华
网站建设 2026/9/15 17:12:31

APK静态分析实战:移动取证中的关键技术与流程

做移动端取证和恶意样本分析这些年,我拆过的APK少说也有几百个。每次拿到新样本,不管是业务部门送来的可疑应用,还是涉案手机里提取出来的安装包,我第一反应永远是:先别急上模拟器,先把静态分析做完。APK静…

作者头像 李华