C/C++汇编 [2022]关于intel和amd指令行为不一样这件事 ### AMD CPU与INTEL CPU指令解析问题 假设一个情况: ```cpp 66 e8 00 00 00 00 ``` 在x86指令集中 66会作为e8的prefix,覆盖code segment size 假设当前大小为16bit 新大小会为32bit.反之一样. code segment size由 GDT/LDT 决定.实模式一般是16 bit("unreal"模式除外) 在x64中,我发现了一个很有意思的地方: 66 E8作为指令前缀时,AMD和INTEL呈现了完全不同的特性,即amd会遵循规则,将e8的code segment size设置为16bit 而intel则直接忽略. 这个问题已经被发现了有一段时间了,capstone在2016年,记录了这个问题. https://github.com/capstone-engine/capstone/issues/776 这是一个概念性代码,不用CPUID判断出是INTEL还是AMD的cpu. ### 简单的思考: 能否用于混淆? ### poc https://github.com/huoji120/cpu_duck 阅读全文 2022-04-16 huoji 0 条评论
系统安全二进制安全C/C++汇编一线开发 [2022]Windows ALPC解码:(二) > 不得不承认,ALPC是我目前见过最棘手、最麻烦的东西,而且他还被非常多的应用所使用,如RPC、WMI、COM组件、打印机等每个组件都或多或少带漏洞.对于EDR的开发者来说,简直就是公共厕所.因此这篇文章会介绍如何正确的清理公共厕所. 继上次[ALPC拦截的文章](https://key08.com/index.php/2021/11/15/1394.html "ALPC拦截的文章")后,我们遇到了一个问题: 如何解码ALPC的包? ALPC的传输机制类似于windows的TCP 分为几个阶段: 阅读全文 2022-03-27 huoji 0 条评论
二进制安全C/C++汇编一线开发 [2021]windows ALPC内核拦截的方法 ALPC这玩意挺复杂的,现在的安全软件应该都是在r3下hook services来实现的,比如 https://bbs.pediy.com/thread-251158.htm 写的 但是r3下挂会遇到PPL问题 https://bbs.pediy.com/thread-250446.htm 所以才有了今天的文章 ### 内核可以的挂钩方法 直接内联hook -PG 找指针改地址,做DKOM -安全软件不应该这样做 由于ALPC_PORT是object回调,因此挂object -win7可以 win10直接炸 IO挂钩 -本文的方法 最开始我是自己逆向ALPC时候偶然发现的,后来搜了一下外网,老外 http://www.zer0mem.sk/?p=542 也找到了一个一样的方法,早知道就先搜搜.... ### 为什么能挂上 在NtAlpcSetInformation->AlpcpInitializeCompletionList->AlpcpAllocateCompletionPacketLookaside中可以看到 ![](https://key08.com/usr/uploads/2021/11/1808403136.png) 这是一个IO回调,可以通过设置微软允许的IOComplateCallback来做一些操作 ### 怎么挂钩 首先我们要知道内核里面有个全局的双向链表是叫做nt!alpclist 根据zeromem的说法是 > 虽然这玩意不公开,但是有个公开结构AlpcPortObjectType是跟他在一起的,定位到AlpcPortObjectType再定位到它就行 ![](https://key08.com/usr/uploads/2021/11/2984097239.png) 但是这仅限于win8之前,win10后,这玩意就成这样的布局了: ![](https://key08.com/usr/uploads/2021/11/2929583585.png) 因此我们再也无法通过公开的AlpcPortObjectType再拿到信息了,只能找特征码: ```cpp \x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\x8D\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\xE8\xCC\xCC\xCC\xCC\xBB\xCC\xCC\xCC\xCC\x48\x8D\xCC\xCC\xCC\x4C\x8B\xC3\x33\xD2 ``` 那个alpclistlock也是一样,硬编码处理 ### 挂钩 我们需要干什么: 遍历alpclist 找到这个ALPC是否是我们的需要挂钩的进程(比如services.exe) 设置IO回调挂钩 首先记得锁一下: ```cpp if (_interlockedbittestandset64((__int64*)&gAlpcpPortListLock, 0)) ExfAcquirePushLockShared((PULONG_PTR)&gAlpcpPortListLock); ``` 然后是遍历这个双向链表 ```cpp PLIST_ENTRY pLink = NULL; for (pLink = gAlpcpPortList->Flink; pLink != (PLIST_ENTRY)&gAlpcpPortList->Flink; pLink = pLink->Flink) { _ALPC_PORT* alpcPort = CONTAINING_RECORD(pLink, _ALPC_PORT, PortListEntry); ``` 其中ALPC_PORT结构每个版本都在变 ```cpp struct _ALPC_PORT { struct _LIST_ENTRY PortListEntry; //0x0 struct _ALPC_COMMUNICATION_INFO* CommunicationInfo; //0x10 struct _EPROCESS* OwnerProcess; //0x18 VOID* CompletionPort; //0x20 VOID* CompletionKey; //0x28 struct _ALPC_COMPLETION_PACKET_LOOKASIDE* CompletionPacketLookaside; //0x30 VOID* PortContext; //0x38 struct _SECURITY_CLIENT_CONTEXT StaticSecurity; //0x40 struct _LIST_ENTRY MainQueue; //0x88 struct _LIST_ENTRY PendingQueue; //0x98 struct _LIST_ENTRY LargeMessageQueue; //0xa8 struct _LIST_ENTRY WaitQueue; //0xb8 union { struct _KSEMAPHORE* Semaphore; //0xc8 struct _KEVENT* DummyEvent; //0xc8 }; struct _ALPC_PORT_ATTRIBUTES PortAttributes; //0xd0 struct _EX_PUSH_LOCK Lock; //0x118 struct _EX_PUSH_LOCK ResourceListLock; //0x120 struct _LIST_ENTRY ResourceListHead; //0x128 struct _ALPC_COMPLETION_LIST* CompletionList; //0x138 struct _ALPC_MESSAGE_ZONE* MessageZone; //0x140 struct _CALLBACK_OBJECT* CallbackObject; //0x148 VOID* CallbackContext; //0x150 struct _LIST_ENTRY CanceledQueue; //0x158 volatile LONG SequenceNo; //0x168 union { struct { ULONG Initialized : 1; //0x16c ULONG Type : 2; //0x16c ULONG ConnectionPending : 1; //0x16c ULONG ConnectionRefused : 1; //0x16c ULONG Disconnected : 1; //0x16c ULONG Closed : 1; //0x16c ULONG NoFlushOnClose : 1; //0x16c ULONG ReturnExtendedInfo : 1; //0x16c ULONG Waitable : 1; //0x16c ULONG DynamicSecurity : 1; //0x16c ULONG Wow64CompletionList : 1; //0x16c ULONG Lpc : 1; //0x16c ULONG LpcToLpc : 1; //0x16c ULONG HasCompletionList : 1; //0x16c ULONG HadCompletionList : 1; //0x16c ULONG EnableCompletionList : 1; //0x16c } s1; //0x16c ULONG State; //0x16c } u1; //0x16c struct _ALPC_PORT* TargetQueuePort; //0x170 struct _ALPC_PORT* TargetSequencePort; //0x178 struct _KALPC_MESSAGE* volatile CachedMessage; //0x180 ULONG MainQueueLength; //0x188 ULONG PendingQueueLength; //0x18c ULONG LargeMessageQueueLength; //0x190 ULONG CanceledQueueLength; //0x194 ULONG WaitQueueLength; //0x198 }; ``` 之后判断进程合法性: ```cpp if (!alpcPort->OwnerProcess || PsGetProcessId((PEPROCESS)alpcPort->OwnerProcess) != TargetPrcessId || !alpcPort->CompletionPort) continue; ``` 然后就是设置回调了 ```cpp void* IoMiniCompletPtr = IoAllocateMiniCompletionPacket(ALPC_NotifyCallback, alpcPort); if (IoMiniCompletPtr == NULL) { __debugbreak(); return; } IoSetIoCompletionEx( alpcPort->CompletionPort, alpcPort->CompletionKey, nullptr, NULL, NULL, FALSE, IoMiniCompletPtr); ``` 这个IoAllocateMiniCompletionPacket和他的结构我是自己IDA逆向推导出来的,因为没公开,没任何可靠的信息 ```cpp struct _IO_MINI_COMPLETION_PACKET_USER { struct _LIST_ENTRY ListEntry; //0x0 ULONG PacketType; //0x10 VOID* KeyContext; //0x18 VOID* ApcContext; //0x20 LONG IoStatus; //0x28 ULONGLONG IoStatusInformation; //0x30 VOID(*MiniPacketCallback)(struct _IO_MINI_COMPLETION_PACKET_USER* arg1, VOID* arg2); //0x38 VOID* Context; //0x40 UCHAR Allocated; //0x48 }; /* 这两玩意是我IDA逆向看的,微软也没说 PAGE:00000001402C807C IoAllocateMiniCompletionPacket proc near PAGE:00000001402C807C ; CODE XREF: AlpcpAllocateCompletionPacketLookaside+82↑p PAGE:00000001402C807C ; NtCreateWorkerFactory+179↓p PAGE:00000001402C807C ; DATA XREF: ... PAGE:00000001402C807C PAGE:00000001402C807C arg_0 = qword ptr 8 PAGE:00000001402C807C PAGE:00000001402C807C 48 89 5C 24 08 mov [rsp+arg_0], rbx PAGE:00000001402C8081 57 push rdi PAGE:00000001402C8082 48 83 EC 20 sub rsp, 20h PAGE:00000001402C8086 48 8B DA mov rbx, rdx PAGE:00000001402C8089 33 D2 xor edx, edx PAGE:00000001402C808B 48 8B F9 mov rdi, rcx PAGE:00000001402C808E 8D 4A 03 lea ecx, [rdx+3] PAGE:00000001402C8091 E8 AA 9C 03 00 call IopAllocateMiniCompletionPacket PAGE:00000001402C8096 48 85 C0 test rax, rax ; 【rax = 分配的io包结构】 PAGE:00000001402C8099 74 0C jz short loc_1402C80A7 PAGE:00000001402C809B 48 89 78 38 mov [rax+38h], rdi ; 【0x38 = _IO_MINI_COMPLETION_PACKET_USER->MiniPacketCallback】 PAGE:00000001402C809F 48 89 58 40 mov [rax+40h], rbx ; 【0x40 = _IO_MINI_COMPLETION_PACKET_USER->Context】 PAGE:00000001402C80A3 C6 40 48 01 mov byte ptr [rax+48h], 1 PAGE:00000001402C80A7 PAGE:00000001402C80A7 loc_1402C80A7: ; CODE XREF: IoAllocateMiniCompletionPacket+1D↑j PAGE:00000001402C80A7 48 8B 5C 24 30 mov rbx, [rsp+28h+arg_0] PAGE:00000001402C80AC 48 83 C4 20 add rsp, 20h PAGE:00000001402C80B0 5F pop rdi PAGE:00000001402C80B1 C3 retn PAGE:00000001402C80B1 IoAllocateMiniCompletionPacket endp rax = 一个指针 */ typedef void(__fastcall* MINIPACKETCALLBACK)( __in _IO_MINI_COMPLETION_PACKET_USER* miniPacket, __inout void* context ); extern "C" { NTKERNELAPI void* NTAPI IoAllocateMiniCompletionPacket( __in MINIPACKETCALLBACK miniPacketCallback, __in const void* context ); NTKERNELAPI void NTAPI IoSetIoCompletionEx( __inout void* completitionPort, __in const void* keyContext, __in const void* apcContext, __in ULONG_PTR ioStatus, __in ULONG_PTR ioStatusInformation, __in bool allocPacketInfo, __in const void* ioMiniCoompletitionPacketUser ); NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); NTSYSAPI PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( _In_ PVOID Base ); NTKERNELAPI void* NTAPI IoFreeMiniCompletionPacket( __in const void* miniPacket ); } ``` 挂钩后,我们顺利得到信息 ![](https://key08.com/usr/uploads/2021/11/798563398.png) ![](https://key08.com/usr/uploads/2021/11/1182783651.png) ### 解码ALPC_PORT信息 ALPC这个只是一个标准协议,每个不同的服务比如 创建服务与创建账号与搜索系统信息 等的具体内容都是不同的,要自己手动解码,但是在这里如zeroman所说,这个keycontext带了一个叫做SubProcessTag的东西,这个东西就对应这个RPC所带的服务(其实不是ALPC传过来的),就可以拿到基本的,某进程发了RPC给某进程,并且RPC是关于xxx服务的,但是服务的具体信息比如名字啥的 **具体解码自己想** 其他的参考: https://bbs.pediy.com/thread-268225.htm#msg_header_h1_7 https://blog.csdn.net/weixin_43787608/article/details/84555474 https://bbs.pediy.com/thread-251158.htm http://www.zer0mem.sk/?p=542 阅读全文 2021-11-15 huoji 0 条评论
系统安全二进制安全C/C++汇编 [2020]微软windows defender研究.pdf 主要说了微软的defender是如何做虚拟执行的 反cpuid反虚拟机 ![](https://key08.com/usr/uploads/2021/10/2461615230.png) 虚拟文件系统(注意这是不可见的,平时也没这些目录,但是在杀毒的时候defender就会生成) ![](https://key08.com/usr/uploads/2021/10/1349146434.png) 虚拟注册表 ![](https://key08.com/usr/uploads/2021/10/1427939148.png) JavaScript引擎 ![](https://key08.com/usr/uploads/2021/10/2711418115.png) vmp、net 脱壳/jit解析 ![](https://key08.com/usr/uploads/2021/10/2836626296.png) pdf文件 链接:https://pan.baidu.com/s/15msYM6Ye1Bu5mJdr_vpMow 提取码:7nua 阅读全文 2021-10-23 huoji 0 条评论
系统安全二进制安全C/C++汇编一线开发 [2021]漫游微软屎山 win32kfull.sys (一) ssdt和shadow ssdt 微软的win32kfull.sys这玩意绝对是,微软整个操作系统上漏洞最多的驱动之一,因为他的代码是从win3.1一路贴屎山贴过来的,在如今windows 20h2最新系统上,你任然可以看到某些莫名其妙的函数 ![](https://key08.com/usr/uploads/2021/09/322474017.png) lszzz zzz qqqqzzzqx等奇怪的看起来就像是屎山的代码 过去二十年,微软这玩意一直是漏洞报告的重头戏,各种本地提权、rce层出不穷,其恶劣程度堪比rpc和wmi.因此我准备单独开一系列介绍这个win32kfull.sys(本人也在持续逆向他中) ## ssdt和shadow ssdt 总所周知,x64后大部分常规API进内核的方式是syscall,当有syscall指令后,cpu会把rip指向amd64_lstar这个寄存器里面的地址,操作系统则把这个寄存器改成自己的进内核的函数,微软windows下这个r3和内核交互的函数名字叫做 kisystemcall64 而操作系统的大部分跟R3交互的API 都是一套叫做SSDT的机制控制的,并且如果是涉及到图形的api 则是放在shadow ssdt也就是sssdt,为什么要这样放,因为内核实在是没办法塞下win3.1的垃圾屎山代码.也没办法重构,所以干脆分开 阅读全文 2021-09-04 huoji 0 条评论
python系统安全工具软件汇编一线开发 [2021]余弦定理检测文件相似度 & 病毒样本基因检测 本余弦定理有如下应用场景: 1.相似度计算 2.信息推送 在网络安全领域,主要就是样本基因检测,或者叫做样本相似度计算,他的公式长这样: ![](https://key08.com/usr/uploads/2021/08/2243488000.png) 请注意,之所以叫做余弦定理,是因为,他就是求一个三角形的角,并且在N维这个定理也成立 ![](https://key08.com/usr/uploads/2021/08/3451125394.png) 样本相似度检测,以两个风灵月影为例,属于同一个家族: ![](https://key08.com/usr/uploads/2021/08/947620823.png) # 编码 通过pefile库,读入文件,然后逐个比对字节码,参数A为字节码相同的,参数B为字节码不同的: ```cpp def get_peinfo_by_cos(pSource,pTarget): source = pefile.PE(pSource) target = pefile.PE(pTarget) source_map,source_sizeof_code,source_base_of_code = get_pe_info(source) target_map,target_sizeof_code,target_base_of_code = get_pe_info(target) a1_dict = {} a2_dict = {} for iter in range(source_sizeof_code): v1 = iter + source_base_of_code v2 = iter + source_base_of_code + 1 if source_map[v1:v2] in a1_dict.keys(): a1_dict[source_map[v1:v2]] = a1_dict[source_map[v1:v2]] + 1 else: a1_dict[source_map[v1:v2]] = 0 for iter in range(target_sizeof_code): v1 = iter + target_base_of_code v2 = iter + target_base_of_code + 1 if target_map[v1:v2] in a2_dict.keys(): a2_dict[target_map[v1:v2]] = a2_dict[target_map[v1:v2]] + 1 else: a2_dict[target_map[v1:v2]] = 0 str1_vector=[] str2_vector=[] for key in a1_dict: str1_count = a1_dict[key] str1_vector.append(str1_count) for key in a2_dict: str2_count = a2_dict[key] str2_vector.append(str2_count) str1_map = map(lambda x: x*x,str1_vector) str2_map = map(lambda x: x*x,str2_vector) str1_mod = reduce(lambda x, y: x+y, str1_map) str2_mod = reduce(lambda x, y: x+y, str2_map) str1_mod = math.sqrt(str1_mod) str2_mod = math.sqrt(str2_mod) vector_multi = reduce(lambda x, y: x + y, map(lambda x, y: x * y, str1_vector, str2_vector)) # 计算余弦值 cos = float(vector_multi)/(str1_mod*str2_mod) return cos ``` 其中,两个是相似的,两个是不相似的,两个是恶意样本家族 来试试: ![](https://key08.com/usr/uploads/2021/08/1402924204.png) 简单粗暴,并且有效. 完整代码: 阅读全文 2021-08-19 huoji 1 条评论