论坛: 编程破解 标题: [20cn讲座]跟我学汇编 复制本贴地址    
作者: ljsh012 [ljsh012]    论坛用户   登录
作者: 286 [unique]  版主 回复  收藏 
1 本讲座以汇编初学者或对汇编一点也不了解的读者为对象,汇编高手不属于该范围,但强烈建议高手指导并增补、修改本文。
2 任何读者可以跟此贴,提出疑问,或解答其中的问题,但对于所有跟贴,水贴、内容有错、毫不相干贴将直接删除,有意义的贴可能会合并到下一讲的内容中,合并后也将删除,请跟贴者谅解。同时按学习进步,请提问者逐步提,不要我没开口,你就问怎么编个病毒的问题。
3 借以抛砖引玉,但不希望大家只朝我扔砖头,希望大家踊跃思考,使之完善。



--------------------------------------------------------------------------------
纵行下天,286足矣。

地主 发表时间: 04-11-15 10:09

--------------------------------------------------------------------------------
回复: 286 [unique]  版主 回复  收藏 
第负一讲 学习汇编前你应该知道的知识

大家坐好了,。不要,不要,不要,男女同学不要相互,女同学不要对我....
 
1 汇编需要什么工具和程序,到哪里下载?
目前阶段,汇编程序仅需要两个程序就够了。 masm.exe,link.exe。二者可由http://www.20cn.org/~unique/Download/Tool/masm.rar下载,前者是编译程序,后者是链接程序。
另外,为了验证和调试程序,还需要一个程序debug.exe,该程序由windows本身就提供,所以就不提供下载地址了。
将二者下载后,放到某一个目录中(任意目录都可以),考虑到很多命令需要通过键盘敲入,所以建议你不要把文件放入到长文件名目录、中文目录或很深的目录中。比如你可以建一个“D:\Masm”目录,并建议此后的程序都放这个目录,此后称这个目录为汇编目录。


2 学习汇编需要有哪些编程方面的知识。
没有任何编程方面的知识,学习此语言等于缘木求鱼,所以请放弃学习的想法。一般来说至少要知道如下几点:
*)程序的运行逻辑结构有顺序(按语句依次执行)、分支结构(IF...THEN...ELSE...),循环结构(FOR...NEXT)三种结构。
*)知道什么是子程序,什么是调用。
*)汇编程序员的视角。不同编程视角编程要求是不一样的。比如删除文件,
  >>用户的视角是找到“删除”按钮或菜单,然后单击一下即可。
  >>高级程序员的视角是知道删除的文件,并发出删除命令。这些通过API实现。
  >>汇编程员的视角是得到要删除的文件名,找到该文件所在位置,通过调用删除“中断命令”进行删除。
  >>操作系统开发人员的视角则是接到删除命令后,先找到系统根目录区,由根目录区的链接依次找到子目录区,直到找到要删除的文件,然后按照操作系统删除文件的规则对该文件名进行修改。比如DOS,只把第一个字符改成"?"。

按程序语句等价的角度看,一行VB的打印语句,用汇编实现大约需要一百二十多行。知道汇编语言的视角后就要知道,前面的道路是坎坷的,没有耐心是不行的。想通过几分钟几行程序就完成很复杂的操作不是件容易的事。

3 学汇编有什么用?
汇编产生于DOS时代或更早,而现在是Windows时代,所以可能遗憾地说:尽管还有批牛人在用汇编开发核心级程序,但我们几乎没什么用,除了必要时间能拿来分析一两个程序的部分代码之外,别的也就没干什么用了。并且并不是所有的汇编命令都能在windows下使用。而泛泛地追求“时髦”而学本语言,最后的结果是损了夫人又折兵。所以学之前你要考虑好。我劝那些为了当“黑客”而学汇编的人就此止步。


--------------------------------------------------------------------------------
纵行下天,286足矣。

B1层 发表时间: 04-11-15 14:40

--------------------------------------------------------------------------------
回复: 286 [unique]  版主 回复  收藏 
第零讲 预备知识

1 一个汇编程序的编译过程是怎么样的。
1)首先你需要找一个编辑器,编辑器用任何“纯文本”编辑器都可以。比如记事本。编好以后保存到汇编目录中。扩展名为asm,比如myfirst.asm。但这里建议你找一个能显示出当前行的编译器。这样出错后排错很容易。
2)然后在DOS下进入D:\Masm目录中,输入“masm myfirst.asm",如果有错系统会提示出错的行位置和出错原因。
3)然后再输入“link myfirst.obj”,即可看到当前目录下有一个myfirst.exe程序。

2 宏汇编和汇编有什么区别吗?
二者的区别在于前者提供宏,后者不提供。后者已找不到了,所以你可以认为二者没有区别。

3 机器语言、汇编语言、高级语言的关系
最早的计算机采用机器语言,这种语言直接用二进制数表示,通过直接输入二进制数,插拔电路板等实现,这种“编程”很容易出错,每个命令都是通过查命令表实现,既然是通过“查表”实现的,那当然也可以让计算机来代替人查表实现了。于是就产生了汇编语言,所以不管别人怎么定义机、汇语言,我就认为,二者是等价。后来人们发现,用汇编语言编某一功能的时候,连续一段代码都是相同或相似,于是就考虑用一句语言来代替这一段汇编语言,于是就产生了高级语言。因此,所有高级语言都能转化成汇编语言,而所以汇编语言又可转化成机器语言。反之,所有机器语言可以转成汇编语言(因为二者等价)。但并不是所以汇编语言都能转成高级语言。

4 计算机的组成
通常都把计算机定义成五部分:运算器、控制器、存储器、输入系统、输出系统。
为了简单其间,我们如此理解:运算器+控制器=CPU。存储器=内存(暂不包括外存,永不包括CACHE)。输入系统=键盘(不包括鼠标),输入系统=显示器(不包括打印机,绘图仪)。

5 寄存器和内存的区别
寄存器在CPU中。内存在内存条中。前者的速度比后者快100倍左右。后面的程序要求每条指定要么没有内存数据,要么在有一个寄存器的参与下有一个内存数据。(也就是说,不存在只访问内存的指令)。

6 汇编语言的计数
与生活中的计数不一样,汇编中的计数是从0开始的。比如16个计数,则是从0~15,而不是生活中的1~16。这一点看起来简单,真运算起来就不是件容易的事了,不信等着瞧。

7 进制问题
又与生活中不一样的地方是进制。切记下面的常识:
*)计算机内部存储都用二进制。
*)我们的汇编源程序默认都用十进制。(除非你指明类型)
*)我们用的调试程序debug默认的都是十六进制。(无法指明其他类型)
其中十六进制的十六个个位数依次是:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。

