您的位置:首页 > 其它

gh0st错误修改

2016-05-17 18:45 399 查看
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年有段时间看过这个源码,发现了这个问题,发表在看雪论坛。这里转载过来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  gh0st bug 异常 调试