python工具软件二进制安全 [2022] 基于NLP的威胁检出引擎 NLP引擎出自于我的一个简单的想法 - 既然人可以通过汇编看出软件是否是病毒 那么机器是否可以通过汇编看呢? 为了让机器能看得懂代码,我首先想到的是非常经典的NLP分类问题,所谓的NLP分类问题,就是训练的时候给一堆词语+词频,推理的时候先把句子中的词语分出来,再然后计算这些词语的词频,最后得出这条句子属于什么类别的结论 阅读全文 2022-03-17 huoji 1 条评论
系统安全二进制安全C/C++ [2022]从微软代码中学习C/C++项目代码规范 ### 1.变量用abcd: ```cpp PCONSOLE_READCONSOLE_MSG a = (PCONSOLE_READCONSOLE_MSG)&Message->u.ApiMessageData; ``` 一个项目里面用abc来命名是大忌,因为这会完全让其他人不知道这个变量的意思,一般情况下一个项目的规范命名有两种: > 驼峰 下划线 而不是微软这种 abcd 阅读全文 2022-01-12 huoji 0 条评论
系统安全二进制安全C/C++ [2021]Windows Services Manager 工作原理 Windows Services Manager这个组件的工作原理比我之前想的还诡异.这里简单说一下"服务"是怎么被管理的 R3下如果你不直接操控服务注册表而是用API如OpenServiceA、CreateServiceA这些API后,会调用到RPC,发往RPC服务端 - services.exe ### handle不是真handle 我们都知道 OpenService、CretaeService这些函数在没有错误的时候都会返回一个句柄.在服务端里面是这样实现的: ![](https://key08.com/usr/uploads/2021/12/452801300.png) 服务端申请一个heap内存->创建一个内核对象,交给内核管理->返回ScServiceObject 在使用其他的API比如ChangeServiceConfig、DeleteService这些API的时候,客户端需要提供一个之前OpenService、CretaeService函数返回的句柄,这没什么.但是到了服务端也就是services.exe的时候,这个"句柄"就是一个heap内存地址. ### 微软判断句柄是否有效的"高级"方法 以RDeleteService为例,微软通过一个叫做ScIsValidServiceHandle的函数判断句柄是否有效: ![](https://key08.com/usr/uploads/2021/12/1937175394.png) 实际上这个判断就是判断内存是否可用,内存前几个字节是否等于特定的签名: ![](https://key08.com/usr/uploads/2021/12/3160170665.png) 其中48726573h是一个签名,char后叫做"Hres" 但是网上的openNt的签名不知道是微软故意的还是怎么的,是跟WIN7的不一样的(也可能是年代太久远改了) 网上的: ![](https://key08.com/usr/uploads/2021/12/4151614666.png) 这波很高级 ### 句柄变成结构 在服务端参数传进来的句柄从SC_HANDLE会变成一个叫做LPSC_HANDLE_STRUCT的结构,你可以简单粗暴的这样理解: ```cpp LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT)hService; ``` 这个"LPSC_HANDLE_STRUCT"长这样: ```cpp typedef struct _SC_HANDLE_STRUCT { DWORD Signature; // For block identification to detect some app errors DWORD Flags; // See definitions above DWORD AccessGranted; // Access granted to client. union { // Object specific data struct { LPWSTR DatabaseName; // Name of database opened } ScManagerObject; struct { LPSERVICE_RECORD ServiceRecord; // Pointer to service record } ScServiceObject; } Type; } SC_HANDLE_STRUCT, * LPSC_HANDLE_STRUCT; ``` 请注意,网上的是错的,网上的OPEN NT结构长这样: https://github.com/Hengle/windows_nt_3_5_source_code/blob/d2894c9125ff1c14028435ed1b21164f6b2b871a/windows_nt_3_5_source_code/NT-782/PRIVATE/WINDOWS/SCREG/SC/SERVER/SCOPEN.H#L52 ```cpp typedef struct _SC_HANDLE_STRUCT{ DWORD Signature; // For block identification to detect some app errors DWORD AccessGranted; // Access granted to client. union { // Object specific data struct { LPWSTR DatabaseName; // Name of database opened } ScManagerObject; struct { LPSERVICE_RECORD ServiceRecord; // Pointer to service record } ScServiceObject; } Type; } SC_HANDLE_STRUCT, *LPSC_HANDLE_STRUCT; ``` 这个是错的。 之后,服务端会通过这个SC_HANDLE_STRUCT结构,检查其中的权限: ![](https://key08.com/usr/uploads/2021/12/729026050.png) 然后从内核删除 ![](https://key08.com/usr/uploads/2021/12/1276980355.png) 而其他的,比如查询config的,则通过服务名寻找到服务注册表项,然后查询之类的(md,到最后还不是查注册表) 阅读全文 2021-12-29 huoji 0 条评论
二进制安全C/C++ [2021]微软的dll搜索源码 from windows XP ```cpp BOOL IsSvcInjected(DWORD dwPid) { HANDLE hProcess; HMODULE hMods[1024]; BOOL res = FALSE; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid); if (hProcess != NULL) { DWORD dwSize; // search through all loaded modules and see if we are injected if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &dwSize)) { DWORD n; for (n = 0; n < (dwSize / sizeof(HMODULE)); n++) { WCHAR wszModName[MAX_PATH]; if (GetModuleBaseNameW(hProcess, hMods[n], wszModName, sizeof(wszModName) / sizeof(WCHAR))) { if (_wcsicmp(wszModName, SBIEDLL L".dll") == 0) { res = TRUE; break; } } } } // if (EnumProcessModules()) CloseHandle(hProcess); } // if (OpenProcess()) return res; } ``` 阅读全文 2021-12-09 huoji 0 条评论
系统安全工具软件二进制安全 [2021]AppDomainManager劫持与powershell hook解码 在红队渗透中,有一种持久化技术经常被使用,那就是AppDomainManager劫持,具体原理本章不再叙述,简单来说,在.net目录新建一个同样文件的.config文件,就可以控制.net的appdomainmanger加载,攻击者往往利用这个特性做后门 ```asp ``` 在安全产品对抗中,安全产品时刻面临着powershell的挑战原因很简单,powershell这玩意,简直是小黑和apt组织狂喜,各种花里胡哨的加密与解密去绕过edr/av,并且微软的amsi接口也各种拉胯,甚至是可以被powershell自己绕过,所以就不能指望微软了,要自己干 -powershell的hook ### 如何获取干净的powershell指令 powershell本质上是基于.net的解释器,在powershell脚本代码执行后,会被编译成il代码继续执行,因此为了解决powershell被混淆的问题,我们需要想办法hook powershell的编译代码,好消息是微软的.net相当于开源,所以很快就能找到这个函数: ```asp System.Management.Automation.CompiledScriptBlockData ``` ![](https://key08.com/usr/uploads/2021/11/1354091260.png) 顺便吐槽一下微软的amsi: ![](https://key08.com/usr/uploads/2021/11/2089727795.png) ### 如何hook 在我们劫持了AppDomainManager启动后,我们需要干的第一件事就是监听程序集加载: ```cpp private const BindingFlags anyType = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly AssemblyLoadEventHandler HookAssemblyLoadEventHandler = new AssemblyLoadEventHandler(Rm_OnAssemblyLoad); private static void Rm_OnAssemblyLoad(object sender, AssemblyLoadEventArgs args) { string assemblyName = args.LoadedAssembly.GetName().Name; string assemblyFullName = args.LoadedAssembly.Location; if (assemblyName == "System.Management.Automation") { ...这里就是程序集加载事件了 ``` 之后,我们要得到此函数的指针 ![](https://key08.com/usr/uploads/2021/11/3051046094.png) ```cpp MethodInfo ReallyCompile = targetMethodClass.GetMethod("ReallyCompile", anyType, null, targetMethodType, null); ... RuntimeHelpers.PrepareMethod(ReallyCompile.MethodHandle); ... IntPtr TargetAddress = ReallyCompile.MethodHandle.GetFunctionPointer(); ``` 然后hook他 ```cpp if (!MinHook.InstallHook(TargetAddress, HookAddress, TrampolineAddress)) { throw new ArgumentNullException("[HUOJI] Min Hook Fail"); //仅供测试,一般要return } ``` 至此,我们就hook了powershell并且拿到了正确信息: ```cpp public void HookReallyCompile(bool optimize) { string Code = this.ToString(); Helper.DbgPrint(Code); .... ``` ![](https://key08.com/usr/uploads/2021/11/1201952192.png) 阅读全文 2021-11-22 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 条评论