8 进制转换
一个比较简单的方法是查表法。
十进制 十六进制 二进制
  0  0  0000
  1  1  0001
  2  2  0010
  3  3  0011
  4  4  0100
  5  5  0101
  6  6  0110
  7  7  0111
  8  8  1000
  9  9  1001
  10  A  1010
  11  B  1011
  12  C  1100
  13  D  1101
  14  E  1110
  15  F  1111
 
好了,结合6,7,8三条。大家来算一个“题”。某一组数据显示时,每个数据占了四个位置,
每行共十六个。问:十六进制的13位置在哪里(第几行,第几列)。
格式如下:m m m m n n n n o o o o p p p p '注:之所以没用ABC是怕与上面十六进制弄混。
      r r r r s s s s t t t t u u u u
      ........

[此贴被 286(unique) 在 11月15日15时15分 编辑过]


[此贴被 286(unique) 在 11月15日16时09分 编辑过]


--------------------------------------------------------------------------------
纵行下天,286足矣。

B2层 发表时间: 04-11-15 14:41

--------------------------------------------------------------------------------
回复: 286 [unique]  版主 回复  收藏 
第一讲 基础知识

1 访问内存
程序在内存中,访问内存是几乎每一程序都要进行的操作,计算机对内存编址是线性的,也就是说是一维的,比如256M的内存,地址就应该是从0~(256M-1),这个地址称为物理地址或绝对地址。
1.1 地址表示
但从汇编程序员的角度看,内存却是二维的,要说明一个地址,需要给出两个值,就象你在平面上指定一点需要说出(X,Y)坐标一样,汇编程序员的内存视角也需要两个“坐标”,前一个称为段地址(Segment),后一个称为偏移地址(Offset),该地址称为逻辑地址。
比如“1234:3DF5”就是一个地址。“1F3F:”不是一个地址,因为他只有段地址,没有编移地址。注意此后的地址都用十六进制表示。
1.2 地址计算
前面提到,计算机编址是一维的,汇编程序员是二维的,那么二者怎么换算呢?由后者到前者的换算方法是,“段地址串”后面加个“0”,然后再加上偏移地址。
比如“1234:3DF5”(十六进制的加减运算参见相关资料)
12340 ‘串后加了一个0
3DF5
-----
16135 ’注意此串仍然是十六进制。
  所以,汇编程序员眼中的地址“1234:3DF5”就是物理地址(计算机编址):16135。
  知道了由后者向前者的转换,那么由前者向后者的转换呢?
“不知道”,为什么不知道,继续往下看。
1.3 到底哪个地址对。
  知道了1.2的地址算法后,我又发现一个问题:
  “1000:6135”的物理地址是多少呢? 10000+6135=16135。
  “1001:6125”的物理地址呢? 10010+6125=16135。
  ......
  那么到底哪个对呢?问题的回答是这样的:假设我现在让你按一下“L”键,我可以告诉你如下几种方法中的一种或几种。1 请按一下“L”键; 2请按一下键盘上第四行第十个键;3 请按一下第十列中的第四个键;4 请按一下“K”右边的键;5 按标准指法单击一下右手无名指。
  举上面的例子也就是说,同一个地址有很多种表示方式,具体用哪一种,要看实际使用时的情况。但无论用哪种方式,只要能达到目的即可。(实际中该问题一般不会受此问题困扰,但初学时突然想不通)。
1.4 有多少内存可以访问
无论是段地址还是偏移地址都是四位十六进制(如果不够四位,前面补0)。也就是说:总共可以访问的地址说是:0000:0000~FFFF:FFFF。 总共FFFF0+FFFF+1=10FFF0个地址。也就是不到1M的空间。
记住如下结论:
*)不管你实际内存有多少,目前我们只能访问不到1M的空间。
*)而实际上连这1M也用不完。其中上端的384K的址只能读不能写,只能读,一般称为ROM。
*)低端的640K可以读写。但这640K的低端100多K也不能随便写,因此DOS系统使用该区。
*)原来1024M的内存,汇编程序只能使用其中400多K。这段内存的容易相当于一个普通文档的大小。不过这就足够了。

2 DEBUG的使用
先记住以下两个命令:D命令和Q命令。前者是显示内存内容,后者是退出DEBUG命令。
-------------以下为抄别的人内容---------------
DEBUG.EXE程序是专门为分析、研制和开发汇编语言程序而设计的一种调试工具,具有跟踪程序执行、观察中间运行结果、显示和修改寄存器或存储单元内容等多种功能。它能使程序设计人员或用户触及到机器内部,因此可以说它是80X86CPU的心灵窗口,也是我们学习汇编语言必须掌握的调试工具。

  1)DEBUG程序使用

在DOS提示符下键入命令:

  C>DEBUG [盘符:][路径][文件名.EXE][参数1][参数2]

这时屏幕上出现DEBUG的提示符“-”,表示系统在DEBUG管理之下,此时可以用DEBUG进行程序调试。若所有选项省略,仅把DEBUG装入内存,可对当前内存中的内容进行调试,或者再用N和L命令,从指定盘上装入要调试的程序;若命令行中有文件名,则DOS把DEBUG程序调入内存后,再由DEBUG将指定的文件名装入内存。
2)DEBUG的常用命令
(1)退出命令 Q
  格式:Q
  功能:退出DEBUG,返回到操作系统。
(2)显示存储单元命令 D
格式1:D[起始地址]
  格式2:D[起始地址][结束地址|字节数]
  功能:格式1从起始地址开始按十六进制显示80H个单元的内容,每行16个单元,共8行,每行右边显示16个单元的ASCII码,不可显示的ASCII码则显示“?”。格式2显示指定范围内存储单元的内容,其他显示方式与格式1一样。如果缺省起始地址或地址范围,则从当前的地址开始按格式1显示。
例如:  -D 200    ;表示从DS:0200H开始显示128个单元内容
        -D 100 120  ;表示显示DS:0100-DS:0120单元的内容
  说明:在DEBUG中,地址表示方式有如下形式:
  段寄存器名:相对地址,如:DS:100
段基值:偏移地址(相对地址),如:23A0:1500

--------------------------小抄结束--------------------------------

3 验证第一节里的内容
运行“开始/程序/附件/MS-DOS命令提示符”(这是win2000,win98下自己找吧)
在“_”下输入D,显示
-d
1398:0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0110 00 00 00 00 00 00 00 00-00 00 00 00 34 00 87 13 ............4...
1398:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1398:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-
我们记下:1398:011C的值是个34。1389:011C的物理地址应该是:13A9C。
那么1000:3A9C的物理地址也应该是13A9C,他的内存也应该是34,(因为本来就是一个地址吗,就象第三行第十列和第十列第三行当然应该是同一个位置)。
-d 1000:3A9C
1000:3A90                  34 00 87 13      4...
1000:3AA0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3AB0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3AC0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3AD0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3AE0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3AF0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3B00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1000:3B10 00 00 00 00 00 00 00 00-00 00 00 00      ............
-
果然如此,同样你可以验证:13A9:000C也肯定是指这一个地址,不信试试。 

