導航:首頁 > 編程語言 > 匯編程序例子

匯編程序例子

發布時間:2023-03-24 01:19:40

❶ 寫出一個匯編語言的框架程序,內容2個段,數據段和代碼段

呵呵,建議你下了解一下殺毒軟體的工作機制。
金山,360,瑞星等殺毒軟體,都是驅動保護的,bat因為比它高層,所以做不到關閉殺毒軟體。否則連批處理都可輕易攻破,那早就有無數病毒泛濫了。只有匯編語言這樣的底層語言才可以,(其實C語言也能做到)。當然,bat也不是不行,可以用bat匯編,叫做ASCIICoding技術,挺難的。2007年以前國內還沒有一個會的(除非會但不說,那我當然就不知道了)。也建議你學習Rootkit Hook技術,可以讀一讀這篇文章。

瘟神的尾行--Rootkit技術發展史
作者:小金
一. 無法驅逐的「助手」
網管小張正在手忙腳亂的尋找他的手工殺毒工具包,因為他在安裝一個網管工具的時候無意中走了神,點擊了「下一步」按鈕後才驚覺安裝程序的界面里一個不引人注目的角落裡寫著「安裝CNNIC網路實名」這一行小字,而且最開頭部分有一個小小的勾。於是著名的「中國網民的得力助手」便理所當然的在他的機器里安了家。
心裡把廠商罵了十八遍的小張終於翻出了他外出修機時最得意的工具IceSword和超級巡警,果然在進程列表和SSDT列表裡發現了紅色警報,小張笑了笑,對付這些一般用戶無法卸載的惡意流氓,自己可謂經驗豐富了,當下便三下五除二的把CNNIC的進程給終結了,SSDT也給恢復了初始狀態,然後小張去刪除注冊表啟動項——突然發出的一個錯誤提示聲音把小張嚇了一跳,再定睛一看,他的笑容凝固了:「刪除項時出錯」。不會吧?小張急忙去刪除CNNIC目錄,結果徹底愣在了那裡,系統彈出的錯誤提示很明確的告訴他,「無法刪除文件,文件可能正在被使用」。怎麼回事?小張一下子沒了頭緒……
達爾文的進化論告訴我們,「物競天擇,適者生存」,同樣,在這安全與入侵的網路世界裡,也在進行著這樣一場選擇的過程……

二. 被AIDS糾纏的互聯網
。。。。。。。。。
。。。。。。。。。。。。。
。。。。。。。。。。。。。。(網路長度限制。。。想看全篇自己搜吧)
三. 結語

雖然到處都在提倡和諧網路的普及,但是,「健康上網」僅僅是指代那些黃賭毒而已嗎?在利益面前,開發者的正義感越發渺小起來,我們的網路世界,是被瘟神緊緊跟隨著的。技術的斗爭越發激烈,但是用戶的電腦知識是不會跟著時代發展而自動填充的,最終,大眾上網的人民成了這一切技術較量的受害者。
這個荒謬的發展方向,何時才能休止呢?

還有這篇:

對大多數的Windows開發者來說,如何在Win32系統中對API函數的調用進行攔截一直是項極富挑戰性的課題,因為這將是對你所掌握的計算機知識較為全面的考驗,尤其是一些在如今使用RAD進行軟體開發時並不常用的知識,這包括了操作系統原理、匯編語言甚至是機器指令(聽上去真是有點恐怖,不過這是事實)。

當前廣泛使用的Windows操作系統中,像Win 9x和Win NT/2K,都提供了一種比較穩健的機制來使得各個進程的內存地址空間之間是相互獨立,也就是說一個進程中的某個有效的內存地址對另一個進程來說是無意義的,這種內存保護措施大大增加了系統的穩定性。不過,這也使得進行系統級的API攔截的工作的難度也大大加大了。

當然,我這里所指的是比較文雅的攔截方式,通過修改可執行文件在內存中的映像中有關代碼,實現對API調用的動態攔截;而不是採用比較暴力的方式,直接對可執行文件的磁碟存儲中機器代碼進行改寫。

二、API鉤子系統一般框架
通常,我們把攔截API的調用的這個過程稱為是安裝一個API鉤子(API Hook)。一個API鉤子基本是由兩個模塊組成:一個是鉤子伺服器(Hook Server)模塊,一般為EXE的形式;一個是鉤子驅動器(Hook Driver)模塊,一般為DLL的形式。

鉤子伺服器主要負責向目標進程注入鉤子驅動器,使得鉤子驅動器運行在目標進程的地址空間中,這是關鍵的第一步,而鉤子驅動器則負責實際的API攔截處理工作,以便在我們所關心的API函數調用的之前或之後能做一些我們所希望的工作。一個比較常見的API鉤子的例子就是一些實時翻譯軟體(像金山詞霸)中必備的的功能:屏幕抓詞。它主要是對一些Win32 API中的GDI函數進行了攔截,獲取它們的輸入參數中的字元串,然後在自己的窗口中顯示出來。

針對上述關於API鉤子的兩個部分,有以下兩點需要我們重點考慮的: 選用何種DLL注入技術,以及採用何種API攔截機制。

三、注入技術的選用
由於在Win32系統中各個進程的地址是互相獨立的,因此我們無法在一個進程中對另一個進程的代碼進行有效的修改,但如果你要完成API鉤子的工作又必須如此。因此,我們必須採取某種獨特的手段,使得API鉤子(准確的說是鉤子驅動器)能夠成為目標進程中的一部分,才有較大的可能來對目標進程數據和代碼進行有控制的修改。

通常可採用的幾種注入方式:

1.利用注冊表
如果我們准備攔截的進程連接了User32.dll,也就是使用了 User32.dll中的API(一般圖形界面的應用程序都是符合這個條件),那麼就可以簡單把你的鉤子驅動器DLL的名字作為值添加在下面注冊表的鍵下: HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows\AppInit_DLLs 值的形式可以為單個DLL的文件名,或者是一組DLL的文件名,相鄰的名稱之間用逗號或空格間隔。所有由該值標識的DLL將在符合條件的應用程序啟動的時候裝載。這是一個操作系統內建的機制,相對其他方式來說危險性較小,但它也有一些比較明顯的缺點:該方法僅適用於NT/2K操作系統,顯然看看鍵的名稱就可以明白;如果需要激活或停止鉤子的注入,只有重新啟動Windows,這個就似乎太不方便了;最後一點也很顯然,不能用此方法向沒有使用User32.dll的應用程序注入DLL,例如控制台應用程序等。另外,不管是否為你所希望,鉤子DLL將注入每一個GUI應用程序,這將導致整個系統性能的下降!

