色婷婷AⅤ一区二区三区|亚洲精品第一国产综合亚AV|久久精品官方网视频|日本28视频香蕉

          新聞中心

          EEPW首頁 > c51軟復(fù)位經(jīng)典分析

          c51軟復(fù)位經(jīng)典分析

          ——
          作者: 時(shí)間:2007-04-05 來源: 收藏

          從單片機(jī)指針說到黑客程序

           

          在電子BBS討論區(qū)上溜達(dá),看到一個(gè)有趣的帖子,整個(gè)帖子內(nèi)容如下:

          純C51復(fù)位功能函數(shù):一個(gè)大三學(xué)生,讓人又愛又怕

          現(xiàn)單列復(fù)位部分如下:

          main()

          {

            &nbspunsigned&nbspchar&nbspcode&nbsprst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32};  // 復(fù)位代碼

             (*((void (*)())(rst)))();  // 執(zhí)行上一行代碼,將rst數(shù)組當(dāng)函數(shù)調(diào)用

          }

          本來我告訴他嵌入如下代碼:

          clr&nbspa

          push&nbspacc

          push&nbspacc

          reti

          結(jié)果他卻玩了前面哪一段,而數(shù)組rst[]中的內(nèi)容恰恰是上面的匯編機(jī)器碼,他的做法是將rst數(shù)組的數(shù)據(jù)當(dāng)作代碼保存,然后采用絕對(duì)地址方式指向該數(shù)組,將該數(shù)組中的代碼當(dāng)作函數(shù)來運(yùn)行。居然通過了!

          我覺得有問題,我說即使如此,那絕對(duì)地址調(diào)用也應(yīng)該寫成(*((void (*)())(&rst)))() 才對(duì)呀,結(jié)果他反駁說,那樣的話,rst的地址就會(huì)當(dāng)成參數(shù)傳遞給這個(gè)絕對(duì)地址函數(shù),而實(shí)際LJMP調(diào)用的地址并非rst的地址,而是一個(gè)不確定的地址。于是我按照自己的說法嘗試了一下,看看匯編結(jié)果,還真的是將rst的地址傳遞給了R1&nbspR2,而絕對(duì)函數(shù)最終LJMP到了
          一個(gè)莫名其妙的地址上去了,死翹!

          看來C真是一匹不容易駕馭的野馬,這個(gè)大三學(xué)生理解力在我之上,我30多歲的人了,干了這么多年還沒他的境界呢,唉,人家才學(xué)了幾天啊,翻了幾天書就這么厲害了,服了!

           

          l         首先分析帖子的C語言代碼

          第一句定義一個(gè)數(shù)組rst[],數(shù)組內(nèi)數(shù)據(jù)就是完成復(fù)位功能的匯編機(jī)器碼,具體對(duì)應(yīng)關(guān)系
          為:clr&nbspa ==&nbsp0xe4、push&nbspacc ==&nbsp0xc0,0xe0、reti ==0x32

          第二句是一個(gè)函數(shù)指針的用法,函數(shù)指針用法稍微有點(diǎn)復(fù)雜,可參看本人著的書,:),以
          下為快速入門講解。

          定義一個(gè)返回值是空函數(shù)指針的定義形式如下:

          void (*p) ( )

          當(dāng)把函數(shù)指針賦值后,就能通過函數(shù)指針調(diào)用函數(shù),調(diào)用形式如下,

                (*p) ( );

          或等價(jià)的簡化形式:

          p ( );

          假設(shè)rst就是函數(shù)指針,則如下調(diào)用形式就可以令單片機(jī)復(fù)位再起。

          (*rst ) ( ); 

          但可惜,rst不是函數(shù)指針,而是數(shù)組名,雖然兩者都是地址,但不可直接調(diào)用數(shù)組名。

          如同把char型變量a賦值給int型變量b,(int) 表示強(qiáng)制類型轉(zhuǎn)換:

          b = (int)&nbspa

          函數(shù)指針的強(qiáng)制類型轉(zhuǎn)換公式如下(C語言的哲學(xué)是定義形式和使用一致):

          (  (void (*)()  )&nbsprst

          這樣經(jīng)過轉(zhuǎn)換后的rst就可以當(dāng)作函數(shù)指針使用了,簡單的調(diào)用形式如下:

          #define &nbspK     (  (void (*)( )  )&nbsprst

          (*K) ( )

          或:

          (     * ( &nbspvoid (*)( )  )rst      ) ( );

          這樣的語句就完成復(fù)位再啟功能了。類型轉(zhuǎn)換符()的優(yōu)先級(jí)跟指針運(yùn)算符*的優(yōu)先級(jí)相同,
          二者的結(jié)合方向是自右至左,所以上述語句就能完成復(fù)位功能了。保險(xiǎn)起見有些程序員常
          常喜歡再加個(gè)括號(hào):

          #define &nbspK     (   (  (void (*)( )  )&nbsprst   )

          (*K) ( )

          (     *(   ( &nbspvoid (*)( )  )rst   )    ) ( );

           

          由于沒有輸入?yún)?shù),上述復(fù)位代碼更嚴(yán)謹(jǐn)?shù)膶懛ㄊ牵?

          #define &nbspK     (   (  (void (*)(void )  )&nbsprst   )

          (*K) ( )

          (     *(   ( &nbspvoid (*)(void )  )rst   )    ) ( );

           

          關(guān)于帖子作者的解釋

          千萬不要犯“&rst”形式的錯(cuò)誤,對(duì)于一維數(shù)組而言,數(shù)組名rst就代表地址。以下二者等
          價(jià),更常用的是等式左邊的形式:

          rst == &rst[0]

          整個(gè)函數(shù)指針無所謂參數(shù)傳遞,只是把rst當(dāng)作程序執(zhí)行地址調(diào)用而已,那個(gè)學(xué)生的解釋也
          有問題。

          還有一點(diǎn)必須提及,不是說能通過編譯,甚至生成正確代碼,就表示某語句一定是對(duì)的。
          對(duì)很復(fù)雜的語句,要考慮到編譯器不嚴(yán)格甚至出錯(cuò)的可能性。

           

          哈佛結(jié)構(gòu)和一個(gè)蠕蟲病毒

          請(qǐng)注意,定義數(shù)組rst[]時(shí)用了關(guān)鍵字code,這是C51特有的關(guān)鍵字,意味著把數(shù)組定義到程序空間。標(biāo)準(zhǔn)C是沒有關(guān)鍵字code的。

          哈佛結(jié)構(gòu)和普林斯頓結(jié)構(gòu):

          哈佛結(jié)構(gòu)——程序空間和存儲(chǔ)空間分開的。C51算是不太嚴(yán)格的哈佛結(jié)構(gòu)——雖地址線分開,但數(shù)據(jù)線沒有分開。DSP是增強(qiáng)的哈佛結(jié)構(gòu)。

          PC電腦上奔騰CPU是普林斯頓結(jié)構(gòu)——數(shù)據(jù)空間和程序空間統(tǒng)一編址。

           

          如果數(shù)組rst[]數(shù)據(jù)的匯編機(jī)器碼是刪除文件的機(jī)器碼,這算不算是病毒?

          曾經(jīng)流行過一種蠕蟲病毒,其發(fā)作機(jī)理采取的就是將惡意代碼保存成文本文件,然后通過指針調(diào)用執(zhí)行這個(gè)文本,很多殺毒程序也不會(huì)查詢文本文件。

          程序也罷,數(shù)據(jù)也罷都是二進(jìn)制形式,如果數(shù)據(jù)空間和程序空間是統(tǒng)一編碼的, 數(shù)據(jù)當(dāng)然可以當(dāng)作程序運(yùn)行。

          在這一點(diǎn)上,相對(duì)而言,哈佛結(jié)構(gòu)的CPU安全性會(huì)好一點(diǎn)點(diǎn)。但嵌入式應(yīng)用少有病毒,一般不用關(guān)心。

           

          單片機(jī)復(fù)位的更好方法

          帖子中匯編語言解釋如下:

          clr&nbspa                      //清除ACC=0

          push&nbspacc               //壓0到堆?!?位

          push&nbspacc               //再壓0到堆棧——再8位

          reti                        //返回到0地址,從而執(zhí)行。

          帖子作者的這種復(fù)位方法比較麻煩,更加簡單的復(fù)位寫法是(摘自《C缺陷與陷阱》):

          (     * ( &nbspvoid (*)( )  )0      ) ( );

          本句的分析方法同上,但更加精煉,沒有多余的匯編語句。

           

          上述復(fù)位的方法可稱為軟件復(fù)位。

          軟件復(fù)位跟真正上電復(fù)位有很大差別:上電復(fù)位時(shí)大部分寄存器都有確定的復(fù)位值;軟件復(fù)位則只相當(dāng)于從0地址開始執(zhí)行而已,寄存器不會(huì)變?yōu)榇_定的復(fù)位值。

          如果用戶要編程實(shí)現(xiàn)上電復(fù)位這種情況,在程序中不要踢看門狗即可。大部分單片機(jī)都有看門狗吧。

           


          ----------------2---------------


          匯編中的ORG&nbsp0X0000H 在C51中如何實(shí)現(xiàn).
          一般是在連接定位模塊中來進(jìn)行地址分配
          1、選擇"options&nbspfo&nbsptarget   "
          2、選擇“BL51&nbspLocate”
          3、在code 欄填入   ?PR?MAIN?SS(0x800)

          其中MAIN是你要定位的函數(shù)名,SS是函數(shù)所在的文件名,要是有多個(gè)函數(shù)需要定位,則在中間加逗號(hào)。

           

          功能強(qiáng)大的時(shí)鐘中斷
            在單片機(jī)程序設(shè)計(jì)中,設(shè)置一個(gè)好的時(shí)鐘中斷,將能使一個(gè)CPU發(fā)揮兩個(gè)CPU的功效,大大方便和簡化程序的編制,提高系統(tǒng)的效率與可操作性。我們可以把一些例行的及需要定時(shí)執(zhí)行的程序放在時(shí)鐘中斷中,還可以利用時(shí)鐘中斷協(xié)助主程序完成定時(shí)、延時(shí)等操作。


            下面以6MHz時(shí)鐘的AT89C51系統(tǒng)為例,說明時(shí)鐘中斷的應(yīng)用。
            定時(shí)器初值與中斷周期 時(shí)鐘中斷無需過于頻繁,一般取20mS(50Hz)即可。如需要百分之一秒的時(shí)基信號(hào),可取10mS(100Hz)。這里取20mS,用定時(shí)器T0工作于16位定時(shí)器方式(方式1)。


              T0的工作方式為:每過一個(gè)機(jī)器周期自動(dòng)加1,當(dāng)計(jì)滿0FFFFh,要溢出時(shí),便會(huì)產(chǎn)生中斷,并由硬件設(shè)置相應(yīng)的標(biāo)志位供軟件查詢。即中斷時(shí)比啟動(dòng)時(shí)經(jīng)過了N+1個(gè)機(jī)器周期。所以,我們只要在T0中預(yù)先存入一個(gè)比滿值0FFFFh小N的數(shù),然后啟動(dòng)定時(shí)器,便會(huì)在N個(gè)機(jī)器周期后產(chǎn)生中斷。


              這個(gè)值便是所謂的“初值”。下面計(jì)算我們需要的初值:時(shí)鐘為6MHz,12個(gè)時(shí)鐘周期為一個(gè)機(jī)器周期,20mS中有10000個(gè)機(jī)器周期。(10000)10=(2710)16,0FFFFh-2710h+1=0D8F0h。由于響應(yīng)中斷、保護(hù)現(xiàn)場及重裝初值還需要7~8個(gè)機(jī)器周期,把這個(gè)值再加上7,即T0應(yīng)裝入的初值是0D8F7h。每次中斷進(jìn)入后,先把A及PSW的值壓入堆棧,然后即把0D8F7h裝入T0。


            設(shè)置一個(gè)單元,每次中斷加1 我們可以取內(nèi)部RAM中一個(gè)單元,取名為INCPI(Increase Per&nbspInterrupt),在中斷中,裝完T0初值后,用INC INCPI指令將其加一。從這個(gè)單元中,無論中斷程序還是主程序,都可以從中獲得20mS的1~256之間任意整數(shù)倍的信號(hào)。例如:有一段向
          數(shù)碼管送顯的程序,需要每0.5秒執(zhí)行一次以便刷新顯示器,便可以設(shè)一單元(稱為等待單元)W_DISP,用/MOV&nbspA,INCPI/ADD&nbspA,#25/MOV&nbspW_DISP,A/語句讓其比當(dāng)前的INCPI值大25,然后在每次中斷中檢查是否于INCPI值相等。若相等,說明已過了25個(gè)中斷周期,便執(zhí)行送顯程序,并且讓W(xué)_DISP再加上25,等待下個(gè)0.5秒。我們可以設(shè)置多個(gè)等待單元,以便取出多個(gè)不同的時(shí)基
          信號(hào)。讓中斷程序在每次中斷時(shí)依次查詢各個(gè)等待單元是否與INCPI相等,若相等,則執(zhí)行相應(yīng)的處理,并重新設(shè)置該等待單元的值,否則跳過。例如:用0.5秒信號(hào)刷新或閃爍顯示器,用1秒信號(hào)產(chǎn)生實(shí)時(shí)時(shí)鐘,或輸出一定頻率的方波,以一定間隔查詢輸入設(shè)備等。


            在中斷中讀鍵 通常,我們在主程序中讀鍵盤,步驟為:掃描鍵盤,若有鍵按下,則延時(shí)幾十毫秒去抖動(dòng),再次確認(rèn)此鍵確實(shí)按下,然后處理該鍵對(duì)應(yīng)的工作,完成后再次重上述步驟。但這有兩點(diǎn)不足:1.處理相應(yīng)工作時(shí)無法鎖存按鍵的輸入,即可能漏鍵。2.延時(shí)去抖時(shí)CPU無法做其它事情,效率不高。如果把讀鍵放入時(shí)鐘中斷中,則可避免上述不足。方法為:如果兩次相鄰的中斷中都讀到同一個(gè)鍵按下,則這個(gè)鍵是有效的(達(dá)到了去抖目的),并將其鎖存到先入先出(隊(duì)列)的鍵盤緩沖區(qū),等主程序來處理。這樣,主程序處理按鍵的同時(shí),仍可響應(yīng)鍵盤的輸入。


              緩沖區(qū)深度通??稍O(shè)為8級(jí),若鎖存的鍵數(shù)多于8個(gè),則忽略新的按鍵,并報(bào)警提示用戶新的按鍵將無效。若鍵盤緩沖隊(duì)列停滯的時(shí)間大大長于主程序處理按鍵所需要的最大時(shí)間,說明主程序已
          出錯(cuò)或跑飛,可以在中斷用指令將系統(tǒng)復(fù)位,起到了看門狗的目的。  


              主程序中的延時(shí) 由于有常開的時(shí)鐘中斷,所以當(dāng)主程序中有需要時(shí)間較短、精度較高的延時(shí)時(shí),應(yīng)暫時(shí)把時(shí)鐘中斷關(guān)閉。而程序中需要時(shí)間較長、精度不高的延時(shí)時(shí),便可仿照下需的寫法,避免多層嵌套的循環(huán)延時(shí)。


            例:在P1.1輸出1秒的高電平脈沖
             &nbspMOV   &nbspA,INCPI
             &nbspINC   &nbspA
             &nbspCJNE   &nbspA,INCPI$    ;等待一次中斷處理完成
             &nbspSETB   &nbspP1.1        ;設(shè)P1.1為H,脈沖開始
             &nbspADD   &nbspA,#50        ;50個(gè)20mS為1秒
             &nbspCJNE   &nbspA,INCPI,$    ;等中斷將INCPI加一50次
             &nbspCLR   &nbspP1.1        ;設(shè)P1.1為L,脈沖結(jié)束


            結(jié)束語:從上看出,要靈活地應(yīng)用時(shí)鐘中斷,將任務(wù)合理分配給中斷和主程序,并且二者要分工明確,接口簡單。這其中的技巧還需要大家在實(shí)踐中多多摸索與體會(huì)。另外要注意:應(yīng)盡量
          縮短中斷處理程序的執(zhí)行時(shí)間,更不要長于20mS。

           

          c語言相關(guān)文章:c語言教程




          關(guān)鍵詞: c51

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