|
今天研究了一下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(){ ... 閱讀全文
|