技术编程|自动重构Meterpreter绕过杀软·续( 六 )


在绕过杀软ESET Nod32的过程中 , 我们发现隐藏与samlib.dll相关的API导入非常重要 , 尤其是下面列表中的API:SamConnectSamConnectWithCredsSamEnumerateDomainsInSamServerSamLookupDomainInSamServerSamOpenDomainSamOpenUserSamOpenGroupSamOpenAliasSamQueryInformationUserSamSetInformationUserSamiChangePasswordUserSamGetGroupsForUserSamGetAliasMembershipSamGetMembersInGroupSamGetMembersInAliasSamEnumerateUsersInDomainSamEnumerateGroupsInDomainSamEnumerateAliasesInDomainSamLookupNamesInDomainSamLookupIdsInDomainSamRidToSidSamCloseHandleSamFreeMemory
据我们所知 , 这些函数并未在AV引擎中被列入黑名单 , 但它们确实以某种方式降低了杀软对其信任度 。因此 , 我们必须为每个函数注册一个ApiCallConsumer , 这意味着我们需要它们的名称和函数原型:static std::map
ApiToHide_samlib = {{"SamConnect","typedef NTSTATUS (__stdcall* _SamEnumerateDomainsInSamServer)(SAMPR_HANDLE ServerHandle, DWORD * EnumerationContext, PSAMPR_RID_ENUMERATION* Buffer, DWORD PreferedMaximumLength,DWORD * CountReturned);"},{"SamConnectWithCreds","typedef NTSTATUS(__stdcall* _SamConnect)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, BOOLEAN Trusted);"},{"SamEnumerateDomainsInSamServer", "typedef NTSTATUS(__stdcall* _SamConnectWithCreds)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, LSA_OBJECT_ATTRIBUTES * ObjectAttributes, RPC_AUTH_IDENTITY_HANDLE AuthIdentity, PWSTR ServerPrincName, ULONG * unk0);"},...}
这里的std :: make_unique是无比完美的 , 因为它允许我们在此循环中实例化堆上的对象 , 同时省去了稍后手动释放这些对象的工作 。当不再使用它们时 , 它们将被自动释放 。
最后 , 我们可以对mimikatz(尤其是kuhl_m_lsadump.c)进行混淆测试:bash run_example_mimikatz.sh test/kuhl_m_lsadump.c
产生一个有趣的结果:

技术编程|自动重构Meterpreter绕过杀软·续
本文插图

可以看到 , 实际的函数调用已正确替换:

技术编程|自动重构Meterpreter绕过杀软·续
本文插图

宏“ PRINT_ERROR”内的字符串被省略了 , 因为我们用do{}while(0)取消了该宏 。附带说明一下 , 我们没有找到比mimikatz更好的项目 , 可以用来测试和发现混淆器中的bug 。它的代码风格确实很奇特 。
改进措施
这是一些留给读者的练习更隐蔽
实际上不需要 LoadLibrary / GetProcAddress这些API , 也可执行运行时动态链接 。
我们最好选择重新实现这些功能以避免hooks , 并且已经有开源项目允许你来执行这样操作(ReflectiveDLLInjection) 。
如果你已经读到了这里 , 那么根据前面的介绍你只需要在翻译单元的顶部(使用findInjectionSpot)注入这些函数的具体实现 , 并更新addGetProcAddress方法即可使用 , 从而替换掉原来的WinAPI 。 错误处理
如果失败 , LoadLibrary将返回NULL , 因此可以为此添加检查并让其从错误中正常恢复 。在当前未加入检测的情况下 , 此应用程序很可能会崩溃 。
如果发生错误 , GetProcAddress也将返回NULL , 所以也必须进行检查 。
总结
在本文中 , 我们展示了如何在不使用正则表达式的情况下准确替换C/C ++代码库中的函数调用 。所有这些都是为了防止杀毒软件静态收集我们在渗透测试中使用的Meterpreter或其他软件的行为信息 。


推荐阅读