2.建立系統范圍的Windows鉤子
要向某個進程注入DLL,一個十分普遍也是比較簡單的方法就是建立在標準的Windows鉤子的基礎上。Windows鉤子一般是在DLL中實現的,這是一個全局性的Windows鉤子的基本要求,這也很符合我們的需要。當我們成功地調用SetWindowsHookEx函數之後,便在系統中安裝了某種類型的消息鉤子,這個鉤子可以是針對某個進程,也可以是針對系統中的所有進程。一旦某個進程中產生了該類型的消息,操作系統會自動把該鉤子所在的DLL映像到該進程的地址空間中,從而使得消息回調函數(在 SetWindowsHookEx的參數中指定)能夠對此消息進行適當的處理,在這里,我們所感興趣的當然不是對消息進行什麼處理,因此在消息回調函數中只需把消息鉤子向後傳遞就可以了,但是我們所需的DLL已經成功地注入了目標進程的地址空間,從而可以完成後續工作。

我們知道,不同的進程之間是不能直接共享數據的,因為它們活動在不同的地址空間中。但在Windows鉤子 DLL中,有一些數據,例如Windows鉤子句柄HHook,這是由SetWindowsHookEx函數返回值得到的,並且作為參數將在 CallNextHookEx函數和UnhookWindoesHookEx函數中使用,顯然使用SetWindowsHookEx函數的進程和使用 CallNextHookEx函數的進程一般不會是同一個進程,因此我們必須能夠使句柄在所有的地址空間中都是有效的有意義的,也就是說,它的值必須必須在這些鉤子DLL所掛鉤的進程之間是共享的。為了達到這個目的,我們就應該把它存儲在一個共享的數據區域中。

在VC++中我們可以採用預編譯指令#pragma data_seg在DLL文件中創建一個新的段,並且在DEF文件中把該段的屬性設置為"shared",這樣就建立了一個共享數據段。對於使用 Delphi的人來說就沒有這么幸運了:沒有類似的比較簡單的方法(或許是有的,但我沒有找到)。不過我們還是可以利用內存映像技術來申請使用一塊各進程可以共享的內存區域,主要是利用了CreateFileMapping和MapViewOfFile這兩個函數,這倒是一個通用的方法,適合所有的開發語言,只要它能直接或間接的使用Windows的API。

在Borland的BCB中有一個指令#pragma codeseg與VC++中的#pragma data_seg指令有點類似,應該也能起到一樣的作用,但我試了一下,沒有沒有效果,而且BCB的聯機幫助中對此也提到的不多,不知怎樣才能正確的使用(或許是另外一個指令,呵呵)。

一旦鉤子DLL載入進入目標進程的地址空間後,在我們調用UnHookWindowsHookEx函數之前是無法使它停止工作的,除非目標進程關閉。

這種DLL注入方式有兩個優點: 這種機制在Win 9x/Me和Win NT/2K中都是得到支持的,預計在以後的版本中也將得到支持;鉤子DLL可以在不需要的時候,可由我們主動的調用 UnHookWindowsHookEx來卸載,比起使用注冊表的機制來說方便了許多。盡管這是一種相當簡潔明了的方法,但它也有一些顯而易見的缺點:首先值得我們注意的是,Windows鉤子將會降低整個系統的性能,因為它額外增加了系統在消息處理方面的時間;其次,只有當目標進程准備接受某種消息時,鉤子所在的DLL才會被系統映射到該進程的地址空間中,鉤子才能真正開始發揮作用,因此如果我們要對某些進程的整個生命周期內的API調用情況進行監控,用這種方法顯然會遺漏某些API的調用 。

3.使用 CreateRemoteThread函數
在我看來這是一個相當棒的方法,然而不幸的是,CreateRemoteThread這個函數只能在Win NT/2K系統中才得到支持,雖然在Win 9x中這個API也能被安全的調用而不出錯,但它除了返回一個空值之外什麼也不做。該注入過程也十分簡單:我們知道,任何一個進程都可以使用 LoadLibrary來動態地載入一個DLL。但問題是,我們如何讓目標進程(可能正在運行中)在我們的控制下來載入我們的鉤子DLL(也就是鉤子驅動器)呢?有一個API函數CreateRemoteThread,通過它可在一個進程中可建立並運行一個遠程的線程--這個好像和注入沒什麼關系嘛?往下看!

調用該API需要指定一個線程函數指針作為參數,該線程函數的原型如下: Function ThreadProc(lpParam: Pointer): DWORD,我們再來看一下LoadLibrary的函數原型: Function LoadLibrary(lpFileName: PChar): HMole。發現了吧!這兩個函數原型幾乎是一樣的(其實返回值是否相同關系不大,因為我們是無法得到遠程線程函數的返回值的),這種類似使得我們可以把直接把LoadLibrary當做線程函數來使用,從而在目標進程中載入鉤子DLL。

與此類似,當我們需要卸載鉤子DLL時,也可以FreeLibrary作為線程函數來使用,在目標進程中卸載鉤子DLL,一切看來是十分的簡潔方便。通過調用GetProcAddress函數,我們可以得到LoadLibrary函數的地址。由於 LoadLibrary是Kernel32中的函數,而這個系統DLL的映射地址對每一個進程來說都是相同的,因此LoadLibrary函數的地址也是如此。這點將確保我們能把該函數的地址作為一個有效的參數傳遞給CreateRemoteThread使用。 FreeLibrary也是一樣的。

