(文章摘自老罗缤纷天地(http://www.luocong.com)
(特别感谢汇编高手dREAMtHEATER对我(老罗)的代码作出了相当好的优化!请参观他的主页http://notexpad.yeah.net/)
上一节我们介绍了花指令,不过花指令毕竟是一种很简单的东西,基本上入了门的Cracker都可以对付得了。所以,我们很有必要给自己的软件加上更好的保护。CRC校验就是其中的一种不错的方法。
CRC是什么东西呢?其实我们大家都不应该会对它陌生,回忆一下?你用过RAR和ZIP等压缩软件吗?它们是不是常常会给你一个恼人的“CRC校验错误”信息呢?我想你应该明白了吧,CRC就是块数据的计算值,它的全称是“CyclicRedundancyCheck”,中文名是“循环冗余码”,“CRC校验”就是“循环冗余校验”。(哇,真拗口,希望大家不要当我是唐僧,呵呵。^_^)
CRC有什么用呢?它的应用范围很广泛,最常见的就是在网络传输中进行信息的校对。其实我们大可以把它应用到软件保护中去,因为它的计算是非常非常非常严格的。严格到什么程度呢?你的程序只要被改动了一个字节(甚至只是大小写的改动),它的值就会跟原来的不同。Hoho,是不是很厉害呢?所以只要给你的“原”程序计算好CRC值,储存在某个地方,然后在程序中随机地再对文件进行CRC校验,接着跟第一次生成并保存好的CRC值进行比较,如果相等的话就说明你的程序没有被修改/破解过,如果不等的话,那么很可能你的程序遭到了病毒的感染,或者被Cracker用16进制工具暴力破解过了。 财 管家园 fs119.net
废话说完了,我们先来看看CRC的原理。
(由于CRC实现起来有一定的难度,所以具体怎样用它来保护文件,留待下一节再讲。)
首先看两个式子:
式一:9/3=3(余数=0)
式二:(92)/3=3(余数=2)
在小学里我们就知道,除法运算就是将被减数重复地减去除数X次,然后留下余数。
所以上面的两个式子可以用二进制计算为:(什么?你不会二进制计算?我倒~~~)
式一:
1001-->9
0011--->3
---------
0110-->6
0011--->3
---------
0011-->3
0011--->3
---------
0000-->0,余数
一共减了3次,所以商是3,而最后一次减出来的结果是0,所以余数为0
式二:
1011-->11
0011--->3
---------
1000-->8
0011--->3
---------
0101-->5
0011--->3
---------
0010-->2,余数
一共减了3次,所以商是3,而最后一次减出来的结果是2,所以余数为2 财软 联盟 fs119.net
看明白了吧?很好,let’sgoon!
二进制减法运算的规则是,如果遇到0-1的情况,那么要从高位借1,就变成了(100)-1=1
CRC运算有什么不同呢?让我们看下面的例子:
这次用式子30/9,不过请读者注意最后的余数:
11110-->30
1001--->9
---------
1100-->12(很奇怪吧?为什么不是21呢?)
1001--->9
--------
101-->3,余数-->theCRC!
这个式子的计算过程是不是很奇怪呢?它不是直接减的,而是用XOR的方式来运算(程序员应该都很熟悉XOR吧),最后得到一个余数。
对啦,这个就是CRC的运算方法,明白了吗?CRC的本质是进行XOR运算,运算的过程我们不用管它,因为运算过程对最后的结果没有意义;我们真正感兴趣的只是最终得到的余数,这个余数就是CRC值。
进行一个CRC运算我们需要选择一个除数,这个除数我们叫它为“poly”,宽度W就是最高位的位置,所以我刚才举的例子中的除数9,这个poly1001的W是3,而不是4,注意最高位总是1。(别问为什么,这个是规定)
如果我们想计算一个位串的CRC码,我们想确定每一个位都被处理过,因此,我们要在目标位串后面加上W个0位。现在让我们根据CRC的规范来改写一下上面的例子:
Poly=1001,宽度W=3
位串Bitstring=11110
BitstringWzeroes=11110000=11110000
11110000
1001||||-
-------------
1100|||
1001|||-
------------
1010||
1001||-
-----------
0110|
0000|-
----------
1100
1001-
---------
101-->3,余数-->theCRC!
还有两点重要声明如下:
1、只有当Bitstring的最高位为1,我们才将它与poly进行XOR运算,否则我们只是将Bitstring左移一位。
2、XOR运算的结果就是被操作位串Bitstring与poly的低W位进行XOR运算,因为最高位总为0。
呵呵,是不是有点头晕脑胀的感觉了?看不懂的话,再从头看一遍,其实是很好理解的。(就是一个XOR运算嘛!)
好啦,原理介绍到这里,下面我讲讲具体怎么编程。 财管家 园 fs119.net
由于速度的关系,CRC的实现主要是通过查表法,对于CRC-16和CRC-32,各自有一个现成的表,大家可以直接引入到程序中使用。(由于这两个表太长,在这里不列出来了,请读者自行在网络上查找,很容易找到的。)
如果我们没有这个表怎么办呢?或者你跟我一样,懒得自己输入?不用急,我们可以“自己动手,丰衣足食”。
你可能会说,自己编程来生成这个表,会不会太慢了?其实大可不必担心,因为我们是在汇编代码的级别进行运算的,而这个表只有区区256个双字,根本影响不了速度。
这个表的C语言描述如下:
for(i=0;i<256;i)
{
crc=i;
for(j=0;j<8;j)
{
if(crc&1)
crc=(crc>>1)^0xEDB88320;
else
crc>>=1;
}
crc32tbl[i]=crc;
}
生成表之后,就可以进行运算了。
我们的算法如下:
1、将寄存器向右边移动一个字节。
2、将刚移出的那个字节与我们的字符串中的新字节进行XOR运算,得出一个指向值表table[0..255]的索引。
3、将索引所指的表值与寄存器做XOR运算。
4、如果数据没有全部处理完,则跳到步骤1。
这个算法的C语言描述如下:
temp=(oldcrc^abyte)&0x000000FF;
crc=((oldcrc>>8)&0x00FFFFFF)^crc32tbl[temp];
returncrc;
好啦,所有的东东都说完啦,最后献上一个完整的Win32Asm例子,请读者仔细研究吧!
(汇编方面的CRC-32资料极少啊,我个人认为下面给出的是很宝贵的资料。)
;****************************************************
;程序名称:演示CRC32原理
;作者:罗聪
;日期:2002-8-24
;出处:http://laoluoc.yeah.net(老罗的缤纷天地)
;注意事项:如欲转载,请保持本程序的完整,并注明:转载自“老罗的缤纷天地”(http://laoluoc.yeah.net)
;
;特别感谢Win32ASM高手——dREAMtHEATER为我的代码作了相当好的优化!
;请各位前去http://NoteXPad.yeah.net下载他的小巧的“cool记事本”——NoteXPad来试用!(100%Win32ASM编写) 财管家 园 fs119.net
;
;****************************************************
.386
.modelflat,stdcall
optioncasemap:none
includewindows.inc
includekernel32.inc
includeuser32.inc
includelibkernel32.lib
includelibuser32.lib
WndProcproto:DWORD,:DWORD,:DWORD,:DWORD
init_crc32tableproto
arraycrc32proto
.const
IDC_BUTTON_OPENequ3000
IDC_EDIT_INPUTequ3001
.data
szDlgNamedb"lc_dialog",0
szTitledb"CRCdemobyLC",0
szTemplatedb"字符串""%s""的CRC32值是:%X",0
crc32tbldd256dup(0);CRC-32table
szBufferdb255dup(0)
.data?
szTextdb300dup(?)
.code
main:
invokeGetModuleHandle,NULL
invokeDialogBoxParam,eax,offsetszDlgName,0,WndProc,0
invokeExitProcess,eax
WndProcprocusesebxhWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
.ifuMsg==WM_CLOSE
invokeEndDialog,hWnd,0
.elseifuMsg==WM_COMMAND
moveax,wParam
movedx,eax
shredx,16
movzxeax,ax
.ifedx==BN_CLICKED
.IFeax==IDCANCEL
invokeEndDialog,hWnd,NULL 财管家园.fs119.net
.ELSEIFeax==IDC_BUTTON_OPEN||eax==IDOK
;******************************************
;关键代码开始:(当当当当……)
;******************************************
;取得用户输入的字符串:
invokeGetDlgItemText,hWnd,IDC_EDIT_INPUT,addrszBuffer,255
;初始化crc32table:
invokeinit_crc32table
;下面赋值给寄存器ebx,以便进行crc32转换:
;EBX是待转换的字符串的首地址:
leaebx,szBuffer
;进行crc32转换:
invokearraycrc32
;格式化输出:
invokewsprintf,addrszText,addrszTemplate,addrszBuffer,eax
;好啦,让我们显示结果:
invokeMessageBox,hWnd,addrszText,addrszTitle,MB_OK
.ENDIF
.endif
.ELSE
moveax,FALSE
ret
.ENDIF
moveax,TRUE
ret
WndProcendp
;**********************************************************
;函数功能:生成CRC-32表
;**********************************************************
init_crc32tableproc
;如果用C语言来表示,应该如下:
;
;for(i=0;i<256;i)
;{
;crc=i;
;for(j=0;j<8;j)
;{
;if(crc&1)
;crc=(crc>>1)^0xEDB88320;
;else
;crc>>=1;
;}
;crc32tbl[i]=crc;
;}
;
;呵呵,让我们把上面的语句改成assembly的:
movecx,256;repeatforeveryDWORDintable
movedx,0EDB88320h
$BigLoop:
leaeax,[ecx-1]
pushecx
movecx,8
$SmallLoop:
shreax,1
jnc@F
xoreax,edx
@@:
dececx
jne$SmallLoop
popecx
mov[crc32tblecx*4-4],eax
dececx
jne$BigLoop
ret
init_crc32tableendp
;**************************************************************
;函数功能:计算CRC-32
;**************************************************************
arraycrc32proc
;计算CRC-32,我采用的是把整个字符串当作一个数组,然后把这个数组的首地址赋值给EBX,把数组的长度赋值给ECX,然后循环计算,返回值(计算出来的CRC-32值)储存在EAX中:
;
;参数:
;EBX=addressoffirstbyte
;返回值:
;EAX=CRC-32oftheentirearray
;EBX=?
;ECX=0
;EDX=?
moveax,-1;先初始化eax
orebx,ebx
jz$Done;避免出现空指针
@@:
movdl,[ebx]
ordl,dl
je$Done;判断是否对字符串扫描完毕
;这里我用查表法来计算CRC-32,因此非常快速:
;因为这是assembly代码,所以不需要给这个过程传递参数,只需要把oldcrc赋值给EAX,以及把byte赋值给DL:
;
;在C语言中的形式:
;
;temp=(oldcrc^abyte)&0x000000FF;
;crc=((oldcrc>>8)&0x00FFFFFF)^crc32tbl[temp];
;
;参数:
;EAX=oldCRC-32
;DL=abyte
;返回值:
;EAX=newCRC-32
;EDX=?
xordl,al
movzxedx,dl
shreax,8
xoreax,[crc32tbledx*4]
incebx
jmp@B
$Done:
noteax
ret
arraycrc32endp
endmain
;********************over********************
;byLC
下面是它的.rc文件:
#include"resource.h"
#defineIDC_BUTTON_OPEN3000
#defineIDC_EDIT_INPUT3001
#defineIDC_STATIC-1 财软联盟 fs119.net
LC_DIALOGDIALOGEX10,10,195,60
STYLEDS_SETFONT|DS_CENTER|WS_MINIMIZEBOX|WS_VISIBLE|WS_CAPTION|
WS_SYSMENU
CAPTION"lc’sassemblyframework"
FONT9,"宋体",0,0,0x0
BEGIN
LTEXT"请输入一个字符串(区分大小写):",IDC_STATIC,11,7,130,10
EDITTEXTIDC_EDIT_INPUT,11,20,173,12,ES_AUTOHSCROLL
DEFPUSHBUTTON"Ca&lc",IDC_BUTTON_OPEN,71,39,52,15
END
如果你能够完全理解本节的内容,那么请留意我的下一讲,我将具体介绍如何运用CRC-32对你的文件进行保护。(呵呵,好戏在后头……)
文章摘自网络,如有侵权,请与我们联系.
(特别感谢汇编高手dREAMtHEATER对我(老罗)的代码作出了相当好的优化!请参观他的主页http://notexpad.yeah.net/)
上一节我们介绍了花指令,不过花指令毕竟是一种很简单的东西,基本上入了门的Cracker都可以对付得了。所以,我们很有必要给自己的软件加上更好的保护。CRC校验就是其中的一种不错的方法。
CRC是什么东西呢?其实我们大家都不应该会对它陌生,回忆一下?你用过RAR和ZIP等压缩软件吗?它们是不是常常会给你一个恼人的“CRC校验错误”信息呢?我想你应该明白了吧,CRC就是块数据的计算值,它的全称是“CyclicRedundancyCheck”,中文名是“循环冗余码”,“CRC校验”就是“循环冗余校验”。(哇,真拗口,希望大家不要当我是唐僧,呵呵。^_^)
CRC有什么用呢?它的应用范围很广泛,最常见的就是在网络传输中进行信息的校对。其实我们大可以把它应用到软件保护中去,因为它的计算是非常非常非常严格的。严格到什么程度呢?你的程序只要被改动了一个字节(甚至只是大小写的改动),它的值就会跟原来的不同。Hoho,是不是很厉害呢?所以只要给你的“原”程序计算好CRC值,储存在某个地方,然后在程序中随机地再对文件进行CRC校验,接着跟第一次生成并保存好的CRC值进行比较,如果相等的话就说明你的程序没有被修改/破解过,如果不等的话,那么很可能你的程序遭到了病毒的感染,或者被Cracker用16进制工具暴力破解过了。 财 管家园 fs119.net
废话说完了,我们先来看看CRC的原理。
(由于CRC实现起来有一定的难度,所以具体怎样用它来保护文件,留待下一节再讲。)
首先看两个式子:
式一:9/3=3(余数=0)
式二:(92)/3=3(余数=2)
在小学里我们就知道,除法运算就是将被减数重复地减去除数X次,然后留下余数。
所以上面的两个式子可以用二进制计算为:(什么?你不会二进制计算?我倒~~~)
式一:
1001-->9
0011--->3
---------
0110-->6
0011--->3
---------
0011-->3
0011--->3
---------
0000-->0,余数
一共减了3次,所以商是3,而最后一次减出来的结果是0,所以余数为0
式二:
1011-->11
0011--->3
---------
1000-->8
0011--->3
---------
0101-->5
0011--->3
---------
0010-->2,余数
一共减了3次,所以商是3,而最后一次减出来的结果是2,所以余数为2 财软 联盟 fs119.net
看明白了吧?很好,let’sgoon!
二进制减法运算的规则是,如果遇到0-1的情况,那么要从高位借1,就变成了(100)-1=1
CRC运算有什么不同呢?让我们看下面的例子:
这次用式子30/9,不过请读者注意最后的余数:
11110-->30
1001--->9
---------
1100-->12(很奇怪吧?为什么不是21呢?)
1001--->9
--------
101-->3,余数-->theCRC!
这个式子的计算过程是不是很奇怪呢?它不是直接减的,而是用XOR的方式来运算(程序员应该都很熟悉XOR吧),最后得到一个余数。
对啦,这个就是CRC的运算方法,明白了吗?CRC的本质是进行XOR运算,运算的过程我们不用管它,因为运算过程对最后的结果没有意义;我们真正感兴趣的只是最终得到的余数,这个余数就是CRC值。
进行一个CRC运算我们需要选择一个除数,这个除数我们叫它为“poly”,宽度W就是最高位的位置,所以我刚才举的例子中的除数9,这个poly1001的W是3,而不是4,注意最高位总是1。(别问为什么,这个是规定)
财管,家园,fs119.net
如果我们想计算一个位串的CRC码,我们想确定每一个位都被处理过,因此,我们要在目标位串后面加上W个0位。现在让我们根据CRC的规范来改写一下上面的例子:
Poly=1001,宽度W=3
位串Bitstring=11110
BitstringWzeroes=11110000=11110000
11110000
1001||||-
-------------
1100|||
1001|||-
------------
1010||
1001||-
-----------
0110|
0000|-
----------
1100
1001-
---------
101-->3,余数-->theCRC!
还有两点重要声明如下:
1、只有当Bitstring的最高位为1,我们才将它与poly进行XOR运算,否则我们只是将Bitstring左移一位。
2、XOR运算的结果就是被操作位串Bitstring与poly的低W位进行XOR运算,因为最高位总为0。
呵呵,是不是有点头晕脑胀的感觉了?看不懂的话,再从头看一遍,其实是很好理解的。(就是一个XOR运算嘛!)
好啦,原理介绍到这里,下面我讲讲具体怎么编程。 财管家 园 fs119.net
由于速度的关系,CRC的实现主要是通过查表法,对于CRC-16和CRC-32,各自有一个现成的表,大家可以直接引入到程序中使用。(由于这两个表太长,在这里不列出来了,请读者自行在网络上查找,很容易找到的。)
如果我们没有这个表怎么办呢?或者你跟我一样,懒得自己输入?不用急,我们可以“自己动手,丰衣足食”。
你可能会说,自己编程来生成这个表,会不会太慢了?其实大可不必担心,因为我们是在汇编代码的级别进行运算的,而这个表只有区区256个双字,根本影响不了速度。
这个表的C语言描述如下:
for(i=0;i<256;i)
{
crc=i;
for(j=0;j<8;j)
{
if(crc&1)
crc=(crc>>1)^0xEDB88320;
else
crc>>=1;
}
crc32tbl[i]=crc;
}
生成表之后,就可以进行运算了。
我们的算法如下:
1、将寄存器向右边移动一个字节。
2、将刚移出的那个字节与我们的字符串中的新字节进行XOR运算,得出一个指向值表table[0..255]的索引。
财软联盟 fs119.net
3、将索引所指的表值与寄存器做XOR运算。
4、如果数据没有全部处理完,则跳到步骤1。
这个算法的C语言描述如下:
temp=(oldcrc^abyte)&0x000000FF;
crc=((oldcrc>>8)&0x00FFFFFF)^crc32tbl[temp];
returncrc;
好啦,所有的东东都说完啦,最后献上一个完整的Win32Asm例子,请读者仔细研究吧!
(汇编方面的CRC-32资料极少啊,我个人认为下面给出的是很宝贵的资料。)
;****************************************************
;程序名称:演示CRC32原理
;作者:罗聪
;日期:2002-8-24
;出处:http://laoluoc.yeah.net(老罗的缤纷天地)
;注意事项:如欲转载,请保持本程序的完整,并注明:转载自“老罗的缤纷天地”(http://laoluoc.yeah.net)
;
;特别感谢Win32ASM高手——dREAMtHEATER为我的代码作了相当好的优化!
;请各位前去http://NoteXPad.yeah.net下载他的小巧的“cool记事本”——NoteXPad来试用!(100%Win32ASM编写) 财管家 园 fs119.net
;
;****************************************************
.386
.modelflat,stdcall
optioncasemap:none
includewindows.inc
includekernel32.inc
includeuser32.inc
includelibkernel32.lib
includelibuser32.lib
WndProcproto:DWORD,:DWORD,:DWORD,:DWORD
init_crc32tableproto
arraycrc32proto
.const
IDC_BUTTON_OPENequ3000
IDC_EDIT_INPUTequ3001
.data
szDlgNamedb"lc_dialog",0
szTitledb"CRCdemobyLC",0
szTemplatedb"字符串""%s""的CRC32值是:%X",0
crc32tbldd256dup(0);CRC-32table
szBufferdb255dup(0)
.data?
szTextdb300dup(?)
.code
main:
invokeGetModuleHandle,NULL
invokeDialogBoxParam,eax,offsetszDlgName,0,WndProc,0
invokeExitProcess,eax
WndProcprocusesebxhWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
.ifuMsg==WM_CLOSE
invokeEndDialog,hWnd,0
.elseifuMsg==WM_COMMAND
moveax,wParam
movedx,eax
shredx,16
movzxeax,ax
.ifedx==BN_CLICKED
.IFeax==IDCANCEL
invokeEndDialog,hWnd,NULL 财管家园.fs119.net
.ELSEIFeax==IDC_BUTTON_OPEN||eax==IDOK
;******************************************
;关键代码开始:(当当当当……)
;******************************************
;取得用户输入的字符串:
invokeGetDlgItemText,hWnd,IDC_EDIT_INPUT,addrszBuffer,255
;初始化crc32table:
invokeinit_crc32table
;下面赋值给寄存器ebx,以便进行crc32转换:
;EBX是待转换的字符串的首地址:
leaebx,szBuffer
;进行crc32转换:
invokearraycrc32
;格式化输出:
invokewsprintf,addrszText,addrszTemplate,addrszBuffer,eax
;好啦,让我们显示结果:
invokeMessageBox,hWnd,addrszText,addrszTitle,MB_OK
.ENDIF
.endif
.ELSE
moveax,FALSE
ret
.ENDIF
moveax,TRUE
ret
WndProcendp
;**********************************************************
;函数功能:生成CRC-32表
;**********************************************************
init_crc32tableproc
;如果用C语言来表示,应该如下:
财管,家园,fs119.net
;
;for(i=0;i<256;i)
;{
;crc=i;
;for(j=0;j<8;j)
;{
;if(crc&1)
;crc=(crc>>1)^0xEDB88320;
;else
;crc>>=1;
;}
;crc32tbl[i]=crc;
;}
;
;呵呵,让我们把上面的语句改成assembly的:
movecx,256;repeatforeveryDWORDintable
movedx,0EDB88320h
$BigLoop:
leaeax,[ecx-1]
pushecx
movecx,8
$SmallLoop:
shreax,1
jnc@F
xoreax,edx
@@:
dececx
jne$SmallLoop
popecx
mov[crc32tblecx*4-4],eax
dececx
jne$BigLoop
ret
init_crc32tableendp
;**************************************************************
;函数功能:计算CRC-32
;**************************************************************
arraycrc32proc
;计算CRC-32,我采用的是把整个字符串当作一个数组,然后把这个数组的首地址赋值给EBX,把数组的长度赋值给ECX,然后循环计算,返回值(计算出来的CRC-32值)储存在EAX中:
;
;参数:
;EBX=addressoffirstbyte
财软联盟 fs119.net
;返回值:
;EAX=CRC-32oftheentirearray
;EBX=?
;ECX=0
;EDX=?
moveax,-1;先初始化eax
orebx,ebx
jz$Done;避免出现空指针
@@:
movdl,[ebx]
ordl,dl
je$Done;判断是否对字符串扫描完毕
;这里我用查表法来计算CRC-32,因此非常快速:
;因为这是assembly代码,所以不需要给这个过程传递参数,只需要把oldcrc赋值给EAX,以及把byte赋值给DL:
;
;在C语言中的形式:
;
;temp=(oldcrc^abyte)&0x000000FF;
;crc=((oldcrc>>8)&0x00FFFFFF)^crc32tbl[temp];
;
;参数:
;EAX=oldCRC-32
;DL=abyte
;返回值:
;EAX=newCRC-32
;EDX=?
xordl,al
movzxedx,dl
shreax,8
xoreax,[crc32tbledx*4]
incebx
jmp@B
$Done:
noteax
ret
arraycrc32endp
endmain
;********************over********************
;byLC
下面是它的.rc文件:
#include"resource.h"
#defineIDC_BUTTON_OPEN3000
#defineIDC_EDIT_INPUT3001
#defineIDC_STATIC-1 财软联盟 fs119.net
LC_DIALOGDIALOGEX10,10,195,60
STYLEDS_SETFONT|DS_CENTER|WS_MINIMIZEBOX|WS_VISIBLE|WS_CAPTION|
WS_SYSMENU
CAPTION"lc’sassemblyframework"
FONT9,"宋体",0,0,0x0
BEGIN
LTEXT"请输入一个字符串(区分大小写):",IDC_STATIC,11,7,130,10
EDITTEXTIDC_EDIT_INPUT,11,20,173,12,ES_AUTOHSCROLL
DEFPUSHBUTTON"Ca&lc",IDC_BUTTON_OPEN,71,39,52,15
END
如果你能够完全理解本节的内容,那么请留意我的下一讲,我将具体介绍如何运用CRC-32对你的文件进行保护。(呵呵,好戏在后头……)
财管家园.fs119.net
数据统计中!!
Google.cn搜索关键字:
原理 较量 我们 CRC eax 运算 crc 这个 进行 计算 CRC-32
Google.cn搜索相关文章:
谷歌中搜索全球网 矛与盾的较量——CRC原理篇
百度中搜索 矛与盾的较量——CRC原理篇
谷歌中搜索www.fs119.net 矛与盾的较量——CRC原理篇
上一篇:关于在8086/88内存寻址方式
Google.cn搜索相关文章:
谷歌中搜索全球网 矛与盾的较量——CRC原理篇
百度中搜索 矛与盾的较量——CRC原理篇
谷歌中搜索www.fs119.net 矛与盾的较量——CRC原理篇
下一篇:矛与盾的较量——CRC实践篇
推荐主题
