gh0st错误修改

  开始看 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值