AddrOfLoadLibrary := GetProcAddress(GetMoleHandle(『Kernel32.dll'), 『LoadLibrary');

HRemoteThread := CreateRemoteThread(HTargetProcess, nil, 0, AddrOfLoadLibrary, HookDllName, 0, nil);

要使用CreateRemoteThread,我們需要目標進程的句柄作為參數。當我們用 OpenProcess函數來得到進程的句柄時,通常是希望對此進程有全權的存取操作,也就是以PROCESS_ALL_ACCESS為標志打開進程。但對於一些系統級的進程,直接這樣顯然是不行的,只能返回一個的空句柄(值為零)。為此,我們必須把自己設置為擁有調試級的特權,這樣將具有最大的存取許可權,從而使得我們能對這些系統級的進程也可以進行一些必要的操作。

4.通過BHO來注入DLL
有時,我們想要注入DLL的對象僅僅是Internet Explorer,很幸運,Windows操作系統為我們提供了一個簡單的歸檔方法(這保證了它的可靠性!)―― 利用Browser Helper Objects(BHO)。一個BHO是一個在 DLL中實現的COM對象,它主要實現了一個IObjectWithSite介面,而每當IE運行時,它會自動載入所有實現了該介面的COM對象。

四、攔截機制
在鉤子應用的系統級別方面,有兩類API攔截的機制――內核級的攔截和用戶級的攔截。內核級的鉤子主要是通過一個內核模式的驅動程序來實現,顯然它的功能應該最為強大,能捕捉到系統活動的任何細節,但難度也較大,不在本文的探討范圍之內(尤其對我這個使用Delphi的人來說,還沒涉足這塊領域,因此也無法探討,呵呵)。

而用戶級的鉤子則通常是在普通的DLL中實現整個API的攔截工作,這才是此次重點關注的。攔截API函數的調用,一般可有以下幾種方法:

1. 代理DLL(特洛伊木馬
一個容易想到的可行的方法是用一個同名的DLL去替換原先那個輸出我們准備攔截的API所在的DLL。當然代理DLL也要和原來的一樣,輸出所有函數。但如果想到DLL中可能輸出了上百個函數,我們就應該明白這種方法的效率是不高的,估計是要累死人的。另外,我們還不得不考慮DLL的版本問題,很是麻煩。

2.改寫執行代碼
有許多攔截的方法是基於可執行代碼的改寫,其中一個就是改變在CALL指令中使用的函數地址,這種方法有些難度,也比較容易出錯。它的基本思路是檢索出在內存中所有你所要攔截的API的CALL指令,然後把原先的地址改成為你自己提供的函數的地址。

另外一種代碼改寫的方法的實現方法更為復雜,它的主要的實現步驟是先找到原先的API函數的地址,然後把該函數開始的幾個位元組用一個JMP指令代替(有時還不得不改用一個INT指令),從而使得對該API函數的調用能夠轉向我們自己的函數調用。實現這種方法要牽涉到一系列壓棧和出棧這樣的較底層的操作,顯然對我們的匯編語言和操作系統底層方面的知識是一種考驗。這個方法倒和很多文件型病毒的感染機制相類似。

3.以調試器的身份進行攔截
另一個可選的方法是在目標函數中安置一個調試斷點,使得進程運行到此處就進入調試狀態。然而這樣一些問題也隨之而來,其中較主要的是調試異常的產生將把進程中所有的線程都掛起。它也需要一個額外的調試模塊來處理所有的異常,整個進程將一直在調試狀態下運行,直至它運行結束。

4.改寫PE文件的輸入地址表
這種方法主要得益於現如今Windows系統中所使用的可執行文件(包括EXE文件和DLL文件)的良好結構――PE文件格式(Portable Executable File Format),因此它相當穩健,又簡單易行。要理解這種方法是如何運作的,首先你得對PE文件格式有所理解。

一個PE文件的結構大致如下所示:一般PE文件一開始是一段DOS程序,當你的程序在不支持Windows的環境中運行時,它就會顯示"This Program cannot be run in DOS mode"這樣的警告語句;接著這個DOS文件頭,就開始真正的PE文件內容了,首先是一段稱為"IMAGE_NT_HEADER"的數據,其中是許多關於整個PE文件的消息,在這段數據的尾端是一個稱為Data Directory的數據表,通過它能快速定位一些PE文件中段(section)的地址;在這段數據之後,則是一個"IMAGE_SECTION_HEADER"的列表,其中的每一項都詳細描述了後面一個段的相關信息;接著它就是PE文件中最主要的段數據了,執行代碼、數據和資源等等信息就分別存放在這些段中。

在所有的這些段里,有一個被稱為".idata"的段(輸入數據段)值得我們去注意,該段中包含著一些被稱為輸入地址表(IAT,Import Address Table)的數據列表,每個用隱式方式載入的API所在的DLL都有一個IAT與之對應,同時一個API的地址也與IAT中一項相對應。當一個應用程序載入到內存中後,針對每一個API函數調用,相應的產生如下的匯編指令:

JMP DWORD PTR [XXXXXXXX]

如果在VC++中使用了_delcspec(import),那麼相應的指令就成為:

CALL DWORD PTR [XXXXXXXX]。

不管怎樣,上述方括弧中的總是一個地址,指向了輸入地址表中一個項,是一個DWORD,而正是這個 DWORD才是API函數在內存中的真正地址。因此我們要想攔截一個API的調用,只要簡單的把那個DWORD改為我們自己的函數的地址,那麼所有關於這個API的調用將轉到我們自己的函數中去,攔截工作也就宣告順利的成功了。這里要注意的是,自定義的函數的調用約定應該是API的調用約定,也就是 stdcall,而Delphi中默認的調用約定是register,它們在參數的傳遞方法等方面存在著較大的區別。

另外,自定義的函數的參數形式一般來講和原先的API函數是相同的,不過這也不是必須的,而且這樣的話在有些時候也會出現一些問題,我在後面將會提到。因此要攔截API的調用,首先我們就要得到相應的IAT的地址。系統把一個進程模塊載入到內存中,其實就是把 PE文件幾乎是原封不動的映射到進程的地址空間中去,而模塊句柄HMole實際上就是模塊映像在內存中的地址,PE文件中一些數據項的地址,都是相對於這個地址的偏移量,因此被稱為相對虛擬地址(RVA,Relative Virtual Address)。

於是我們就可以從HMole開始,經過一系列的地址偏移而得到IAT的地址。不過我這里有一個簡單的方法,它使用了一個現有的API函數ImageDirectoryEntryToData,它幫助我們在定位IAT時能少走幾步,省得把偏移地址弄錯了,走上彎路。不過純粹使用RVA從HMole開始來定位IAT的地址其實並不麻煩,而且這樣還更有助於我們對PE文件的結構的了解。上面提到的那個API 函數是在DbgHelp.dll中輸出的(這是從Win 2K才開始有的,在這之前是由ImageHlp.dll提供的),有關這個函數的詳細介紹可參見MSDN。

在找到IAT之後,我們只需在其中遍歷,找到我們需要的API地址,然後用我們自己的函數地址去覆蓋它,下面給出一段對應的源碼

procere RedirectApiCall; var ImportDesc:PIMAGE_IMPORT_DESCRIPTOR; FirstThunk:PIMAGE_THUNK_DATA32; sz:DWORD;
begin

//得到一個輸入描述結構列表的首地址,每個DLL都對應一個這樣的結構 ImportDesc:=ImageDirectoryEntryToData(Pointer(HTargetMole), true, IMAGE_DIRECTORY_ENTRY_IMPORT, sz);

while Pointer(ImportDesc.Name)<>nil do
begin //判斷是否是所需的DLL輸入描述

if StrIComp(PChar(DllName),PChar(HTargetMole+ImportDesc.Name))=0 then begin

//得到IAT的首地址
FirstThunk:=PIMAGE_THUNK_DATA32(HTargetMole+ImportDesc.FirstThunk);

while FirstThunk.Func<>nil do
begin

if FirstThunk.Func=OldAddressOfAPI then
begin

//找到了匹配的API地址 ......
//改寫API的地址

break;

end;

Inc(FirstThunk);

end;

end;

Inc(ImportDesc);

end;

end;

最後有一點要指出,如果我們手工執行鉤子DLL的退出目標進程,那麼在退出前應該把函數調用地址改回原先的地址,也就是API的真正地址,因為一旦你的DLL退出了,改寫的新的地址將指向一個毫無意義的內存區域,如果此時目標進程再使用這個函數顯然會出現一個非法操作。

五、替換函數的編寫
前面關鍵的兩步做完了,一個API鉤子基本上也就完成了。不過還有一些相關的東西需要我們研究一番的,包括怎樣做一個替換函數。 下面是一個做替換函數的步驟: 首先,不失一般性,我們先假設有這樣的一個API函數,它的原型如下:

function SomeAPI(param1: Pchar;param2: Integer): DWORD;

接著再建立一個與之有相同參數和返回值的函數類型:

type FuncType= function (param1: Pchar;param2: Integer): DWORD;

然後我們把SomeAPI函數的地址存放在OldAddress指針中。接著我們就可以著手寫替換函數的代碼了:

function DummyFunc(param1: Pchar;param2: Integer): DWORD; begin ......

//做一些調用前的操作

//調用被替換的函數,當然也可以不調用
result := FuncType(OldAddress) (param1 , param2);

//做一些調用後的操作

end;

我們再把這個函數的地址保存到NewAddress中,接著用這地址覆蓋掉原先API的地址。這樣當目標進程調用該API的時候,實際上是調用了我們自己的函數,在其中我們可以做一些操作,然後在調用原先的API函數,結果就像什麼也沒發生過一樣。當然,我們也可以改變輸入參數的值,甚至是屏蔽調這個API函數的調用。

盡管上述方法是可行的,但有一個明顯的不足:這種替換函數的製作方法不具有通用性,只能針對少量的函數。如果只有幾個API要攔截,那麼只需照上述說的重復做幾次就行了。但如果有各種各樣的API要處理,它們的參數個數和類型以及返回值的類型是各不相同的,仍然採用這種方法就太沒效率了。

的確是的,上面給出的只是一個最簡單最容易想到的方法,只是一個替換函數的基本構架。正如我前面所提到的,替換函數的與原先的API函數的參數類型不必相同,一般的我們可以設計一個沒有調用參數也沒有返回值的函數,通過一定的技巧,使它能適應各種各樣的API 函數調用,不過這得要求你對匯編語言有一定的了解。

首先,我們來看一下執行到一個函數體內前的系統堆棧情況(這里函數的調用方式為stdcall),函數的調用參數是按照從右到左的順序壓入堆棧的(堆棧是由高端向低端發展的),同時還壓入了一個函數返回地址。在進入函數之前,ESP正指向返回地址。因此,我們只要從ESP+4開始就可以取得這個函數的調用參數了,每取一個參數遞增4。另外,當從函數中返回時,一般在EAX中存放函數的返回值。

了解了上述知識,我們就可以設計如下的一個比較通用的替換函數,它利用了Delphi的內嵌式匯編語言的特性。

Procere DummyFunc;

asm add esp,4 mov eax,esp//得到第一個參數

mov eax,esp+4//得到第二個參數 ......

//做一些處理,這里要保證esp在這之後恢復原樣

call OldAddress //調用原先的API函數 ......

//做一些其它的事情

end;

當然,這個替換函數還是比較簡單的,你可以在其中調用一些純粹用OP語言寫的函數或過程,去完成一些更復雜的操作(要是都用匯編來完成,那可得把你忙死了),不過應該把這些函數的調用方式統一設置為stdcall方式,這使它們只利用堆棧來傳遞參數,因此你也只需時刻掌握好堆棧的變化情況就行了。如果你直接把上述匯編代碼所對應的機器指令存放在一個位元組數組中,然後把數組的地址當作函數地址來使用,效果是一樣的。

六、後記
做一個API鉤子的確是件不容易的事情,尤其對我這個使用Delphi的人來說,為了解決某個問題,經常在OP、C++和匯編語言的資料中東查西找,在程序調試中還不時的發生一些意想不到的事情,弄的自己是手忙腳亂。不過,好歹總算做出了一個 API鉤子的雛形,還是令自己十分的高興,對計算機系統方面的知識也掌握了不少,受益非淺。當初在寫這篇文章之前,我只是想翻譯一篇從網上Down下來的英文資料(網址為 ,文章名叫"API Hook Revealed",示例源代碼是用VC++寫的,這里不得不佩服老外的水平,文章寫得很有深度,而且每個細節都講的十分詳細)。

❷ 匯編語言是怎麼調用c語言的程序的

一、 參數傳遞的基本規則(ATPCS(ARM—Thumb Procere Call Standard))
1、 參數傳遞

二、匯編程序、C程序相互調用舉例
1、 C程序調用匯編程序
匯編程序的設計要遵守ATPCS(ARM—Thumb Procere Call Standard),保證程序調用時參數的正確傳遞。在匯編程序中使用EXPORT 偽操作聲明本程序,使得本程序可以被別的程序調用。在C程序使用extern聲明該匯編程序。
下面是一個C程序調用匯編程序的例子。其中匯編程序str實現字元串復制功能,C程序調用str完成字元串復制的工作。
//C程序
#include <stdio.h>
extern void str(char *d, const char *s);
int main( )
{
const char *srcstr=」First string-source」;
char dststr[ ]=」Second string-destination」;
printf(「Before ing:\n」);
printf(「%s\n %s\n」, srcstr,dststr);
str(dststr,srcstr);
printf(「After ing:\n」);
printf(「%s\n %s\n 「,srcstr,dststr);
while(1) ;
}

;匯編程序
AREA S, CODE, READONLY
EXPORT str
Str
LDRB R2, [R1], #1
STRB R2, [R0], #1
CMPR2,#0
BNE Str
MOV PC, LR
END
2、 匯編程序調用C程序
匯編程序的設計要遵守ATPCS,保證程序調用時參數的正確傳遞。在匯編程序中使用IMPORT偽操作聲明將要調用的C程序。下面是一個匯編程序調用C程序的例子。其中在匯編程序中設置好各參數的值。本例中有6個參數,分別使用寄存器R0存放第1個參數,
R1存放第2個參數, R2存放第3個參數, R3存放第4個參數, 第5個、第6個參數利用數據棧傳送。由於利用數據棧傳遞參數,在程序調用結束後要調整數據棧指針。
//C程序g( )返回6個參數的和
int g( int a, int b, int c, int d, int e, int f )
{
printf(「e=%d\n」, e);
printf(「f=%d\n」, f);
return (a+b+c+d+e+f);
}

; 匯編程序調用C程序 g( ) 計算6個整數 i, 2*i, 3*i, 4*i, 5*i, 6*i的和
EXPORT f
AREA f ,CODE, READONLY
IMPORT g
MOV R0, #1
ADD R1, R0, R0
ADD R2, R1, R0
ADD R3, R2, R0
ADD R4, R3, R0
ADD R5, R4, R0
STR R4, [SP, #-4]!
STR R5, [SP, #-4]!
BL g
ADD SP, SP, #4
ADD SP, SP, #4
STOP B STOP
END

❸ 匯編代碼實例

偽 指 令
偽指令是對匯編起某種控製作用的特殊命令,其格式與通常的操作指令一樣,並可加在匯編程序的任何地方,但它們並不產生機器指令。許多偽指令要求帶參數,這在定義偽指令時由「表達式」域指出,任何數值與表達式勻可以作為參數。不同匯編程序允許的偽指令並不相同,以下所述的偽指令僅適用於MASM51系統,但一些基本的偽指令在大部份匯編指弊好程序中都能使用,當使用其它的匯編程序版本時,只要注意一下它們之間的區別就可以了。MASM51中可用的偽指令有:ORG 設置程序起始地址END 標志源代碼結束EQU 定義常數SET 定義整型數DATA 給位元組類型符號定值BYTE 給位元組類型符號定值WROD 給字類唯鉛型符號定值BIT 給位地址取名ALTNAME 用自定義名取代保留字DB 給一塊連續的存儲區裝載位元組型數據DW 給一塊連續的存儲區裝載字型數據DS 預留一個連續的存儲區或裝入指定位元組。INCLUDE 將一個源文件插入程序中TITLE 列表文件中加入標題行NOLIST 匯編時不產生列表文件NOCODE 條件匯編時,條件為假的不產生清單一、ORG 偽指令ORG用於為在它之後的程序設置地址值,它有一個參數,其格式為:ORG 表達式表達式可以是一個具體的數值,也可以包含變數名,如果包含變數名,則必須保證,當第一次遇到這條偽指令時,其中的變數必須已有定義(已有具體的數值),否則,無定義的值將由0替換,這將會造成錯誤。在列表文件中,由ORG定義的指令地址會被列印出來。ORG指令有什麼用途呢?指令被翻譯成機器碼後,將被存入系統的ROM中,一般情況下,機器碼總是一個接一個地放在存儲器中,但有一些代碼,其位置有特殊要求,典型的是五個中斷入口,它們必須被放在0003H,000BH,0013H,001BH和0023H的位置,否則就會出錯,如果我們編程時不作特殊處理,讓機器代碼一個接一個地生成,不能保證這些代碼正好處於這些規定的位置,執行就會出錯,這時就要用到ORG偽指令了。看如下例子:例:INT_0 EQU 1000HTIME_0 EQU 1010HINT_1 EQU 1020HTIME_1 EQU 1030HSERIAL EQU 1040HAJMP START ;跳轉到主程序起始點LJMP INT_0 ;外中斷0處理程序LJMP TIME_0 ;定時中斷0處理程序LJMP INT_1 ;外中斷1處理卜行程序LJMP TIME_1 ;定時中斷1處理程序LJMP SERIAL ;串列口中斷程序START:NOPEND上面的程序經匯編後列表文件如下:The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 108-26-96 1000 = INT_0 EQU 1000H1010 = TIME_0 EQU 1010H1020 = INT_1 EQU 1020H1030 = TIME_1 EQU 1030H1040 = SERIAL EQU 1040H0000 0111 AJMP START ;跳轉到主程序起始點0002 021000 LJMP INT_0 ;外中斷0處理程序0005 021010 LJMP TIME_0 ;定時中斷0處理程序0008 021020 LJMP INT_1 ;外中斷1處理程序000B 021030 LJMP TIME_1 ;定時中斷1處理程序000E 021040 LJMP SERIAL ;串列口中斷程序START:0011 00 NOP0000 ENDThe Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 208-26-96;%T Symbol Name Type ValueINT_0 . . . . . . . . . . . . . I 1000INT_1 . . . . . . . . . . . . . I 1020SERIAL. . . . . . . . . . . . . I 1040START . . . . . . . . . . . . . L 0011TIME_0. . . . . . . . . . . . . I 1010TIME_1. . . . . . . . . . . . . I 1030;%Z00 Errors (0000)由列表文件,可以繪出代碼在ROM中的映象圖如下:代碼
01H
11H
02H
10H
00H
02H
10H
10H
02H
10H
20H

地址
00H
01H
02H
03H
04H
05H
06H
07H
08H
09H
0AH

代碼
02H
10H
30H
02H
10H
40H
00H





地址
0BH
0CH
0DH
0EH
0FH
10H
11H
12H
13H
14H
15
由上面的映象圖可知,在03H處的代碼為10H,而不是我們要的02H,所以外斷程序INT_0不能被正確執行,其它各中斷程序的情況同樣如此,如在0BH處,本來存放的應當是定時器0中斷程序,但按上述的映象圖,0BH處開始的3個代碼是:02H,10H,30H,這是定時器1的入口地址,所以,如果定時器0發生中斷,所執行的其實是定時器1的中斷程序,這當然不對。例2:INT_0 EQU 1000HTIME_0 EQU 1010HINT_1 EQU 1020HTIME_1 EQU 1030HSERIAL EQU 1040HAJMP START ;跳轉到主程序起始點ORG 0003HLJMP INT_0 ;外中斷0處理程序ORG 000BHLJMP TIME_0 ;定時中斷0處理程序ORG 0013HLJMP INT_1 ;外中斷1處理程序ORG 001BHLJMP TIME_1 ;定時中斷1處理程序ORG 0023HLJMP SERIAL ;串列口中斷程序START:NOPEND上面的程序經過匯編後列表文件如下:The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 108-26-961000 = INT_0 EQU 1000H1010 = TIME_0 EQU 1010H1020 = INT_1 EQU 1020H1030 = TIME_1 EQU 1030H1040 = SERIAL EQU 1040H0000 0126 AJMP START ;跳轉到主程序起始點0003 ORG 0003H0003 021000 LJMP INT_0 ;外中斷0處理程序000B ORG 000BH000B 021010 LJMP TIME_0 ;定時中斷0處理程序0013 ORG 0013H0013 021020 LJMP INT_1 ;外中斷1處理程序001B ORG 001BH001B 021030 LJMP TIME_1 ;定時中斷1處理程序0023 ORG 0023H0023 021040 LJMP SERIAL ;串列口中斷程序START:0026 00 NOP0000 ENDThe Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 208-26-96;%T Symbol Name Type ValueINT_0 . . . . . . . . . . . . . I 1000INT_1 . . . . . . . . . . . . . I 1020SERIAL. . . . . . . . . . . . . I 1040START . . . . . . . . . . . . . L 0026TIME_0. . . . . . . . . . . . . I 1010TIME_1. . . . . . . . . . . . . I 1030;%Z00 Errors (0000)由列表文件,可以繪出代碼在ROM中的映象圖如下:代碼
01H
11H

02H
10H
00H






地址
00H
01H
02H
03H
04H
05H
06H
07H
08H
09H
0AH

代碼
02H
10H
10H





02H
01H
20H

地址
0BH
0CH
0DH
0EH
0FH
10H
11H
12H
13H
14H
15H

代碼





02H
10H
30H




地址
16H
17H
18H
19H
1AH
1BH
1CH
1DH
1EH
1FH
20H

代碼


02H
10H
40H
00H






地址
21H
22H
23H
24H
25H
26H
27H
28H
29H
2AH
2BH
由映象圖可知,各中斷程序的代碼都在其規定地址處,一旦產生中斷即可執行相應的程序。至於圖中未填的部分(如02H),根據各編程器不同而不同,一般為FFH或00H。 二、END END語句標志源代碼的結束,匯編程序遇到END語句即停止運行。若沒有END語句,匯編將報錯。END語句有一個參數,可以是數值0,也可以是表達式,其格式是:標號: END 表達式它的值就是程序的地址並且作為一個特殊的記錄寫入HEX文件。若這個表達式省略,HEX文件中其值就是0。三、EQU EQU以及其它一些符號定義偽指令用來給程序中出現的一些符號賦值。對這些符號名的要求與其它符號相同,即長度不限,大小寫字母可互換並且必須以字母開頭。由等值指令定義的符號是匯編符號表的一部分。等值偽指令有兩種形式。一種用EQU,另一種用字元「=」即符號名 EQU 表達式符號名 = 表達式兩種形式的效果是一樣的。符號名在左邊,其對應的值在右邊。值可以是變元,其它的符號名或表達式。只要在兩遍掃描中求出表達式的值就行,否則引用該符號名時將報錯。當表達式的值是字元串時,只取後兩個字元。若串長為1,高位位元組被置0,符號名的值被列印在程序清單中。由等值偽指令定義的符號名不允許重名。如果經定義的符號名被重定義,則匯編將報出錯,並且這個符號名按新定義的處理,最好不要在程序中出現重名。例:0469= ABC EQU 469H0464= XY EQU ABC-502F0= JK = 7520754 XYJK = XY+JK在列表文件中最左邊的數字不是這些偽指令所在的地址而是通過匯編後賦給符號名的值。第一條符號名ABC被起來469H,第二條XY被賦於ABC-5,因此XY的值為469H-5=464H,JK的值為752(即2F0H),XYJK的值XY+JK=464H+2F0H=754H四、SET SET偽指令有些類似於等值偽指令,它定義了一個整數類型的符號名,它的格式為符號名 SET 表達式SET偽指令與等值偽指令的唯一區別在於SET偽指令所定義的符號名右以在程序中多次定義,而不報錯。例:002D= K57 SET 101101B8707= K57 SET 34567五、DATA與BYTE DATA與BYTE都是用來定義位元組類型的存儲單元,賦予位元組類型的存儲單元一個符號名,以便在程序中通過符號名來訪問這個存儲單元,以幫助對程序的理解。BYTE與DATE之間的區別類似於EQU和SET,BYTE偽指令不能定義重名。六、WORD WORD偽指令類似於DATE偽指令,只是WORD偽指令定義了一個字類型的符號名,其格式為:符號名 WORD 表達式0027= VAL31 WORD 390021= PAR7 WORD 21H一個字由2個位元組組成。當然,因為8051匯編語言集沒有字操作,所以程序執行時,只處理位元組。WROD偽指令僅僅允許用戶定義一個認為是字的存儲位置。七、BIT BIT偽指令定義了一個字位類型的符號名,其格式為:符號名 BIT 表達式這里表達式的值是一個位地址,這個偽指令有助於位的地址符號化。例:002F= LOG3 BIT 470014= Y731 BIT 14H八、ALTNAME 替換名(ALTNAME)偽指令提供用戶一種手段,以定義一個符號名來替換一個保留字,此後這個答名與被替換的保留字均可等效地用於程序中。任何保留類型的答名均可被替換。替換名偽指令格式為:ALTNAME 保留字,新名例:0002= ALTNAME R2 COUNT013A EA MOV A,R2013B E502 MOV A,COUNT九、DB DB偽指令用於定義一個連續的存儲區,給該存儲區的存儲單元賦值。該偽指令的參數即為存儲單元的值,在表達式中對變元個數沒有限制,只要此條偽指令能容納在源程序的一行內,其格式為:標號: DB 表達式只要表達式不是字元串,每一表達式值都被賦給一個位元組。計算表達式值時按16位處理,但其結果只取低8位,若多個表達式出現在一個DB偽指令中,它們必須以逗號分開。表達式中有字元串時,以單引號「'」作分隔符,每個字元佔一個位元組,字元串不加改變地被存在各位元組中,並不將小寫字母轉換成大寫字母。例如:DB 00H 01H 03H 46HDB 'This is a demo!'十、DW DW為以位元組為單元(十六位二進制)來給一個的存儲區賦值,其格式為:標號: DW 表達式例如:0000 3035 D46B DW 12341,54379,10110100101110B0004 2D2E0006 4344 4243 DW 'ABCD','BC','A'000A 0041000C 2868 02E8 DW 456*375h,83+295h,'YZ',72h-4560010 595A FEAA十一、DS DS為定義存儲內容的偽指令,用它定義一個存儲區,並用指定的參數填滿該存儲區。DS偽指令包含兩個變元,第一個變元定義了存儲區的長度的位元組數,在匯編時,匯編程序將跳過這些單元把其它指令匯編在這些位元組之後,因此在使用DS偽指令時第一個變元不可活力第二個變元表示在這些單元中真入什麼值,第二個變元可以活力活力時這些位元組將不處理。下例中0173處有一條DS 9,則空出9個位元組,下一第指令被匯編到017C處;在017C處空出1BH個單元,在這些位元組中被27H所填充。DS指令的格式如下:標號: DS 表達式1,表達式2表達式1定義了存儲區的長度(以位元組為單位)。這個變元不能省略。表達式2是可選擇的,它的值低8位用以填入所定義的存儲區。若省略則這部分存儲單元不處理。例:0000 04 INC A0001 DS 9000A 04 INC A000B DS 1BH,27H0026 04 INC A十二、INCLUDE INCLUDE偽指令用於鏈接源文件,即將一個源文件插入到另一個源文件中。它有一個參數,指出將要插入的文件名,該文件名中可包括驅動器名和路徑名。若文件沒有擴展名,則默認為是ASM。但待插入的文件必須是可以打開的。若文件打開操作失敗,則產生致命錯誤,匯編將停止運行。反之,匯編程序將文件內容讀入並按源代碼處理。當遇到文件結速符時,匯編程序返回到INCLUDE偽指令處繼續身下處理源程序。被插入的文件在程序清單中以「I」開頭。本宏匯編版本支持級嵌套,可在程序中用INCLUDE偽指令插入任意多個文件,但是,在一般情況下DOS允許打開的文件數量是有限的,如果用戶需要打開較多的文件,則必須在CONFIG.SYS文件中加入FILES=40或更多的值,若超過8級嵌套或打開的文件太多,則產生致命錯誤,匯編中止運行。INCLUDE偽指令提供了模塊化程序設計手段,在匯編程序處理主程序時,模塊被插入,盡管這不等價於鏈接和裝配可重定位的目標模塊,但它具有類似的功能,被插入的源文件中不應該包含END偽指令,否則,匯編就會提前停止運行,END偽指令只能出現在主程序中。此外,在主程序進行匯編前所有附加的源文件必須通過匯編,產生相應的HEX及LST文件,由於附加的文件沒有END偽指令,因此,附加文件匯編時,匯編程序將顯示:「沒有結束語句」的錯誤,但並不影響與主程序的鏈接。下面是一個使用INCLUDE偽指令的例子,其主程序的源文件MAIN.ASM為:;MAIN.ASMORG 27HSTART:CLR AMOV R3,AINCLUDE MOD1INC R5INCLUDE MOD2.ASMDEC R3END START主程序為帶有END偽指令的完整的源文件。程序中有兩INCLUDE偽指令,分別將兩附加的文件MOD1.ASM及MOD2.ASM鏈接到主程序中。以下是這兩個文件。;MOD1.ASMMOV R2,#31HMOV R5,#18H;MOD2.ASMMOV R6,#47HANL A,#07HMOV R1,A注意MOD1.ASM及MOD2.ASM均沒有END指令。在進行匯編時必須先對MOD1.ASM和MOD2.ASM進行匯編,然後在匯編MAIN.ASM,由於上兩個文件沒有END偽指令,所以在匯編時會出現錯誤提示,不用管它,繼續下面的工作,就可以得到正確的結果。以下是形成的列表文件:The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1;MOD2.ASMMOV R6,#47HANL A,#07HMOV R1,A08-27-96;MAIN.ASM0027 ORG 27HSTART:0027 E4 CLR A0028 FB MOV R3,AI INCLUDE MOD1I ;MOD1.ASMI0029 7A31 MOV R2,#31HI002B 7D18 MOV R5,#18HI002D 0D INC R5I INCLUDE MOD2.ASMI ;MOD2.ASMI002E 7E47 MOV R6,#47HI0030 5407 ANL A,#07HI0032 F9 MOV R1,A0033 1B DEC R30027 END STARTThe Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 208-27-96;%T Symbol Name Type ValueSTART . . . . . . . . . . . . . L 0027;%Z00 Errors (0000)十三、TITL TITLE偽指令用於在列表文件頁頭建立一個標題,其格式為:$TITLE 標題行這里標量行就是將出現在頁頭的標量與通常的字元串定義不同。這里標量行不加引號。匯編從$TITLE之後的第一個可列印字元開始,到回車符之間的字元串作為標量標量的最大長度是60個字元,基標量行省略,則標題行為空行。若TITLE偽指令在一頁,它說明的標量行包含在本頁,否則,標題將出現在下頁頁頭。十四、PAGE PAGE偽指令用於形成新的一中定義一面的行數。其格式為:$PAGE 表達式若表達 式預設則開始新的一頁,若有表達式,則每頁行數重新定義。匯編開始時頁長為66行。一頁中除出頁外,剩餘55行用於列印源程序,這一格式適用於標准列印紙。如果變元值小於66,頁內可列印的源代碼行將相應減少。頁長最小值為12。若小於12時,每頁內除頁上只列印一行源程序。頁長變元是16位位元組,因而每頁最長可定義到65535行,這時分頁列印變為連續列印,在屏幕顯示程序清單或在捲筒紙上列印程序清單時,常常使用連續列印,如果在啟動匯編時用/N選項,頁長就是65535。十五、LIST與NOLIST 它們的格式為:$LIST$NOLISTLIST偽指令使匯編時主生程序清單,但即使不用該指令,匯編也會自動產生清單。但如果使用了NOLIST偽指令後需要繼續主生清單則必須使用LIST偽指令。NOLIST偽指令使匯編時不產生清單,所有包含此偽指令及在這條偽指令之後的語句都不進入列表文件。當不需要任何列表文件,並且不需要顯示程序清單時,可以在啟動匯編時不加.L附加項,且在源代碼的第一行加上NOLIST指令。使用NOLIST偽指令與附加項/L不同之處是NOLIST偽指令可加在源程序中,與LIST偽指令配合使用,使源程序中某些部分不產生清單。而不加附加項/L則不產生任何程序清單。不過,不管有無$NOLIST偽指令,程序在匯編時檢查到的錯誤都將在屏幕上顯示出錯的源代碼行及錯誤信息十六、NOCODE 其格式為 $NOCODENOCODE偽指令使得在匯編時,條件匯編程序結構中那些真值為假的條件不產生清單。有關條件匯編結構在下面介紹。如果沒有這條偽指令,匯編將主生所有條件下的清單,不論其真值是否為真。但是假的條件,不產生目標碼。而NOCODE偽指令使匯編清單中只列出那些由匯編程序用到的部分,因此,當使用NOCODE偽指令時,程序清單與源程序並非逐行對應。 本文來自CSDN博客,轉載請標明出處: http://blog.csdn.net/sunnf/archive/2008/10/23/3129370.aspx

❹ 實現兩個數相減的匯編語言程序

;80x86匯編程序例子:
CLS ;清進桐梁位(借位)標志
MOV BL,0AH ;被減數送BL
MOV CL,05H ;減數銀局送鋒輪讓CL
SUB BL,CL ;兩數相減

❺ 匯編語言程序

SRT:
MOV A,R7
MOV R5,A
SRT1:
CLR F0
MOV A,R5
DEC A
MOV R5,A
MOV R2,A
JZ SRT5
MOV A,R0
MOV R6,A
SRT2:
MOV A,@R0
MOV R3,A
INC R0
MOV A,@R0
MOV R4,A
CLR C
SUBB A,R3
JNC SRT4
SETB F0
MOV A,R3
MOV @R0,A
DEC R0
MOV A,R4
MOV @R0,A
INC R0
SRT4:
DJNZ R2,SRT2
MOV A,R6
MOV R0,A
JB F0,SRT1
SRT5:
RET
END
比如上面的這個例子,採用某種計逗或神算機山虧的匯編指令寫出來的實現某個功能的程序團陵。
打字不易,如滿意,望採納。

❻ 簡單匯編語言實例

(1)判斷雙字的高位字是不是1,如培銷果不是,直接返回
(2)清除進位標志
(3)將低字求反,弊岩然後 +1
(4)將進位保存起來
(5)將高位字求反,加上剛配卜游才的進位
--------------
XXXX:
MOV AX, X + 1 ;取 X 的高位字
CMP AX, 32768
JB X_EXIT ;是正數就跳轉

MOV AX, X ;取 X 的低位字
XOR AX, 0FFFFH ;取反
ADD AX, 1
PUSHF
MOV X, AX

MOV AX, X + 1 ;取X 的高位字
XOR AX, 0FFFFH ;取反
POPF
ADC AX, 0
MOV X + 1, AX

X_EXIT:
RET

閱讀全文

與匯編程序例子相關的資料

熱點內容
linux粘滯位 瀏覽:137
安卓如何把備忘錄調成黑色 瀏覽:862
dhcp伺服器手動分配ip地址 瀏覽:308
阿里雲國內伺服器數量 瀏覽:455
壓縮機安全裕度 瀏覽:226
android交叉編譯環境 瀏覽:775
美團雲伺服器質量怎麼樣 瀏覽:396
蘋果手機游戲解壓包怎麼安裝 瀏覽:446
java程序員面試流程 瀏覽:681
遼寧圖片加密軟體地址 瀏覽:932
程序員35後應該學些啥技術 瀏覽:724
蘋果怎麼把app還原成ipa包 瀏覽:358
天正怎麼分解加密圖紙 瀏覽:829
你喜歡的大胸部電影 瀏覽:755
飛盧破解版網址 瀏覽:632
怎麼在米家app裡面找到小愛同學 瀏覽:208
網盤的小說在哪個文件夾 瀏覽:59
阿里程序員約炮 瀏覽:939
java語言程序設計題 瀏覽:464
法和經濟學pdf 瀏覽:703