--------------------------------------------------------------------------------
[sq回复:]
回复: 霜泉 [ljsh012]  论坛用户 回复  收藏  修改  删除 
286老大,这个讲座很好.希望再开一些其他语言的讲座.
看了以上,我也来说几句,不对的地方还望大家指正.286在讲物理地址的计算时没有讲为什么要在段地址的后面添0,我补充一下,因上面所涉及到的cpu的地址总线为20位,即寻址范围为0到2的20次方。即1MB。而存储器寻址时是通过物理地址,即地址从0开始编号,顺序加1(为线性增长)。而我们表示时是用16进制。所以物理地址是这样的00000--FFFFF(16进制,线性增长,一个16进制位对应4个二进制位,所以上面4*5=20位。)而机器规定每16个字节为一小段,看下面的
00000,00001,00002,......,0000F;
00010,00011,.............,0001F;
..................................
FFFF0,FFFF1,.............,FFFFF;
以上表示的就是内存的物理地址。每行第一个即为小段段地址。
而程序员编程时就不太可能都用物理地址来表示数据存放的地址了。解决这个问题的方法是把内存分段(分大段)。每段的大小可以自由划分(在20位地址总线下最大不超过64KB--为什么不能超过64KB呢?)自己划分的段地址必须为每小段的段首地址(在开辟段时机器会自动得到段地址,程序员只须关心偏移地址)而我们发现段地址(16进制下)的最后一位都是0.故我们表示段地址时只要了16进制下的前4位。这正好是两个字节。(这就便于在计算机中表示了)
  简单的讲,在程序员编程序时,因为段地址是只取了前四位(16进制下),所以在在计算物理地址时就要在后面补0。然后加上偏移地址。即得到实际物理地址。存储器也就找到了准确的存储单元的地址。
复杂点讲,就是机器为16位字长,而机器只识别二进制。段地址上面是取了4个16进制位,即二进制下的16位。偏移地址也是用16位(二进制)两个字节。2^16=64KB,这里说明了上面的段的大小为什么在64KB以下。(当然有时程序需要的空间会大过64KB,而此时光用偏移地址已经无法(不够)表示后面的内容了,所以程序员应该另加说明了(即已经在其他段下了)) 而我们知道上面的地址总线为20位。我们在16位字长的机器里怎样提供20位的地址呢。方法就是把段地址16位左移四位,再加上偏移地址就实现20位物理地址了。而我们知道左移4位即相当于把原先的数乘于2^4。即乘于16d.(10进制)。而16d(10进制)化成16进制后就是10H(16进制)所以在段地址后面添0实际上乘了个10H。

引用:
记住如下结论:
*)不管你实际内存有多少,目前我们只能访问不到1M的空间。
;286老大,这里怎么讲,在32地址总线下也只能寻这麽点,不严密吧,因该指明是在16字长(20为地址总线)的机子下阿。

引用:
大家来算一个“题”。某一组数据显示时,每个数据占了四个位置,
每行共十六个。问:十六进制的13位置在哪里(第几行,第几列)。
格式如下:m m m m n n n n o o o o p p p p '注:之所以没用ABC是怕与上面十六进制弄混。
  r r r r s s s s t t t t u u u u
  ........
你这里我看不明白是说些什么,你是说16进制的13这个地址在几行几列?还是指这个13为内存的一个内容,他在哪里?(内存的内容可以随意阿)还望指教。
谢谢。
偶也是刚学汇编,看了286老大的这篇没完的讲座,颇有收获,也许我说的一些可能不对或者是在钻牛角尖。(但我觉得这正好是初学者容易困惑的,所以写了以上)
还有一点不明白的是存储器即要花一些空间来存放地址,又要花一些空间存放数据。地址是怎样指向数据所在的字节呢?是不是开辟了一个段后,也跟着开辟一个存放地址的空间?不懂,高手指教。
感谢286,支持286。


[此贴被 霜泉(ljsh012) 在 11月18日16时14分 编辑过]
================================================================================
1 即使是32地址总线下,只要采用实模式,就只能使用其中的20根地址线。要使用所有地址线要采用保护模式,那另当别论。你看看,我们现在的机器早不是20位地址线了,可我用DEBUG运行的时候,显示的不还是20根地址线了吗?那是因为我们采用了实模式,只用最低的20根线。真无奈。

2 怨我没说清。我是指第几行第几列这个位置而不是值。这可就是大名鼎鼎的INT 13H。这可是破解硬盘保护卡的基础。

3 “还有一点不明白的是存储器即要花一些空间来存放地址,又要花一些空间存放数据。地址是怎样指向数据所在的字节呢?是不是开辟了一个段后,也跟着开辟一个存放地址的空间?不懂,高手指教。”
请听下回分解。

[此贴被 286(unique) 在 11月19日18时00分 编辑过]


--------------------------------------------------------------------------------
霜(双)化了,
能变成泉(全)么?
我在尽力溶化。
技术、品质、人际---->人生。
  http://shuangquan.oicp.net    


B4层 发表时间: 04-11-18 13:48

--------------------------------------------------------------------------------
回复: 286 [unique]  版主 回复  收藏 
4 DEBUG命令
-------------------继续小抄----------------
前面已学过:显示存储单元命令 D
再学一个命令
(1)修改存储单元命令 E

格式1:E[起始地址] [内容表]

格式2:E[地址]

功能:格式1按内容表的内容修改从起始地址开始的多个存储单元内容,即用内容表指定的内容来代替存储单元当前内容。

例如:?E DS:0100 'VAR' 12 34

表示从DS:0100 为起始单元的连续五个字节单元内容依次被修改为

'V'、'A'、'R'、12H、34H。

格式2是逐个修改指定地址单元的当前内容。

如:?E DS:0010

156F:0010 41.5F

其中156F:0010单元原来的值是41H,5FH为输入的修改值。若只修改一个单元的内容,这时按回车键即可;若还想继续修改下一个单元内容,此时应按空格键,就显示下一个单元的内容,需修改就键入新的内容,不修改再按空格跳过,如此重复直到修改完毕,按回车键返回DEBUG“-”提示符。如果在修改过程中,将空格键换成按“-”键,则表示可以修改前一个单元的内容。

