断断续续读完这本书,花了接近两年时间,做游戏之前开始读,公司的项目做的差不多了,才读完第一遍。刚进去的时候是新人,转眼成了老鸟。
初读这本书的缘由,只是逆向的时候职业习惯对于C系语言地址+偏移的敏感,不解类的构造和传统的struct在内存布局上的区别和暗中手脚。读完之后,ADT,布局,虚函数表,临时对象,模板绑定,运行期识别等实现方式了然于胸。
书的上半部分已经解了我的惑,阐述的内容集中于C++之于C的内存布局和编译器在强类型语法上的支持及实现手法,表现于vptr,virtual public之实现,重在编译和链接期。
下半部分对new/placement new,全局对象预分配,临时对象的生成时机,EH, RTTI, type_info,type_id,以及dynamic_cast之于引用和指针都做了独到的讲解,重在运行期的数据结构组织查询方式。
全书每一个测试都贯穿了执行效率和代码复杂度上代价互换的思考,到后期的一条测试主线甚至加进了各家编译器之于不同应用平台的特化性/效率取舍,受益匪浅。
以下是感悟了:
C++之尤雅 在于有效解决了函数名重用的问题 vtable的实际作用一方面在于解决了无数的if elseif,另一方面避免了系列函数名之累赘。
然而局限也在源于此,无论vtable也好,EH也好,RTTI也好,都是在编译期硬编码了某些地址或者执行期依赖的结构信息,在视野扩展到操作系统的跨进程环境下,不同进程内虚拟地址就无法共享了。
回过头来看 ,C++编译器上的很多东西,MFC也有一些蹩脚实现。书的最后几句话讲了COM相关的东西,如COM当初开发组的leader所言,微软之所以要做COM,初衷只是解决进程间通信的问题。
最后抄一段代码来结束这半年的开发和学习生活。
#include "stdafx.h"
#include <iostream>
#include <typeinfo>
using namespace std;
class B
{
};
class D :public B
{
};
int _tmain(int argc, _TCHAR* argv[])
{
B *pb = new B;
D *pd = new D;
cout <<”pb’s type name = ” << typeid(pb).name() << endl;
cout <<”pd’s type name = ” << typeid(pd).name() << endl;
cout <<”pb’s type rawname = ” << typeid(pb).raw_name() << endl;
cout <<”pd’s type rawname = ” << typeid(pd).raw_name() << endl;
system(”pause”);
return 0;
}
这本书忙中偷闲读了半年有余才看完4章 每次总是看过几页又得往回翻 年底重拾此书 写下一些代码调试了书中知识点
发现毕业几年来第一次碰到一本书写得如此有味道
前几章的内容不外乎讲各厂家编译器里对象初始化、内存布局、内存管理、虚表的实现细节
细读来 以前理解不是很深刻的多重继承下指针转化的偏移开销 初始化表和继承member的执行顺序 派生类和基类的布局方式 虚函数表的生成和调用等问题都收益颇多
太久没有更新过这个地方了 花了点时间写的测试代码贴上来献献丑
#include "stdafx.h"
#include <windows.h>
class Point2d
{
public:
Point2d()
{
x= 0.1;
y=0.2;
}
~Point2d()
{
}
public:
virtual DWORD getPos(){return pos;};
public:
float x;
float y;
static DWORD pos;
};
DWORD Point2d::pos = 3;
int _tmain(int argc, _TCHAR* argv[])
{
Point2d point2d;
//取类基址 地址应该在栈上
void* p = (void*)&point2d;
//取vptr地址 ms的编译器把vptr放在类开头
unsigned int pVptr = (unsigned int)*(unsigned int*)p;
//取vtable地址
unsigned int* p2 = (unsigned int*)pVptr;
//vtable里第一个DWORD就是getPos的指针
unsigned int addr = *p2;
//调用虚函数getPos
DWORD ret = 0;
__asm
{
push eax;
mov eax,addr;
call eax;
mov ret,eax;
pop eax;
}
printf(”getPos= %u\n”,ret);
printf(”&point2d.x = %p\n”,&point2d.x);
printf(”&point2d.y = %p\n”,&point2d.y);
printf(”&Point2d::x = %p\n”,&Point2d::x);
printf(”&Point2d::y = %p\n”,&Point2d::y);
printf(”&point2d.pos = %p\n”,&point2d.pos);
printf(”&Point2d::pos = %p\n”,&Point2d::pos);
return 0;
}
一个vc6的项目放到vc8下重新编译 这里死活过不去 查了些资料无果 后来翻到一句老外的回答
If AfxGetMainWnd is called from the application’s primary thread, it returns the application’s main window according to the above rules. If the function is called from a secondary thread in the application, the function returns the main window associated with the thread that made the call.
大概意思就是说在子线程里面调用AfxGetMainWnd()返回的是和当前线程相关联的窗体句柄而不是当前程序的主窗体句柄。不知道这是不是vc8的一个改变,也没时间去细查。
解决方法1:
CWnd* m_pCWnd = NULL;
在OnInitDialog里 m_pCWnd = AfxGetMainWnd();
解决方法2:
调用AfxGetMainWnd()的地方替换成AfxGetApp()->m_pMainWnd
编译后运行问题解决
最近遇到这个问题很多次了。
以前解决好了,就忘了怎么解决,然后问题过了,之后仍要重新翻帖子找资料,这确实是微软的BUG。娘的。
解决方法:
1、无效断点所在的项目和启动项目的设置:项目->属性->配置属性->C/C++->常规->调试信息格式,这里不能为『禁用』;
回:默认的Debug模式,这个就是非禁用状态。
2、项目->属性->配置属性->链接器->调试->生成调试信息,这里设为『是』;
回:Debug模式的默认值。(手抽和VS版本混乱者排除)
3、C/C++->优化->优化选择『禁用』;
回:Debug模式的默认值。(手抽和VS版本混乱者排除)
4、删除解决方案下的.ncb文件;
回:这个方法比较管用。
5、工具->选项->调试->『要求源文件与原始版本完成匹配』去掉勾;
回:这个不建议使用,不然编译出来的版本可能不是你最新的代码编译出来的。
而且你也很难找问题,强烈BS这个方法。
6、最后在上述设置的情况下,重新编译整个解决方案;
回:这个方法比较管用。
7、回过头来,检查原来的代码到底会不会被执行到
通常来说,1,2,3点出问题的可能性最大 ,实在不行 可以通过去掉和代码的完全匹配的来强制调试,但是很可能这些地方都检查之后也不能解决你的问题。
如果这些方法都不能解决问题,ctrl+A选中不能下断点的cpp或者h文件全部内容,编辑->高级->设置选定内容的格式。
一般来说清理一次符号文件和obj文件再编译就可以调试了。
前几天大概读了下gh0st的框架 并且编译完成 着手调试 花了两天时间成功搭建好局域网利用虚拟机作为实验目标的调试环境
1.vm 6.5 绿色版 随便下一个 装上vmtool 可以顺便开个IIS测试http上线
2.找了个2000 adv server来做虚拟机 网络模式选最简单的桥接 懒得配 做好之后建立快照以便驱动崩溃后恢复
3.打开编译好的gh0st控制端生成服务端一个 选择DNS上线 ip就填控制端主机的局域网ip即可
4.将服务端文件拖到vm机上运行 ok 能够上线了
下面来配调试环境:
1.可以就用源码里的release编译 懒得新建了 改一点参数 禁用/02优化 改为/0d 调试信息格式/Zi或/ZI均可 打开调试信息输出/DEBUG 3个工程都改好后重新编译 就能断住了
2.复制重新编译生成的服务端文件和VC8远程调试工具服务端文件(remote debugger目录)到vm机 打开x86目录下的msvsmon文件 依次选择菜单栏工具->选项->无身份验证 复选允许任何用户调试 端口默认即可
3.打开vc8 gh0st解决方案 把svchost设为默认启动项目 随便下个断点(我是搜索“文件管理” ,于是断点下到了服务端处理文件管理消息的地方)
4.vc8工具栏 依次选择->调试->附加到进程->远程->输入ip地址->回车即看到目标vm机进程
5.gh0st服务端的代码是注入svchost的 由于进程列表中有多个不好确定 可以通过控制端发送一个比较大的文件 服务端打开taskmgr 被注入代码的svchost进程CPU占用率会飙升 记住pid号 回到vc8环境中 attach上去
6.断点打好 服务端随便执行点命令 ok 断住了 慢慢爽吧:)


分享到做啥
分享到收客
