gh0st错误修改

来源:互联网 发布:数据库自定义函数 编辑:程序博客网 时间:2024/06/05 09:44

  • SetPaneText 的崩溃问题
  • WSAIoctl 参数类型导致栈异常
  • CIniFile 构造函数导致异常
  • 栈上对象多线程析构函数导致程序崩溃

  开始看 gh0st 源码,找来了一份比较纯净的官方代码来读,有点抓狂,听说使用很老的VC6.0写的,现在需要用 VS2010 重新创建工程,并拷贝代码过去,编译,分析整个执行流程,调试每一个遇到的bug,在这过程中学到了很多,记录下来,供后来参考:

SetPaneText 的崩溃问题

  这个应该属于多线程操作控件的问题,参见MFC不能多线程操作控件的原因 这里面讲解的比较深入了。
关于状态栏StatusBar有几点需要说明:
1)刚刚创建工程 CMainFrame 类里面就有一个 CMFCStatusBar m_wndStatusBar; 状态栏变量定义。在原版工程里面是CStatusBar m_wndStatusBar;
2)在这个类的 OnCreate 函数里面调用 m_wndStatusBar.SetPaneInfo(0, m_wndStatusBar.GetItemID(0), SBPS_STRETCH , 300); 设置每个状态栏分割宽度,后面两个参数 SBPS_STRETCH 表示 剩余的宽度都算在这个分割里面,300表示最小宽度,MSDN文档。
3)关于 CMFCStatusBar 使用方法可见 鸡啄米专栏 VS2010/MFC编程入门之三十八(状态栏的使用详解) 。
4)gh0st里面是这样使用 m_wndStatusBar 的:

void CIOCPServer::OnAccept(){        m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_CLIENT_CONNECT);}void CALLBACK CMainFrame::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode){    g_pFrame->m_wndStatusBar.SetPaneText(1, str);}

当异常的时候调用栈如下:
这里写图片描述
跟踪到异常位置来到系统代码:

        // should also be in the permanent or temporary handle map        CHandleMap* pMap = afxMapHWND();        ASSERT(pMap != NULL);        CObject* p=NULL;        if(pMap)        {            ASSERT( (p = pMap->LookupPermanent(m_hWnd)) != NULL ||                    (p = pMap->LookupTemporary(m_hWnd)) != NULL);        }

总之一句话,这个 SetPaneText 是从其它线程 ListenThreadProc 调用过来的,如果要正常使用可以修改为如下方式 :

void CALLBACK CMainFrame::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode){    g_pFrame->PostMessageA(UpdatePane);}

g_pFrame 是一个 CMainFrame 类型指针,在CMainFrame 类里面加入一个消息处理过程:

ON_MESSAGE(UpdatePane, &CMainFrame::OnUpdatepane)afx_msg LRESULT CMainFrame::OnUpdatepane(WPARAM wParam, LPARAM lParam){    m_wndStatusBar.SetPaneText(1, "test");    return 0;}

  在 ListenThreadProc 向 CMainFrame 类发送一个消息 PostMessage(UpdatePane),然后在 CMainFrame 类里面处理这个消息,至此问题完美解决。
参考:
MFC中从一个类向其他类发送消息的方法

WSAIoctl 参数类型导致栈异常

以前gh0st代码如下:

    const char chOpt = 1;    WSAIoctl        (        pContext->m_Socket,         SIO_KEEPALIVE_VALS,        &klive,        sizeof(tcp_keepalive),        NULL,        0,        (unsigned long *)&chOpt,        0,        NULL        );

WSAIoctl 原型声明如下:

int WSAAPI WSAIoctl(    __in SOCKET s,    __in DWORD dwIoControlCode,    __in_bcount_opt(cbInBuffer) LPVOID lpvInBuffer,    __in DWORD cbInBuffer,    __out_bcount_part_opt(cbOutBuffer, *lpcbBytesReturned) LPVOID lpvOutBuffer,    __in DWORD cbOutBuffer,    __out LPDWORD lpcbBytesReturned,    __inout_opt LPWSAOVERLAPPED lpOverlapped,    __in_opt LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine    );

  倒数第三个参数应当是 LPDWORD 类型,而且是输出,传入的仅仅是char类型,虽然因为对齐 char 也分配了4字节,不会导致栈覆盖,但是在debug模式下系统加入了严苛的栈检测机制:虽然分配了四字节,因为是char类型,所以剩余的三字节是不应该修改的,在release下就没有这问题。
