|
今天研究了一下vc6函数调用,看看vc6调用函数时候都做了什么。有些意思。 我写下了如下代码:
int fun(int a,int b) { int i = 3; return a+b+i; }
int main() { int a = 1,b=2; int result ; result = fun(1,2); return result; } 非常简单。反汇编后(Debug版)变成这样
1: int fun(int a,int b) 2: { 00401020 push ebp 00401021 mov ebp,esp 00401023 sub esp,44h 00401026 push ebx 00401027 push esi 00401028 push edi 00401029 lea edi,[ebp-44h] 0040102C mov ecx,11h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 3: int i = 3; 00401038 mov dword ptr [ebp-4],3 4: return a+b+i; 0040103F mov eax,dword ptr [ebp+8] 00401042 add eax,dword ptr [ebp+0Ch] 00401045 add eax,dword ptr [ebp-4] 5: } 00401048 pop edi 00401049 pop esi 0040104A pop ebx 0040104B mov esp,ebp 0040104D pop ebp 0040104E ret
7: int main() 8: { 00401060 push ebp 00401061 mov ebp,esp 00401063 sub esp,4Ch 00401066 push ebx 00401067 push esi 00401068 push edi 00401069 lea edi,[ebp-4Ch] 0040106C mov ecx,13h 00401071 mov eax,0CCCCCCCCh 00401076 rep stos dword ptr [edi] 9: int a = 1,b=2; 00401078 mov dword ptr [ebp-4],1 0040107F mov dword ptr [ebp-8],2 10: int result ; 11: result = fun(1,2); 00401086 push 2 00401088 push 1 0040108A call @ILT+5(fun) (0040100a) 0040108F add esp,8 00401092 mov dword ptr [ebp-0Ch],eax 12: return result; 00401095 mov eax,dword ptr [ebp-0Ch] 13: } 00401098 pop edi 00401099 pop esi 0040109A pop ebx 0040109B add esp,4Ch 0040109E cmp ebp,esp 004010A0 call __chkesp (004010c0) 004010A5 mov esp,ebp 004010A7 pop ebp 004010A8 ret 我们主要来看看函数调用部分 1.参数压栈push 2 push 1 参数从右向左压栈(__cdcel),esp递减 2.调用函数 call @ILT+5(fun) (0040100a) 这条指令会把下一行代码的地址压栈,也就是函数返回地址。同时跳转到函数入口处 3.进入函数体push ebp mov ebp,esp 首先保存ebp的地址,然后把esp保存到ebp中去 00401023 sub esp,44h 00401026 push ebx 00401027 push esi 00401028 push edi 减小stack的指针(注意,stack是从内存的高端向低端生长的),为局部变量保留一些空间,这里的44h不是固定的,由编译器计算得来 00401029 lea edi,[ebp-44h] 0040102C mov ecx,11h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 用0xCC填充局部变量空间。这是Debug模式特有的,如果是字符串,你就看到被初始化成"烫烫烫烫烫烫" 至此,整个堆栈变成 |-----------------| | 局部变量2 | |-----------------| | 局部变量1 |<----ebp-4 |-----------------| | old ebp |<----ebp |-----------------| | 函数返回地址| <----ebp+4 |-----------------| | 参数1 | <----ebp+8 |-----------------| | 参数2 | |-----------------| Next: int i = 3; 00401038 mov dword ptr [ebp-4],3 这里你看到[ebp-4]就是第一个局部变量i了 0040103F mov eax,dword ptr [ebp+8] 00401042 add eax,dword ptr [ebp+0Ch] 00401045 add eax,dword ptr [ebp-4] [ebp+8],[ebp+0Ch]分别是a和b了 4.函数返回函数的结果都是放在eax中(ps:你可以在vc的watch窗口输入@EAX,就可以直接看到函数返回值了) 00401048 pop edi 00401049 pop esi 0040104A pop ebx 0040104B mov esp,ebp 0040104D pop ebp 0040104E ret 把edi,esi,ebx恢复,然后恢复esp,ebp,这时函数的返回地址就在栈顶,调用ret就可以返回了。 那如果改变函数的返回地址会怎样? ok,我们修改一下代码:
#include <stdio.h> void fun2() { printf("fun2() called"); }
int fun(int a,int b) { int i = 3; printf("return address:0x%x\n",&i+2); printf("fun2 address:0x%x\n",&fun2); /*int *p = (int*)&fun2; __asm { mov ebx,p mov dword ptr[ebp+4],ebx }*/ *(&i+2)=(int)&fun2; //modify return address return a+b+i; }
int main() { int a = 1,b=2; int result ; result = fun(1,2); return result; } Wow,这时,我们就会发现fun2被调用了。这就是Buffer overrun(缓冲溢出)所做的事情吧。 5.最后一步,调用者调整堆栈指针call @ILT+5(fun) (0040100a) add esp,8 为什么要调整呢,因为调用之前push两个参数进入栈,现在要恢复它 mov dword ptr [ebp-0Ch],eax 这句话就是享用函数调用的果实了(EAX保存了函数的返回值) ------end--------
记得以前在程序员杂志上面,看见有人提到这个问题,试了很多种方法,结果是没办法将程序删除。 真的没办法删除自身么? 请运行下面的代码:
#include <windows.h> #include <shlobj.h>
BOOL SelfDelete() { SHELLEXECUTEINFO sei; TCHAR szModule [MAX_PATH], szComspec[MAX_PATH], szParams [MAX_PATH];
// get file path names: if((GetModuleFileName(0,szModule,MAX_PATH)!=0) && (GetShortPathName(szModule,szModule,MAX_PATH)!=0) && (GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)!=0)) { // set command shell parameters lstrcpy(szParams,"/c del "); lstrcat(szParams, szModule); lstrcat(szParams, " > nul");
// set struct members sei.cbSize = sizeof(sei); sei.hwnd = 0; sei.lpVerb = "Open"; sei.lpFile = szComspec; sei.lpParameters = szParams; sei.lpDirectory = 0; sei.nShow = SW_HIDE; sei.fMask = SEE_MASK_NOCLOSEPROCESS;
// increase resource allocation to program SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// invoke command shell if(ShellExecuteEx(&sei)) { // suppress command shell process until program exits SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS); SetProcessPriorityBoost(sei.hProcess,TRUE);
// notify explorer shell of deletion SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0); return TRUE; } else // if error, normalize allocation { SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); } } return FALSE; }
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // on program exit // close all handles etc. if(!SelfDelete()) { // add error messaging } return 0; // WinMain exit }
程序的思想是通过创建一个另外的进程(ShellExecuteEx),再赋予本进程比较高的权限(SetPriorityClass), 等这个程序退出以后,那个杀进程的进程就可以删除程序了,另外程序通过SHChangeNotify通知Explorer:程序被删除。
具体API的使用方法请看MSDN.
ps:这个程序是我在老外的网站上找到的,不是我写的。我在VC6,Win2000 Professional上面调试通过
这是小明我自编的题目。 1.请在不运行程序的情况下,说出下面的程序运行后会崩溃么?如果是,在哪一行。
1 #include <stdio.h> 2 #include <memory.h> 3 class Test 4 { 5 public: 6 Test() 7 { 8 memset(this,0,sizeof(*this)); 9 } 10 int s; 11 void test() 12 { 13 printf("test()\n"); 14 } 15 void test1() 16 { 17 printf("test1():%d\n",this->s); 18 } 19 virtual void test2() 20 { 21 printf("test2()\n"); 22 } 23 }; 24 25 int main() 26 { 27 Test *s; 28 s->test(); 29 s->test2(); 30 s->test1(); 31 return 0; 32 } 2 .修改一下,又是在哪一行呢?
1 #include <stdio.h> 2 #include <memory.h> 3 class Test 4 { 5 public: 6 Test() 7 { 8 memset(this,0,sizeof(*this)); 9 } 10 int s; 11 void test() 12 { 13 printf("test()\n"); 14 } 15 void test1() 16 { 17 printf("test1():%d\n",this->s); 18 } 19 virtual void test2() 20 { 21 printf("test2()\n"); 22 } 23 }; 24 25 int main() 26 { 27 Test *s = new Test(); 28 s->test(); 29 s->test1(); 30 s->test2(); 31 return 0; 32 } 3.再修改一下,情况会如何呢?
1 #include <stdio.h> 2 #include <memory.h> 3 class Test 4 { 5 public: 6 Test() 7 { 8 memset(this,0,sizeof(*this)); 9 } 10 int s; 11 void test() 12 { 13 printf("test()\n"); 14 } 15 void test1() 16 { 17 printf("test1():%d\n",this->s); 18 } 19 virtual void test2() 20 { 21 printf("test2()\n"); 22 } 23 }; 24 25 int main() 26 { 27 Test s ; 28 s.test(); 29 s.test1(); 30 s.test2(); 31 return 0; 32 }
最后说一下答案吧 第一题是在29行,第二题在30行,最后一题不报错。 如果你不知道为什么,建议看看<<Inside the c++ Object Model>>
当我回想起来我刚刚学习C语言,Turbo C2.0提供的丰富的函数,可以让枯燥的文本界面,显示出花花绿绿的文字界面。在windows时代,这些函数都不在标准库中。不过WINAPI可以帮你实现。
#include <windows.h> #include <string> #include <ctime>
enum Colors { BLACK = 0, BLUE = 1, DARK_GREEN = 2, LIGHT_BLUE = 3, RED = 4, PURPLE = 5, ORANGE = 6, GREY = 7, DARKER_GREY = 8, MEDIUM_BLUE = 9, LIGHT_GREEN = 10, TEAL = 11, RED_ORANGE = 12, LIGHT_PURPLE = 13, YELLOW = 14, WHITE = 15 };
void set_cursor(short x, short y) { COORD point = {x, y}; ::SetConsoleCursorPosition(::GetStdHandle(STD_OUTPUT_HANDLE), point); }
void set_color(unsigned short color) { ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), color); }
void delay(unsigned int delay) { ::Sleep(delay); }
void set_title(std::string title) { ::SetConsoleTitle(title.c_str()); }
void show_cursor(bool show, int size = 25) { CONSOLE_CURSOR_INFO cci; if (size <= 0) size = 1; if (size > 100) size = 100; cci.dwSize = size; cci.bVisible = show; ::SetConsoleCursorInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &cci); }
void clear_screen() { system("cls"); } 代码很简单,不用多作解释了
条件:不借助任何文件操作,输出程序的source code. 程序:
#include <stdio.h> char c[] = {0x7d,0x3b,0xa,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0xa,0x7b,0xa,0x20,0x20,0x20,0x20,0x70,0x72,0x69,0x6e,0x74,0x66,0x28,0x22,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x73,0x74,0x64,0x69,0x6f,0x2e,0x68,0x3e,0x5c,0x6e,0x63,0x68,0x61,0x72,0x20,0x63,0x5b,0x5d,0x20,0x3d,0x20,0x7b,0x22,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x3d,0x30,0x3b,0x69,0x3c,0x73,0x69,0x7a,0x65,0x6f,0x66,0x28,0x63,0x29,0x3b,0x2b,0x2b,0x69,0x29,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x72,0x69,0x6e,0x74,0x66,0x28,0x22,0x30,0x78,0x25,0x78,0x2c,0x22,0x2c,0x63,0x5b,0x69,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x70,0x72,0x69,0x6e,0x74,0x66,0x28,0x22,0x25,0x73,0x22,0x2c,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x30,0x3b,0xa,0x7d,}; int main() { printf("#include <stdio.h>\nchar c[] = {"); for(int i=0;i<sizeof(c);++i) { printf("0x%x,",c[i]); } printf("%s",c); return 0; } 至于怎么做到的,大家可以自己琢磨 不过不要以为只有C能做到,java也可以哦
我接收到一个任务,是把公司的一个产品从vc6迁移到vs2005,结果发现了很多的warning和error
warning 主要是使用了strcpy,strcat这样的函数 这些在2005中都是unsafe_api. 在vs2005都推荐使用strcpy_s,strcat_s. 我想微软这么做固然跟C++ standard有偏差 但是这些函数的使用确实造成了微软产品经常有的漏洞 微软深受这些函数的危害阿 所以在vs2005这些都是warning
error的类型主要是以下几种,多半和STL有关
1.include 带.h的旧式头文件,比如 #include <iostream.h>改为include <iostream>
2.vc6的string iterator的 char *,而vs2005中却不是 strcpy(s.begin(), str);是不能compile的,应改为strcpy((char *) s.c_str(),str);
3.函数返回类型不支持缺省是int missing type specifier - int assumed. Note: c++ does not support default-int <Code> extern IsWindowsNT();
<Fix> extern int IsWindowsNT();
www.ogre3d.org OGRE: A 3D library for OpenGL and/or Direct3D
template <typename T> class Singleton { protected: static T* ms_Singleton;
public: Singleton( void ) { assert( !ms_Singleton ); ms_Singleton = static_cast< T* >( this ); } ~Singleton( void ) { assert( ms_Singleton ); ms_Singleton = 0; } static T& getSingleton( void ) { assert( ms_Singleton ); return ( *ms_Singleton ); } static T* getSingletonPtr( void ) { return ( ms_Singleton ); } };
//client端的代码 //Singleton的类 //Root.h class Root:public Singleton<Root> { public: void Use(); };
//初始化 //Root.cpp Root * Singleton<Root>::ms_Singleton =0; Root g_root;//must declare once only
//使用 //Test.Cpp Root::getSingleton().Use();
很简单,使用的技巧是template base class 这种实现方法的好处是复用性好。 不过使用者要声明Root * Singleton<Root>::ms_Singleton =0;挺讨厌的
改进方法:template static member function 也就是改变声明一个static T*在template class,换成使用静态成员函数取出instance
template <typename T> class Singleton { private: static T* & ms_Singleton() { static T* ms_Singleton_ =0 ; return ms_Singleton_; } public: Singleton( void ) { assert( !ms_Singleton() ); ms_Singleton() = static_cast< T* >( this ); } ~Singleton( void ) { assert( ms_Singleton() ); ms_Singleton() = 0; } static T& getSingleton( void ) { assert( ms_Singleton() ); return ( *ms_Singleton() ); } static T* getSingletonPtr( void ) { return ( ms_Singleton() ); } };
1.download ACE from: www.cs.wustl.edu/~schmidt/ACE.html
2.build ACE in Visual Studio,generate ACE.lib(dll)[for release] & ACEd.lib(dll)[for debug]
3.create a empty project named: ACETest
4.add ACE path into Project Include Path and add ACE/lib into project lib path
5.create a file(hello.cpp)
#ifdef _DEBUG #pragma comment(lib,"ACED.lib") #else #pragma comment(lib,"ACE.lib") #endif
#include <ace/OS_main.h> #include <ace/ACE.h> #include <ace/Log_Msg.h> #include <ace/SOCK_Connector.h>
int main(int argc, char *argv[]) { ACE_INET_Addr addr(1500,"127.0.0.1"); //remote address ACE_SOCK_Connector con; // connetor for socket client ACE_SOCK_Stream stream; // stream is for socket read/write
if(con.connect(stream,addr)==-1) //connect to remote address { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("connection failed"))); return 1; }
const char msg[] = "Hello,ACE!";
stream.send_n(msg,sizeof(msg)); // send_n function send exactly n bytes
char buffer[1024] = {0};
if(stream.recv(buffer,sizeof(buffer)-1)==-1) // just call socket recv { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("recv failed"))); return 1; } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) recv:%s\n"), buffer));
if (stream.close () == -1) //close the connection { ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close"))); return 1; }
return 0; }
6.build & execute
摘要: 1.boost::any
boost::any是一种通用的数据类型,可以将各种类型包装后统一放入容器内最重要的它是类型安全的。有点象COM里面的variant.
使用方法:any::type() 返回包装的类型any_cast可用于any到其他类型的转化
#include <boost/any.hpp>void test_any(){ ... 阅读全文
|