-------------------小抄结束----------------

5 使用DOS时,汇编用户可以从DOS操作系统中得到什么?
现在编程,通常很多功能都是通过调用系统API。很多高级语言都直接把这些API包装起来,以系统接口或函数的方式提供给用户,那么汇编函数都能得到什么呢?
首先,汇编用户有很多东西可以调用。他们主要是:
5.1 BIOS提供的接口。现在硬件与软件的区分已越来越不明显,很多硬件不仅仅是电路,而还要提供一些固化写入硬件的一部分“程序”,这些程序以ROM的方式出现,汇编用户最大的好处就是可以直接使用这些“程序”,这些使用不仅功能强大,而且效率非常高。
5.2 DOS功能调用,作为操作系统也象BIOS一样向用户提供了相应的“程序”。这些程序在很大程序上扩充了BIOS。与BIOS不同的是,这部分程序放在内存中,它可以被修改。而BIOS中不能再修改。
==========================================================
以上两种接口都通过一种相同的格式调用,这些程序统称为“中断”,现在先不要理解中断的本意,你现在可以认为是系统提供给你的函数。
============================================================
5.3 系统共享数据区。编过程序的人都知道全局变量的好处,全局变量方便之外在于任何函数、过程都可以调用、读取、修改。全局变量不足之处是危险性,有一个过程改了这个变量值,其它的也得跟着改变了。DOS操作系统同样也提供了这样的共享数据区,该区是整个系统的共享区,任何程序都可以查找、修改。当然,修改某处必然会对其它程序造成影响。

6 再谈中断
前面5.2已提到中断了,现在问题是不同硬件不一样,即使相同硬件的ROM,不同版本,各个BIOS中断程序所处的位置也不一样,DOS中断也一样,不同版本、不同配置,在内存位置也不一样。那么你使用某一个中断,系统怎么知道你使用的那个中断程序在哪呢?
为了解决这一问题,DOS会在启动的时候,把所有这些(BIOS和DOS)中断的首地址保存到一个地址。这个地址很容易记,这段地址是内存的绝对零地址(0000:0000)。前面已讲过,每个地址在汇编程序员角度来看是二维的,也就是分为段地址和偏移地址。每个地址各占两个字节,所以要表示这个二维地址需要4个字节。所以每个中断首地址由4个字节表示。一共256个中断,占用了1024个字节的位置。
另外需要注意的是,这4个表示地址的字节,数据是由低向高的。比如12 34 56 78所表示的地址是:7856:3412。
一般用INT M表示中断M,如果M是十六进制,则在后面加上一个H。比如19号中断,十六进制应该是13H。所以该中断就是INT 13H。

7 再谈系统共享数据区
该共享数据区在绝对地址:0040:0000开始。

8 验证我上面说的内容
8.1 找中断
运行DEBUG后。输入D 0000:0000。显示绝对零地址的内容。
C:\>debug
-d 0:0
0000:0000 68 10 A7 00 8B 01 70 00-16 00 9B 03 8B 01 70 00 h.....p.......p.
0000:0010 8B 01 70 00 B9 06 0E 02-40 07 0E 02 FF 03 0E 02 ..p.....@.......
0000:0020 46 07 0E 02 0A 04 0E 02-3A 00 9B 03 54 00 9B 03 F.......:...T...
0000:0030 6E 00 9B 03 88 00 9B 03-A2 00 9B 03 FF 03 0E 02 n...............
0000:0040 A9 08 0E 02 99 09 0E 02-9F 09 0E 02 5D 04 0E 02 ............]...
0000:0050 A5 09 0E 02 0D 02 DC 02-B8 09 0E 02 8B 05 0E 02 ................
0000:0060 02 0C 0E 02 08 0C 0E 02-13 0C 0E 02 AD 06 0E 02 ................
0000:0070 AD 06 0E 02 A4 F0 00 F0-37 05 0E 02 71 84 00 C0 ........7...q...
-u 0070:018B
0070:018B 1E      PUSH  DS
0070:018C 50      PUSH  AX
0070:018D B84000    MOV  AX,0040
0070:0190 8ED8    MOV  DS,AX
0070:0192 F70614030024 TEST  WORD PTR [0314],2400
0070:0198 754F    JNZ  01E9
0070:019A 55      PUSH  BP
0070:019B 8BEC    MOV  BP,SP
0070:019D 8B460A    MOV  AX,[BP+0A]
0070:01A0 5D      POP  BP
0070:01A1 A90001    TEST  AX,0100
0070:01A4 7543    JNZ  01E9
0070:01A6 A90002    TEST  AX,0200
0070:01A9 7422    JZ  01CD
首先,D命令把中断首地址显示出来。每4个表示一个地址。其中INT 0的中断首地址为:00A7:1068,INT 1的中断地址为:0070:018B.......0070:018B是中断3的首地址。后面那个U命令就表示显示该地址的“中断程序”的内存。
  你们可以试着找找INT 13H的位置在哪。
8.2 验证系统共享数据区
  系统共享数据区内容极为丰富,我实在记不住哪么多了。我曾记在一个本上,可惜那个本早在N年前(3<N<6)就丢了。兄弟们谁找到这个地址的内容,一定要贴上来,这里有东西可以让大家眼界大开。
  前几年,我用的286计算机是黑白显示器(555555~~~~~~~~~,别嫌我老、旧、慢呀),可当时有个游戏非要彩显,不是彩显不让运行。我就是改了这个区的某一个位,让哪游戏“以为”我用的是彩显,于是游戏能用了。虽然不好看,但总能用。
  在DOS下,你每按一个键,系统都会记下来,下面我们一起找找这个键盘缓冲区的地址。知道这个地址,你就可以作一个“虚拟”键盘,通过发命令来模拟某个人在按键。这个地址位于:0040:001E。 其中每个键有两个字节,一个字节是ASCII码,一个是扫描码。共16个。
C:\>debug
-d 40:0
0040:0000 F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F ..........x.x...
0040:0010 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 20 39 "....(....*.*. 9
0040:0020 34 05 30 0B 3A 27 30 0B-0D 1C 64 20 20 39 34 05 4.0.:'0...d 94.
0040:0030 30 0B 3A 27 30 0B 0D 1C-71 10 0D 1C 64 20 00 00 0.:'0...q...d ..
0040:0040 A2 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00 ..........P.....
0040:0050 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0040:0060 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 A1 B7 11 00 .....)0.........
0040:0070 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01 ................
-d 0040:0000
0040:0000 F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F ..........x.x...
0040:0010 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 3A 27 "....(....*.*.:'
0040:0020 30 0B 30 0B 30 0B 30 0B-0D 1C 64 20 20 39 30 0B 0.0.0.0...d 90.
0040:0030 30 0B 30 0B 30 0B 08 0E-08 0E 34 05 30 0B 00 00 0.0.0.....4.0...
0040:0040 1F 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00 ..........P.....
0040:0050 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0040:0060 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 24 B8 11 00 .....)0.....$...
0040:0070 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01 ................
-
既然是键盘缓冲区,每个输入的键都会显示在该区中,第一次我只输入了“d 40:0”,所以你可以在此后显示数据右边字符中找到这些字符,注意是间隔开的。
第二次我输入“d 0040:0000”,则右边显示的是“d 0040:0000”的内容。你可以找找。




