不要太武断呀,不然会禁锢思想的,岂不是和电脑一样了。看了帖子:
http://community.csdn.net/Expert/topic/3770/3770688.xml?temp=4.357547E-02
好像很多人都这样认为,希望大家重新认识这个问题。具体方法没什么价值,只希望扩展大家的思维。
以下是代码,操作系统 Windows XP SP1 。#include <iostream>
using namespace std;
//控制台程序,在VC++6、VC.NET Debug模式下,采用编译器默认设置调试成功。
//此方法与具体代码和编译器相关,搞着玩的,没什么实用价值,只作验证。//此函数只有跳出功能,eax移作他用,返回值无意义。
unsigned short breakfunc(unsigned short a)
{
__asm
{
//begin1 手工恢复堆栈现场,使堆栈成为此函数调用前的状态
pop edi;
pop esi;
pop ebx;
mov esp,ebp;
pop ebp;
pop eax;//eax中保存了进程中此函数执行完后下一条将执行的语句的地址。
//end1 --------------------------------------------- add esp,4;//此条语句为了平衡main函数的堆栈。与具体代码相关。
add eax,9;//在eax中存放  cout<<"break out and goto here"<<endl; 语句地址。
jmp eax;//挑转。
}
return 0;
}void main()
{
 unsigned short a=123,b;
 while(1)
 b=breakfunc(a);//不是死循环啊。  cout<<"break out and goto here"<<endl;
 cout <<b<<endl;
}各位有何高见尽管发表,洗耳恭听。若您能够提供有价值的 !(常规) 编程方法,50送上。