修改为 LONG 类型即可解决问题:

    unsigned long chOpt;    *((char *)&chOpt)   = 1;

CIniFile 构造函数导致异常

系统自动定义的一个对象

// 唯一的一个 ChostApp 对象ChostApp theApp;

这个构造函数应该是在最早执行的,在 ChostApp 里面有一个成员

CIniFile    m_IniFile;

所以要首先调用 CIniFile 的构造函数

CIniFile::CIniFile(void){    char szAppName[MAX_PATH];    int  len;    HINSTANCE   hinst;    //hinst = AfxGetInstanceHandle();    ::GetModuleFileName(GetModuleHandle(NULL), szAppName, sizeof(szAppName));    len = strlen(szAppName);}

因为 AfxGetInstanceHandle() 调用导致异常。
具体可见CSDN论坛讨论。

栈上对象多线程,析构函数导致程序崩溃

  打开主控端。开启被控端, 此时主控端显示上线。

  在server端点击桌面管理可以正常显示被控端桌面,然后关闭远程桌面,此时被控端出现一个错误:
TestDll.exe 中的 0x5950cc6f (server.dll) 处有未经处理的异常: 0xC0000005: 读取位置 0x02ecfca4 时发生访问冲突

nRet = m_pClient->Send((LPBYTE)lpData, nSize);5950CC64  mov         eax,dword ptr [ebp+0Ch]  5950CC67  push        eax  5950CC68  mov         ecx,dword ptr [ebp+8]  5950CC6B  push        ecx  5950CC6C  mov         edx,dword ptr [ebp-18h]  5950CC6F  mov         ecx,dword ptr [edx+4]  

此时执行到的指令位置是 5950CC6F

对应的源代码是

int CManager::Send(LPBYTE lpData, UINT nSize){  int  nRet = 0;  try  {    nRet = m_pClient->Send((LPBYTE)lpData, nSize);  }catch(...){};  return nRet;}

寄存器edx的数值是 edx 0x02ecfca0 unsigned long

  奇怪的是在关闭远程桌面以前这个函数执行了很多次,都没有出现这个问题。现在在关闭远程桌面之后就这样。通过对比发现 出现访问异常的内存 在关闭远程桌面之后数值出现了变化。

可以在关闭远程桌面之后 vs2010下内存访问断点,edx + 4 的位置

          调试  ->  新建断点   ->    新建内存断点

此时按F5发现中断在manager类的析构函数里面:

CManager::~CManager(){  CloseHandle(m_hEventDlgOpen);}

再看堆栈窗口 是从 Loop_ScreenManager 函数结尾调用而来的:

DWORD WINAPI Loop_ScreenManager(SOCKET sRemote){  CClientSocket  socketClient;  if (!socketClient.Connect(CKernelManager::m_strMasterHost,  CKernelManager::m_nMasterPort))    return -1;  CScreenManager  manager(&socketClient);  socketClient.run_event_loop();  return 0;}

  这样就大概分析出执行流程:

  在上面函数中定义了一个CScreenManager类的对象manager,这个对象在栈中。CScreenManager基类是 CManager
当在主控端把远程桌面关闭之后run_event_loop 会返回,这样这个函数也就返回了,对象manager也就开始调用自己的析构函数,以前能访问的现在也就不能访问了。 所以就会出现上面的问题。

  其实在运行的时候CScreenManager类的构造函数中创建了2个线程ControlThread ,WorkThread,当正常运行没有调试器中断的时候当函数 Loop_ScreenManager 执行完之后ControlThread 这个线程还未完全退出,不知道这样会产生什么意外后果??

  经过试验在 函数 Loop_ScreenManager结束之前加入 sleep(20) 可以解决这个问题。

注:14年有段时间看过这个源码,发现了这个问题,发表在看雪论坛。这里转载过来。

0 0