有很多人认为汇编已经失去了用武之地,包括曾经的我。我用的是visual c++6.0。现在我要问你一个问题。
a=10,b=20。你现在要使a和b的值交换。你有什么办法。这是教科书上说得很多的方法。第一种方法:
#include <iostream.h>
void main()
{
  int a,b,c;
  a=10;
  b=20;
  c=a;
a=b;
b=c;
  cout<<“a=”<<a<<“,b=”<<b;
}
输出当然是a=20,b=10
该方法引入了一个新的变量,即多开辟了一个DWORD的空间。现在请你再看我的实现方法。第二种方法:(把加了下划线的部分改成如下代码)
_asm
  {
    push a
    push b
    pop a
    pop b
  }
我稍微解释一下:_asm告诉编译器,我要使用内联汇编代码了。换成__asm也一样(注意这里有两根英文半角下划线,前面的是一条)。上面的那段代码也等价于
_asm  push a
_asm  push b
_asm  pop a
_asm  pop b
看来还是用花括号{}来得方便。
上述,我在c++中使用了内联汇编,说实话,这种方法比第一种方法来得差。因为我的两个push就开辟了2个DWORD空间。
有没有办法不开辟额外的空间呢?请看第三种实现方法:
  _asm
  {
    mov eax,a
    mov ebx,b
    xchg eax,ebx
    mov a,eax
    mov b,ebx
  }
在听取下面的朋友的批评之后,上面这段代码不免有脱了裤子放屁之嫌,应改为
_asm
  {
    mov eax,a
    xchg eax,b
    mov a,eax
  }
这样看起来就比较好了。因为第一段代码执行的时候也要先把值移动到寄存器中。
要说内联汇编最重要的作用就是在写溢出代码和注册机中。如果你有志于学这些东西,那么下面的文章你要好好看了。可惜的是内联汇编不是宏汇编,一些伪指令内联汇编中是不能用的,比如说
.if
.elseif
.endif
虽然在很多时候写代码不方便了,但是勉强还行。
很重要的一点:一般来说,在_asm块开始的时候,你不应该假定某个寄存器中包含着值。也就是说所有的寄存器都是可用的。当然不指eip,esp,ebp用这些寄存器用得不好你会死得很难看,是cracker都知道这三个寄存器的作用吧!还要注意PUSH,POP配对。这是为了堆栈平衡。
现在基本上我们迈出了学习内联汇编的第一步,也是一大步。剩下的都只是一些细节了。
现在我要编写一个函数。
int cmpare(int a,int b)
{
   _asm
   {
     mov eax,a
       cmp eax,b
     jge line1;都是因为伪指令不能用,否则这里肯定是用.if伪指令更容易看懂。
     mov eax,b
line1:
   }
}
我写这个函数的目的是为了告诉大家整数在默认情况下是采用eax来作为返回值的,还有就是在内联汇编中推荐用汇编自己的注释符号“;”,虽然说c++的注释符号也可以使用。这里我的a=10,b=20。虽然在编译时c++提示我如图1
 
http://bbs.pediy.com/upload/2006/4/image/image001.png_820.png 
图1

但是真正执行cout<<cmpare(10,20)输出的是20。另外浮点通过st(0)返回值。
sz[1]= 3;
  _asm
  {
    mov eax,sz[1]
    mov a,eax
  }
    cout<<a;
你知道这将会输出什么吗?你绝对意想不到,我告诉你,在我这里是63753420。是不是很迷惑。当然开始的时候我也很迷糊,咱们先把mov eax,sz[1]改成mov eax,sz[4]然后再编译,看现在已经达到了我们预想的情况a=3。至于为什么吗?原因是:[]是c++和汇编共同包含的操作符,会被编译成汇编的操作符。而一个int是4字节,所以我们的代码应该是sz[4]而不是sz[1],后面的下标实际上是起着寻址的作用。前面的数组名sz是起着基地址的作用。如果不好理解你应该用OD调试看看。为了简便我们的写法。我觉得我们可以这样操作数组:
sz[xl*lx]。
lx是我们先前就定义的各种类型常量,xl是序列,即一般的下标值。这个类型的常量我们可以用
_asm  
  {
    mov eax,type sz;sz是数组或者变量名
    mov a,eax
  }
  cout<<a;
这样就可以输出这种数据的每个数据所占的大小了,单位是byte。
内联汇编学习的路还很远,大家要努力学习。
学习汇编,最常用的东西除了寄存器就是指针了,在汇编中指针的反映就是使用[ address]。
不懂事的程序员往往很容易写出这样的代码:
_asm
  {
    lea eax,a
    mov b,[eax]
  }
这当然是错误的,这反映为对汇编代码知识不是很了解。因为mov指令是不可以这样使用的
mov m32,m32;m32表示32位储存器。所以只是应该改成下面这样。
_asm
  {
    lea eax,a
    mov ebx,[eax]
    mov b,ebx
  }