--------------------------------------------------------------------------------
纵行下天,286足矣。

B5层 发表时间: 04-11-19 17:45
回复时间:(2004-11-20 00:34:18.000)
--------------------------------------------------------------------------------
[sq回复:]
回复: 仙人掌 [cailman]  论坛用户 回复  收藏 
找别扭:
*)低端的640K可以读写。但这640K的低端100多K也不能随便写,因此DOS系统使用该区。
*)原来1024M的内存,汇编程序只能使用其中400多K。这段内存的容易相当于一个普通文档的大小。不过这就足够了。


To:霜泉 [ljsh012]
  应该说存储器上的每个字节空间都有编号,里面只能放二进制数.内存(存储器)里的二进制数你把它当指针用的时候它就表示一个地址,把它当数据用的时候它就是数据.
  而你给CPU一个地址然后它怎么找到对应的内存空间,这个我也不懂(我想你问的是这个意思吧).期待懂的人指教.

[此贴被 仙人掌(cailman) 在 11月20日23时15分 编辑过]


--------------------------------------------------------------------------------
20CN的主要作用是为广大计算机爱好者提供一个友好的技术交流平台。并整理和发掘网络安全、黑客方面技术文献及工具、代码。为大家提供发布安全、黑客相关文章及工具的园地。
20CN的建设需要你的参与,按此可为网站更新文档
B7层 发表时间: 04-11-20 23:00

--------------------------------------------------------------------------------
回复: 霜泉 [ljsh012]  论坛用户 回复  收藏  修改  删除 
不错,感觉又有点长进。
而且从中断程序的地址保存在内存绝对物理地址的开始中可以猜想我们的程序的地址或许就紧跟着保存在后面了。(赫赫,这里全属瞎想。)
用U命令能够列出汇编程序,同时显示对应的堆栈的内容(16进制)我想问的是一个汇编指令按理来讲对应的二进制代码应该是相同的。但是用多个u命令列出的程序中,比如push指令,其对应的存储他的代码都不同。说明我的分析不对,那么汇编指令在内存中是怎样存储的?(或许内存中根本不存储汇编指令,而是用机器来确定)
另外一个问题是,用c语言写个赋值语句,比如用a=6;来存储值,&a来得到内存中变量a的地址。然后用%x输出a的地址为ffc4.原本以为用d 0000:ffc4能够显示出a变量中存储的值6,但是结没有显示出来。说明我们用c语言得到的这个变量a的地址并非和内存中实际a的地址相符。(应该有联系,但目前我不知道之间的关系)
286老大,麻烦解答一下上面的问题。谢谢。

To:仙人掌。
  我觉得你对上面的理解或许还有些不实在。你知道为什么要在内存地址开始存储中断的地址吗?那是因为内存加载中断程序时是把它放在绝对地址的后后面了,程序员很难找到在哪里俄。而程序员要调用中断程序时,显然只能通过二维的方法:段地址+偏移地址。而正如上面所讲,dos在加载中断程序时已经把他的绝对物理地址用二维的形式保存在物理地址的开始处了。程序员调用时只须用调用中断的命令int mH(第几个中断)(其实我想也可以直接用段地址+偏移地址的方法找到中断程序所在的地方只是这样就麻烦了。汇编程序直接用个int指令就完成这个工作了,省事。)至于我问的那个问题,还是等待后面几讲之后再作定论。我想到时就能明白了。(其实我现在是这麽想的,绝对物理地址不需要存储的。因为他是成线性增长的。存储器在找由程序员给出的二维地址后(通过计算后就能得到绝对物理地址),而找的这个过程因为有20位(假设是20位总线)的数据总线,而20位的数据总线可以表示2^20个不同的情况,这些情况分别对应内存的不同的实际物理地址。这样就完成找的工作了,这里说明cpu一次只能找到一个地址。而为什么这麽快能,那就是频率的问题了。所以那个fsb(前端控制总线)就是关键影响速度的。赫赫,是自己理解的,或许下一讲后我的想法就崩溃了)

[此贴被 霜泉(ljsh012) 在 11月21日00时49分 编辑过] 回复时间:(2004-11-21 00:52:39.000)
--------------------------------------------------------------------------------
[sq回复:]
回复: 286 [unique]  版主 回复  收藏 
TO peregrine:
你问题的答案部分在你自己发的贴子中都可以找到。还有些请等待并看对霜泉的回答。

TO 仙人掌:
多谢指正及解疑。
“而你给CPU一个地址然后它怎么找到对应的内存空间,”,这个要看计算机原理了。内存访问这一章节。

TO 霜泉:
“从中断程序的地址保存在内存绝对物理地址的开始中可以猜想我们的程序的地址或许就紧跟着保存在后面了”,跟在后面是不紧,但很显然不是“紧”跟在后面,因为中断后面是系统数据区,再向后是操作系统自己的程序占用。这大约占100K左右,当然你运行的系统程序越多,为个数据越大。如果操作系统占的量大太了(比如大于500K),程序就没空间,这时会出现“内存空间不够的错误”。
  “用U命令能够列出汇编程序,同时显示对应的堆栈的内容(16进制)”,不是堆栈内存,而是汇编命令所对应的机器码,这也就是我此前说的汇编语言与机器语言是等价的原因。
  “一个汇编指令按理来讲对应的二进制代码应该是相同的。”一个汇编指令对应是相同,比如push dx与push dx肯定相同,(,别朝我扔砖头),但push dx与push cx不是同一个汇编指令。而是两个不同的指令。
  “另外一个问题是...之间的关系)”,原理对,但不是一两句话就能说清,涉及太多,所以暂且不说。
  你对仙人掌的解答不完全对,,呵呵,一则,中断地址总要放一个地方吧?你说放哪呢?比如放到1234:5678,也很好记,但到哪个时候你还会不会问:为什么放1234:5678,而不是0000:0000的,他总要放个地方。因此放绝对零也无可厚非。另外,你查一下“计算机原理”中内存访问章节,CPU访问所有内存的址时,速度都是完全一样的。也就是说,0000:0000不比FFFF:FFFF快多少。这一点就象是让你写由圆心到圆周上任意一点的矩离相等一样。