解决方案 »

  1.   

    你这样写有啥意思啊?跳出了循环当然不是死循环  while(1)
     {
     goto jump;
     }
    jump:
     cout<<"break out and goto here"<<endl;
     cout <<b<<endl;
      

  2.   

    原帖地址:
    http://community.csdn.net/Expert/topic/3770/3770688.xml?temp=4.357547E-02
      

  3.   

    呵呵,我看了. ^o^在汇编里面,通常都是 call someproc然后someproc里面pop eax来得到EIP的值,在VC里面可以用GetThreadContext得到线程的CONTEXT,然后用CONTEXT的DWORD Eip来获取的,不过这个函数通常用来调试或者跟踪API,比较麻烦,如果让我选,我也用pop eax这种办法.不过话说回来了,如果你不跳转,那就是死循环.
      

  4.   

    对呀,调试API我没用过,现在正在学汇编,所以用汇编了。另外要平衡堆栈,不知C++能不能做到,如果可以,可否告诉我如何去做,感激涕零。
      

  5.   

    这个问题这样讨论没有什么意义。while(1)里面执行你的breakfunc相当于Label:                ;while(1)的循环
         ......
         call [breakfunc]
         ......     JMP Labelbreakfunc:
         ......
         JMP EAX ;如果你在这里JMP到breakfunc之外了,
                 ;当然无法ret了,所以也就不会死循环了。
                 ;但是有可能程序红叉叉啊
         ......
         ret
      

  6.   

    对呀,当时调的时候几次都差一点“当”掉,好在XP稳定性不错,虽然是D版的。不过只要正确平衡堆栈并跳转到正确地址就没什么问题了。这个问题是没什么意思,有意思的是那么多人都是如此的遵循C++的语言规则。
      

  7.   

    hehe,我觉得规则有利于创建安全代码和团队协作
    你这样适合做创造型的工作,例如:hacker
      

  8.   

    C约定调用模式是右边的参数先入堆栈,然而子程序不负责校正堆栈,必须由调用者自己平衡堆栈。在 Win32 汇编中,约定使用 StdCall 方式,所以我们要在程序开始的时候使用 .model stdcall 语句。
    也就是说,在 API 或子程序中,最右边的参数先入堆栈,然后子程序在返回的时候负责校正堆栈,调用者不必平衡堆栈。
    唯一一个特殊的win api 是wsprintf,它是C 约定的,所以必须自己平衡堆栈。
    以下内容为程序代码:;; 
    ;;测试调用API中堆栈的平衡.wsprintf 是唯一一个特殊,它是C 约定的,所以必须自己平衡堆栈
    ;; ml /c /coff test.asm      link /subsystem:windows test.obj
    ;;  .386
      .model flat, stdcall
      option casemap :none   ; case sensitiveinclude  windows.inc
    include  user32.inc
    include  kernel32.incincludelib user32.lib
    includelib kernel32.lib  .data?
    szBuffer db 256 dup (?)  .data
    szFormat db '%d%d',0  .code
    SomeFun  proc  push 9
      push 9
      push offset szFormat
      push offset szBuffer
      call wsprintf
      ;;;;;;;;;;;;;;;;;;;;;
      add esp,4*4 ;;必须得平衡堆栈
      ;;;;;;;;;;;;;;;;;;;;;;;;;;
      ret
    SomeFun  endp
      start:  
      call SomeFun
      
      push 0
      push 0
      push offset szBuffer
      push 0
      call MessageBox 
      
      invoke ExitProcess,NULLend start
      

  9.   

    PS:在VC中恢复堆栈和平衡堆栈都是由系统自动给你完成
      

  10.   

    你在VC里面调试代码的时候反汇编一下就会看到
    pop edi;
    pop esi;
    pop ebx;
    mov esp,ebp;
    pop ebp;
    以及add esp,4,
      

  11.   

    masm32 提供了完整的库函数,照你这样说, wsprintf 函数的调用使用常规的方法应该是不行了,对吗?
      

  12.   

    add esp,4*4 ;;必须得平衡堆栈
    这句什么意思,编译不通过。
      

  13.   

    将[color=Red]去掉就好了。我知道你什么意思了,StdCall 方式使用call直接调用,只压栈,不必出栈;C方式使用call直接调用,压栈后还要负责出栈。另外还有还有PASCAL方式,除了压栈顺序相反外,其它同StdCall 方式。其实masm32 提供了invoke 伪指令,直接使用它就行了,调用方式没变化。
      

  14.   

    呵呵,怎么还在啊?那就帮你仔细说说吧//__cdecl:实参的压栈顺序是从右到左。由主调用函数负责参数压栈并且恢复堆栈,
    //由于主调用函数管理堆栈,所以可以实现变参函数。
    void __cdecl test1(int m,int n)
    {
    return;// pop         edi
    // pop         esi
    // pop         ebx
    // mov         esp,ebp
    // pop         ebp
    // ret
    }//__stdcall:实参的压栈顺序是从右到左。在主调用函数中负责压栈,在被调用函数中负责平衡堆栈。
    //因此不能实现变参函数,因为被调函数不能事先知道弹栈数量。
    void __stdcall test2(int m,int n)
    {
    return;// pop         edi
    // pop         esi
    // pop         ebx
    // mov         esp,ebp
    // pop         ebp
    // ret         8 ;平衡堆栈
    }void main()
    {
    int m = 123,n = 321;
    test1(m,n);
    // mov         eax,dword ptr [ebp-8]
    // push        eax ;进栈
    // mov         ecx,dword ptr [ebp-4]
    // push        ecx ;进栈
    // call        @ILT+655(test1) (00401294)
    // add         esp,8 ;平衡堆栈
    test2(m,n);
    // mov         edx,dword ptr [ebp-8]
    // push        edx ;进栈
    // mov         eax,dword ptr [ebp-4]
    // push        eax ;进栈
    // call        @ILT+565(test2) (0040123a)
    unsigned short a = 123,b = 234;
    static unsigned int c=345;
    char d = 12;
    while(1)
    b = breakfunc(a,c,d);
    cout<<"break out and goto here"<<endl;
    cout <<b<<endl;
    }//由于传递的是变量的地址(类似指针),变量地址为4个字节,所以平衡堆栈时add esp,4*N(N = 变量个数)
      

  15.   

    add esp,4;//此条语句为了平衡main函数的堆栈。与具体代码相关。
    这个是平衡breakfunc的堆栈StdCall 方式使用call直接调用,只压栈,不必出栈;C方式使用call直接调用,压栈后还要负责出栈。
    //不是不出栈,是被调用者不负责平衡堆栈
    PS:这些东西我不敢保证VC里面所有的三角都知道,但是星星们肯定知道,也许我比较菜才会回答,(注意我得昵称 ^o^)而且在VC里面,这所有的东西都是由系统帮你完成的.那些汇编代码都是VC在调试的时候反汇编得到
      

  16.   

    多谢      EnochShen(小疯子:真的好菜—知耻而后勇!)好像大家都以为我在故弄玄虚,好冤枉啊!我只是一个普通的编程爱好者,连程序员都没得当(好惨),因为喜欢编程,所以常来这里喝点水,哪敢在这么多大虾面前班门弄斧,只是感觉编程是通过计算机语言来控制计算机的,语言本身只是形式,我们最终使用的还是计算机(了解计算机如何工作比学习编程更有趣)。如果只是停留在语言表面,忽略了计算机本身,他编程水平再高,我也不会羡慕他的。我知道如果要求这样做,很多人都能够实现,只是没往那里想而已。也可能是现在正在学汇编的原因吧,思想有点不大正常。