这样a的值才顺利到达了变量b这里。下面再举几个例子,只是希望对一些汇编代码不太熟悉的小鸟一些帮助。我们继续。
a=0x120;
_asm
  {
    lea ebx,a
    movzx eax, byte ptr [ebx]
    mov b,eax
  }
cout<<b;
b等于多少?知道吗?如果你说的是32,那么你很聪明,汇编学得还不错。如果你说的是48,那么你已经把整数变量和字符串混起来了,在ASCII码表中0的表示就是48。如果你说的是1,那么你应该还不懂汇编。
在内存中ebx指向的内存应该是20 01。因为所有的东西在内存中都是反向存储的。在计算机运算的时候就是01 20。在出一道练习题
在内存中20 30 04这个值是多少?答案:04 30 20。
这里我们要特别注意的是在写注册机时,你得先知道,你输入的值,它是在当整数计算,还是字符串计算。通常它取得到的都是字符串,有些软件会把字符串转换成整数。
这么点东西,我学习了一天啊!惭愧,惭愧……
最后夸奖自己一句,这篇文章真的很好,很难找到这么浅显易懂的文章啊!^_^

  • 标 题:Re: 【原创】内联汇编
  • 作 者:foxabu
  • 时 间:2007-02-11 15:51

引用:
最初由 kflnig 发布
有很多人认为汇编已经失去了用武之地,包括曾经的我。我用的是visual c++6.0。现在我要问你一个问题。
a=10,b=20。你现在要使a和b的值交换。你有什么办法。这是教科书上说得很多的方法。第一种方法:
#include <iostream.h>
void main()
{
........ 
我只说“你现在要使a和b的值交换”
千万不要小视编译器优化。。具体你用Release版编译一下就知道了
另外MSVCRT也有库函数可以用:swap。
Swaps bytes.
void _swab(
   char *src,
   char *dest,
   int n 
);

  • 标 题:答复
  • 作 者:testttt
  • 时 间:2007-02-11 16:24

我觉得VC的优化是做的不错,如果是为提高效率而使用内联汇编,基本上没有必要。

调过几个Borland C++ 1999的程序,垃圾代码不是一般的多

  • 标 题:答复
  • 作 者:jjnet
  • 时 间:2007-02-12 14:29

asm
  {
    mov eax,a
    mov ebx,b
    xchg eax,ebx  //用西裤哥的话来说,这叫脱了裤子打屁
    mov a,eax
    mov b,ebx
  }
楼上的指出。。。。。。。 


===========
教学的话, 可以这么写

asm
  {
    mov eax,a
    xchg eax,b
    mov a,eax
  }

  • 标 题:内联汇编补遗
  • 作 者:kflnig
  • 时 间:2007-02-24 16:51

内联汇编补遗
kflnig狂枫
    上次写了一篇内联汇编,但是有点不周全的地方,所以写了这篇补遗。
    针对大家的批评,尤其是我那个XCHG的使用,简直是脱了裤子放屁,自己汇编功底差,一时误记,在此向大家道歉。
    mov ebx,dword ptr[eax]简单点吧,mov ebx,[eax],64bit的机器我不敢说正确,但是32bit的机器中还是后一种写法更加简单,因为指针指向的值,32bit中默认就当作dword类型了。
    结构类型,也得说一下。这个东西还是很头痛的,没有汇编写过程序的一般人是不会知道的。Yonsm 大哥的内联汇编教程中有提到。这里我再补充一下。
#include <iostream.h>
struct first 
       { 
           int a; 
       }; 
      struct first str; 
void main()
{      
str.a=10;
      __asm 
       { 
           MOV EBX, OFFSET str 
           MOV [EBX]str.a,20 
       }
   cout<<str.a;

     注意定义str结构类型的位置。
Yonsm 大哥的内联汇编教程说得对,都是非常对的,可是他功力太高,忘了提醒我们一点OFFSET的特点。
    OFFSET不能用于获取局部变量的地址,只是由于OFFSET伪指令确定偏移量是在编译时预先确定好的,对局部变量在编译时是不确定的,它与堆栈寄存器的取值有关,而堆栈寄存器的取值随着执行环境的不同而不同。所以在声明struct变量的时候应该放在开头,应该是全局变量,大家注意核实,我不想误导你。对c语言这种很拗口的语法,我也并不是很清楚。当然还有一类人是不看上文也绝对不会写错的。懒人。
   因为懒人通常是
struct first 
       { 
           int a; 
       }str; 
    这么写的。这样也可以。
    现在明白了吧!我只是提醒这个,Yonsm 大哥提到的,可以省略些代码的写法,你不妨自己去看看他的教程。
若你看过crack7中的《Thinking in ASM 》,那么你不妨再看看我的。
他们都在大谈swap(a,b)之后a,b的值有没有换过。我来一个稍微变态的,也作为对使用内联汇编者的提醒,小心这种情况。
#include <iostream.h>
void swap(int &a, int &b)
{
  __asm
  {
  push a
  push b
  pop a
  pop b
  }
}
void main()
{
  int a,b;
  cin>>a>>b;
  swap(a,b);
  cout<<a1<<b1;
}
答案当然是没有啦。

  • 标 题:答复
  • 作 者:forgot
  • 时 间:2007-02-28 22:09

好像优化newbie手册。。。

一个原则是最好不要使用堆栈,时空都耗费很厉害
找一个reg来交换吧。。。
不过编译器会干的,研究这种优化上有意义吗


这些其实就是手熟而已,经常用asm作开发的都知道。但要他们研究brainfuck可能会晕

  • 标 题:答复
  • 作 者:kflnig
  • 时 间:2007-03-14 20:26

内联汇编再补遗[大结局]
kflnig中文名:狂枫
    声明:此文或许会有多处错误,大家请踊跃发言!提出错误!本文基于WINXP,VC6.0调试通过。
  一篇内联汇编产生了这么多的补遗,^_^如果你内联汇编一般,看了这篇文章那么肯定吓一跳。因为我本来的题目是7474742。就是气死气死气死你!它早就把我气死过一回了。我的目标就是把你搞混到不敢学内联汇编!^_^
如果你对自己的内联汇编很有信心,那么看下去。
说点不重要的。也是复习一下。
void main()
{
  int a[10];
  a[0]=10;
  a[1]=20;
  __asm
  {
    mov eax,a[0]
    add eax,a[4]
    mov a[0],eax
  }
  cout<<a[0];
}
我在第一篇《内联汇编》中已经说过。这样是输出a[0]+a[1]。
我们再来看。法一:
void main()
{
  int bl1,bl2;
  bl1=10;
  bl2=20;
  __asm
  {
    mov eax,bl1
    xchg eax,bl2
    mov bl1,eax
  }
  cout<<bl1<<bl2;
}
这个更加简单了就是bl1和bl2交换。那么法二:
void main()
{
  int bl1,bl2;
  bl1=10;
  bl2=20;
  __asm
  {
    lea eax,bl1
    mov ebx,[eax]

    lea ecx,bl2
    mov edx,[ecx]

    xchg ebx,edx

    mov [ecx],edx
    mov [eax],ebx
  }
  cout<<bl1<<bl2;
}
上面的代码毫无优化可言,我只是为了对照c++源码的翻译式写法。也是bl1,bl2的值相互交换。
从代码的高级程度无疑是法一高得多。但是,我推荐小鸟牢牢掌握法二。要问我为什么,先等等,待会儿说! 
我们知道
void swap(int &a,int &b)
{
__asm{
    push  a
    push  b
    pop   a
    pop   b
    }
}
这样执行
c=10;
d=20;
swap(c,d);
c依旧是10而d依旧是20。我没有什么好办法只好来一个笨点的。
void swap(int &a,int &b)
{
  int a1,b1;
  a1=a;
  b1=b;
  __asm
  {
    mov eax,a1
    xchg eax,b1
    mov b1,eax
  }
  a=a1;
  b=b1;  
}
言归正传。
void add(int c[10],int d[10])
{
  __asm
  {
    mov eax,c[0]
    add eax,d[0]
    mov c[0],eax
  }
  cout<<c[0];
}
void swap(int c[10],int d[10])
{
  __asm
  {
    mov eax,c[0]
    xchg eax,d[0]
    mov c[0],eax
  }
  cout<<c[0]<<d[0];
}
void main()
{
  int a[10],b[10],bl1,bl2;
  a[0]=10;
  b[0]=20;
  swap(a,b);
}
这样是对的输出20,10。但是假如把swap(a,b);改成add(a,b);那么只会跳出一个丑陋的对话框。因为swap(a,b)没有问题,所以原因不再其它而在add函数自己内部的__asm中。这就是传数组,这个很特别的特例。我们在add函数里操纵的是数组的地址。如果你要add那么用类法二的方法吧!
void add(int c[10],int d[10])
{
  __asm
  {
    mov eax,c[0]
    mov ebx,[eax]

    mov ecx,d[0]
    add ebx,[ecx]

    mov [eax],ebx
  }
  cout<<c[0];
}
这样就不会有丑陋的对话框了。以后不要再把两个地址加起来了。在函数中处理传入的数组尤其小心!所以上面的swap(a,b)其实是很高级的交换c[0],d[0]的地址。
好了我的《内联汇编》教程(共三篇)结束了。写得很详细。相信大家的内联汇编技术也很强了。我也不弱^_^自己多用才是可以提高!
这是我在寒假里给大家写的最后一篇文章了。再见!
最后我来布置一道终极考试题目a=10;这用内联汇编怎么写。
这是一题怎么写都不会错的。比如
__asm
{
mov    eax,10
mov    a,eax
}
或者
_asm
{
    mov    eax,10
    mov   [a],eax
}
都对!不要问我为什么,我也不知道,这是VC的编译器这么说的。
所以我们可以知道,假如是这样:
void add(int c,int d)
{
  __asm
  {
    mov eax,c
    add eax,d
    mov c,eax
  }
  cout<<c;
}
这个add函数可以安全执行。我相信此刻你已被内联汇编搞混了头脑!到底该怎么用,还是自己去试验然后总结吧!