--------------------------------------------------------------------------------
纵行下天,286足矣。

B9层 发表时间: 04-11-22 09:21

--------------------------------------------------------------------------------
回复: 憨狗 [hackgou]  论坛用户 回复  收藏 
哇塞!这么热闹啊??呵呵
我也来凑凑热闹!

1。说说仙人掌兄的问题吧:
   
引用:
--------------------------------------------------------------------------------
而你给CPU一个地址然后它怎么找到对应的内存空间,
--------------------------------------------------------------------------------



其实这个挺简单的:
先说说我们讨论的这个问题的大前提:我们现在讨论的是80×86时代的汇编(也是20位的dos时代,可别以为有非20位的dos哦/
:P),而非如今主流的32位平台,因为32位平台向下兼容,所以我们现在的话题有可能是对的,因为很多
内容在32位下就不一定了。

开始主题:   
引用:
--------------------------------------------------------------------------------
而你给CPU一个地址然后它怎么找到对应的内存空间,
--------------------------------------------------------------------------------



在dos时代(硬件方面是386以下),因为16位的处理器无法访问(寻址)20位的地址总线提供的1M内存,于是就使用分段的办
法解决:每个段64K,
(为什么是64K??因为16位寄存器可以存储的最大范围就是2^16=2^6×2^10=64K。
按照最大段来计算,多少段?1M/64K=16个段),这样就可以利用两个16位寄存器来寻址这20位的1M内存(虽然,是浪费了12位
宝贵的寄存器位数,两个16位寄存器合起来的寻址能力是2^32=4G,现在只需利用其中2^20的寻址能力,但却变相实现了利用
两个16位寄存器来寻址20位1M的内存的功能),因此可以利用一个寄存器来表示段地址(那个64K的段从哪儿开始),另外一个
16位的寄存器来表示你要访问的数据在该64K的段内的什么地方。起前一个功能的寄存器就叫段寄存器(按段寄存器所指的段的
用途来分,这些段寄存器又分为:CS、DS、SS、ES等。而后一个功能则一般通过通用寄存器来实现的,通常后面这个用来在段
内寻址的地址就叫做段内偏移(offset),它也是16位的寄存器。

在具体寻址的时候是如何计算的呢?段寄存器和段内偏移地址直接相加?不是的,因为那样最多能够达到17位的寻址能力。正
确的做法是将段寄存器左移4位,然后加上段内偏移地址,这样就得到了20位的地址了,使用则20位地址就能够实现1M的存储空
间寻址了。

这个计算是谁来完成的呢??人工么?还是编译器编译的时候执行的呢??都不是。而是CPU内部的BIU(bus interface unit
,总线接口单元)来完成。它包括段寄存器(CS、DC、SS、ES)和地址产生器(当然不止两个部分,但是和内存寻址的就主要是
这两部分),当遇到一个寻址的指令后,地址产生器就执行逻辑地址(16位的段:16位的偏移)到20位的物理地址(PA)的计
算,计算的公式如前所述(段地址乘以16+偏移地址),这样就“找到对应的内存空间“。


2。 
引用:
--------------------------------------------------------------------------------
一个汇编指令按理来讲对应的二进制代码应该是相同的
--------------------------------------------------------------------------------


这个问题其实和汇编语句到底如何编译位机器语言有关,一条汇编语句包含两个部分:80×86指令和数据部分。其中数据部分
包括:立即数、寄存器等等。比如mov
dx,ax其中mov是指令部分。而dx,ax就是数据部分(也可以理解为汉语的动宾关系:指令为动词,数据部分为宾语,别问我谁是
主语啊?问我就告诉你:EU)。所以如果两条汇编语句的动宾(指令和数据部分)全相同,则他们的二进制代码也就完全相同
。这是以前在C51上亲自进行汇编语句到机器语句的手工翻译时所遵循的规律。因为C51上没有关于栈的操作,所以抱歉的说:
没有push指令方面的具体的翻译经验可以讨论。也无法直接回答你关于push指令不同的问题。但是现在我们参考Intel公司的指
令格式和编码来做做80×86指令系统的试验,先来看看两条关于堆栈操作(push ax)和立即数(mov
ax,1234)操作指令的编码格式来窥窥汇编语句到二进制代码转换的过程,以理解不同语句与其二进制代码的关系:
1。压栈:
PUSH
register 1111 1111 : 11 110 reg
register (alternate encoding) 0101 0 reg

2。弹栈:
POP
register 1000 1111 : 11 000 reg
register (alternate encoding) 0101 1 reg

3。立即数操作:
immediate to register 1100 011w : 11 000 reg : immediate data
immediate to register (alternate encoding) 1011 w reg : immediate data

4。寄存器域:

reg w=0  w=1 reg w=0 w=1
000 AL  AX 000 AL EAX
011 BL  BX 011 BL  EBX

可以看出来Intel本身对同一指令都有两种不同的编码格式,但是无论我们选择哪一种格式编码,得到的二进制结果虽然不一样
,但是执行的结果却是一样的。好了,我们来段简单的汇编语句:
mov ax,1234
push ax
pop bx


我们先来手工翻译一下:

Mov ax,1234指令有两种翻译方法:

汇编语句    ――>  二进制代码        ――>  16进制代码
法一:Mov ax,1234    ――>  11 000 000  1234  ――>  C0 1234
法二:Mov ax,1234    ――>  1011 1 000  1234  ――>  B8 1234

Push ax也有两种翻译方法:

      汇编语句    ――>  二进制代码        ――>  16进制代码
法一:push ax    ――>  1111 1111:11 110 000  ――>  FF:F0
法二:push ax    ――>  0101 0 000            ――>  50

Pop  bx也有两种翻译方法:

      汇编语句    ――>  二进制代码        ――>  16进制代码
法一:Pop bx    ――>  1000 1111:11 000 011  ――>  8F:C3
法二:Pop bx    ――>  0101 1 011            ――>  5B

那么根据我们的手工译码有两套方案:
方案一:
mov ax,1234    ――>  C01234
push ax        ――>  FF:F0
pop bx          ――>  8F:C3

方案二:

mov ax,1234    ――>  B81234
push ax        ――>  50
pop bx          ――>  5B


代码:
--------------------------------------------------------------------------------

-a148E:0100 mov ax,1234
148E:0103 push ax
148E:0104 pop bx
148E:0105-u 100
148E:0100 B83412        MOV    AX,1234 
;怎么样??和我们手工翻译的第二套方案一模一样吧。
148E:0103 50            PUSH    AX    ;还真是一点都不假呢!
148E:0104 5B            POP    BX    ;真的呢!
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0100  NV UP EI PL NZ NA PO NC
148E:0100 B83412        MOV    AX,1234
;为了验证正确性,再来执行一下??注意红色部分的变化
-g 103
AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0103  NV UP EI PL NZ NA PO NC
148E:0103 50            PUSH    AX
-g
104AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEC  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0104  NV UP EI PL NZ NA PO NC
148E:0104 5B            POP    BX
-g
105AX=1234  BX=1234  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0105  NV UP EI PL NZ NA PO NC
148E:0105 0000          ADD    [BX+SI],AL                        DS:1234=00

--------------------------------------------------------------------------------


最后三条红色部分跟踪语句可以换成(t命令,更显专业):

代码:
--------------------------------------------------------------------------------

-t
AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0103  NV UP EI PL NZ NA PO NC
148E:0103 50            PUSH    AX
-t
AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEC  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0104  NV UP EI PL NZ NA PO NC
148E:0104 5B            POP    BX
-t
AX=1234  BX=1234  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0105  NV UP EI PL NZ NA PO NC
148E:0105 0000          ADD    [BX+SI],AL                        DS:1234=00

--------------------------------------------------------------------------------




写完了这些,基本上可以回答霜泉兄的问题吧,.
大家看完了,如果我也写清楚到大家那个完全理解的地步了的话,不知道兄弟们是不是有种想写编译器的冲动啊?呵呵。其实
涉及的东西还远不止这些呢!


[此贴被 霜泉(ljsh012) 在 12月02日13时39分 编辑过]

地主 发表时间: 04-12-01 13:33

回复: NetDemon [netdemon]   ADMIN   登录
20CN服务器损坏,现正使用备用服务器,数据被恢复到11月11日的状态,在此之后注册的用户需要重新注册.

  2004-11-30
  NetDemon


谢谢!!!

B1层 发表时间: 04-12-01 15:41

回复: 286 [unique]   版主   登录
感谢霜泉兄弟,正是你的仔细认真,才得挽救这个文章,其中有很多内容我是在线写的,没有本地备份。
最近太忙,得有空了再接着写。

B2层 发表时间: 04-12-01 17:53

回复: ljsh012 [ljsh012]   论坛用户   登录
老大们就不用客气了。算是我对20cn的一点点付出,(有点臭美了是不?)毕竟我已在这里汲取了许多知识。

B3层 发表时间: 04-12-01 18:48

回复: hackgou [hackgou]   论坛用户   登录
感谢霜泉兄对本贴做的本地备份,要不然大家的这些心血可就付诸东流了哦,要知道我当初可是写到了凌晨一点过才完成了最后的修改哦。/:p。:
但是在11号之后我对我的回帖进行了一些修改,还好在本地存有档,如下:麻烦更改一下我的原文,谢谢:


哇塞!这么热闹啊??呵呵
我也来凑凑热闹!

1。说说仙人掌兄的问题吧:
   
引用:
而你给CPU一个地址然后它怎么找到对应的内存空间,


其实这个挺简单的:
先说说我们讨论的这个问题的大前提:我们现在讨论的是80×86时代的汇编(也是20位的dos时代,可别以为有非20位的dos哦/
:P),而非如今主流的32位平台,因为32位平台向下兼容,所以我们现在的话题有可能是对的,因为很多
内容在32位下就不一定了。

开始主题:   
引用:
而你给CPU一个地址然后它怎么找到对应的内存空间,


在dos时代(硬件方面是386以下),因为16位的处理器无法访问(寻址)20位的地址总线提供的1M内存,于是就使用分段的办
法解决:每个段64K,
(为什么是64K??因为16位寄存器可以存储的最大范围就是2^16=2^6×2^10=64K。
按照最大段来计算,多少段?1M/64K=16个段),这样就可以利用两个16位寄存器来寻址这20位的1M内存(虽然,是浪费了12位
宝贵的寄存器位数,两个16位寄存器合起来的寻址能力是2^32=4G,现在只需利用其中2^20的寻址能力,但却变相实现了利用
两个16位寄存器来寻址20位1M的内存的功能),因此可以利用一个寄存器来表示段地址(那个64K的段从哪儿开始),另外一个
16位的寄存器来表示你要访问的数据在该64K的段内的什么地方。起前一个功能的寄存器就叫段寄存器(按段寄存器所指的段的
用途来分,这些段寄存器又分为:CS、DS、SS、ES等。而后一个功能则一般通过通用寄存器来实现的,通常后面这个用来在段
内寻址的地址就叫做段内偏移(offset),它也是16位的寄存器。

在具体寻址的时候是如何计算的呢?段寄存器和段内偏移地址直接相加?不是的,因为那样最多能够达到17位的寻址能力。正
确的做法是将段寄存器左移4位,然后加上段内偏移地址,这样就得到了20位的地址了,使用则20位地址就能够实现1M的存储空
间寻址了。

这个计算是谁来完成的呢??人工么?还是编译器编译的时候执行的呢??都不是。而是CPU内部的BIU(bus interface unit
,总线接口单元)来完成。它包括段寄存器(CS、DC、SS、ES)和地址产生器(当然不止两个部分,但是和内存寻址的就主要是
这两部分),当遇到一个寻址的指令后,地址产生器就执行逻辑地址(16位的段:16位的偏移)到20位的物理地址(PA)的计
算,计算的公式如前所述(段地址乘以16+偏移地址),这样就“找到对应的内存空间“。


2。 
引用:
一个汇编指令按理来讲对应的二进制代码应该是相同的

这个问题其实和汇编语句到底如何编译位机器语言有关,一条汇编语句包含两个部分:80×86指令和数据部分。其中数据部分
包括:立即数、寄存器等等。比如mov
dx,ax其中mov是指令部分。而dx,ax就是数据部分(也可以理解为汉语的动宾关系:指令为动词,数据部分为宾语,别问我谁是
主语啊?问我就告诉你:EU)。所以如果两条汇编语句的动宾(指令和数据部分)全相同,则他们的二进制代码也就完全相同
。这是以前在C51上亲自进行汇编语句到机器语句的手工翻译时所遵循的规律。因为C51上没有关于栈的操作,所以抱歉的说:
没有push指令方面的具体的翻译经验可以讨论。也无法直接回答你关于push指令不同的问题。但是现在我们参考Intel公司的指
令格式和编码来做做80×86指令系统的试验,先来看看两条关于堆栈操作(push ax)和立即数(mov
ax,1234)操作指令的编码格式来窥窥汇编语句到二进制代码转换的过程,以理解不同语句与其二进制代码的关系:
1。压栈:
PUSH
register 1111 1111 : 11 110 reg
register (alternate encoding) 0101 0 reg

2。弹栈:
POP
register 1000 1111 : 11 000 reg
register (alternate encoding) 0101 1 reg

3。立即数操作:
immediate to register 1100 011w : 11 000 reg : immediate data
immediate to register (alternate encoding) 1011 w reg : immediate data

4。寄存器域:

reg w=0  w=1 reg w=0 w=1
000 AL  AX 000 AL EAX
011 BL  BX 011 BL  EBX

可以看出来Intel本身对同一指令都有两种不同的编码格式,但是无论我们选择哪一种格式编码,得到的二进制结果虽然不一样
,但是执行的结果却是一样的。好了,我们来段简单的汇编语句:
mov ax,1234
push ax
pop bx


我们先来手工翻译一下:

Mov ax,1234指令有两种翻译方法:

汇编语句    ――>  二进制代码        ――>  16进制代码
法一:Mov ax,1234    ――>  11 000 000  1234  ――>  C0 1234
法二:Mov ax,1234    ――>  1011 1 000  1234  ――>  B8 1234

Push ax也有两种翻译方法:

      汇编语句    ――>  二进制代码        ――>  16进制代码
法一:push ax    ――>  1111 1111:11 110 000  ――>  FF:F0
法二:push ax    ――>  0101 0 000            ――>  50

Pop  bx也有两种翻译方法:

      汇编语句    ――>  二进制代码        ――>  16进制代码
法一:Pop bx    ――>  1000 1111:11 000 011  ――>  8F:C3
法二:Pop bx    ――>  0101 1 011            ――>  5B

那么根据我们的手工译码有两套方案:
方案一:
mov ax,1234    ――>  C01234
push ax        ――>  FF:F0
pop bx          ――>  8F:C3

方案二:

mov ax,1234    ――>  B81234
push ax        ――>  50
pop bx          ――>  5B


代码:

-a
148E:0100 mov ax,1234
148E:0103 push ax
148E:0104 pop bx
148E:0105

-u 100
148E:0100 B83412        MOV    AX,1234 
;怎么样??和我们手工翻译的第二套方案一模一样吧。
148E:0103 50            PUSH    AX    ;还真是一点都不假呢!
148E:0104 5B            POP    BX    ;真的呢!

-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0100  NV UP EI PL NZ NA PO NC
148E:0100 B83412        MOV    AX,1234
;为了验证正确性,再来执行一下??注意红色部分的变化

-g 103


AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0103  NV UP EI PL NZ NA PO NC
148E:0103 50            PUSH    AX


-g 104

AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEC  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0104  NV UP EI PL NZ NA PO NC
148E:0104 5B            POP    BX


-g 105

AX=1234  BX=1234  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0105  NV UP EI PL NZ NA PO NC
148E:0105 0000          ADD    [BX+SI],AL                        DS:1234=00



最后三条红色部分跟踪语句可以换成(t命令,更显专业):
代码:



-t

AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0103  NV UP EI PL NZ NA PO NC
148E:0103 50            PUSH    AX

-t


AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEC  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0104  NV UP EI PL NZ NA PO NC
148E:0104 5B            POP    BX

-t


AX=1234  BX=1234  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=148E  ES=148E  SS=148E  CS=148E  IP=0105  NV UP EI PL NZ NA PO NC
148E:0105 0000          ADD    [BX+SI],AL                        DS:1234=00





写完了这些,基本上可以回答霜泉兄的问题吧,.
大家看完了,如果我也写清楚到大家那个完全理解的地步了的话,不知道兄弟们是不是有种想写编译器的冲动啊?呵呵。其实
涉及的东西还远不止这些呢!


[此贴被 憨狗(hackgou) 在 12月01日21时53分 编辑过]

B4层 发表时间: 04-12-01 21:48

回复: ljsh012 [ljsh012]   论坛用户   登录
憨狗兄,我已把你的原文按照你说的更改了。但是红色字样就没了。而且本贴在美观方面就远不如原先的了,但是看着还行,建议286老大保留着憨狗兄后来的回复。

B5层 发表时间: 04-12-02 13:46

回复: autosee [autosee]   论坛用户   登录
感谢20cn各位朋友,更感谢286同志,原本我对汇编没有接触过,以前想学,但听朋友说很难,所以就没去看这方面的书,但自从我看到本贴后,看到286及各位20CN社友的细心讲解,让我知道了汇篇的大门在哪,对它产生了兴趣,至少不像别的那样,一开始就让我感到学起来难,而看到这里的贴子上我跟着朋友们的讲解,思维也跟着朋友的转,没有觉得有什么难处,所以希望各位能将汇编讲解进行到底,虽然没有物质上的奖励,但却有对各位发自内心的感激。

B6层 发表时间: 04-12-02 16:41

回复: ALLyeSNO [allyesno]   论坛用户   登录
问一下 windows里的debug支持调试 32位的汇编吗?

学汇编有什么用?
汇编产生于DOS时代或更早,而现在是Windows时代,所以可能遗憾地说:尽管还有批牛人在用汇编开发核心级程序,但我们几乎没什么用,除了必要时间能拿来分析一两个程序的部分代码之外,别的也就没干什么用了。并且并不是所有的汇编命令都能在windows下使用。而泛泛地追求“时髦”而学本语言,最后的结果是损了夫人又折兵。所以学之前你要考虑好。我劝那些为了当“黑客”而学汇编的人就此止步。

win32汇编呢?黑客守护者 就是用它编出来的

[此贴被 ALLyeSNO(allyesno) 在 12月02日17时54分 编辑过]

B7层 发表时间: 04-12-02 17:43

回复: hackgou [hackgou]   论坛用户   登录
其实你用r指令看看寄存器就知道这个问题了。

B8层 发表时间: 04-12-04 15:20

回复: bluehacker [bluehacker]   论坛用户   登录
汇编是比其他高级语言学起来麻烦点,但是我喜欢那种程序编 写格式,简单清楚。
ALLyeSNO对汇编是什么态度呢?支持还是无所谓?
我记得有个MASM32不错,可以实现很多常见功能。而且有OO的思想~~

B9层 发表时间: 04-12-10 21:28

回复: seagate [seagate]   论坛用户   登录
我们刚学过,现在正在准备着复习考试,
  这些倒是小事情,
只觉得老主给了不少实际的应用途径,
  哈哈,最高明的是16->20位地址的解释,哈哈,直接说后面加个0,简单多了。
希望各位能再多给点汇编的实际应用,
  有了实践,很容易学好这语言.

B10层 发表时间: 04-12-25 18:06

论坛: 编程破解

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号