1. 程式人生 > >linux驅動編寫過程中遇到的幾個問題及解決辦法

linux驅動編寫過程中遇到的幾個問題及解決辦法

1)抓取qq號一直不成功,直接上程式剖析問題

payload的資料型別是char指標,也就是每個元素都是char型別的,qq號的字元為(0-9),並且第一個字元不能為0. ‘0’-‘9’對應的ASCII碼的16進製為30-39

而且得知qq號的儲存方式為16進位制,故這樣比較:if(payload[15+qqlen]>=0x30&&payload[15+qqlen]<=0x39)

但是剛開始並不知道數字前面加0x就可以用16進位制的形式比較,所以就用10進位制的形式比較,

首先我定義變數

 unsigned int tmp = 0; 

tmp = payload[i] - '0';

if(tmp >=30&&tmp <=39)

可是這樣就失敗了,抓不到qq號。

後來改成0x30的形式就可以抓到qq號了。

if(iphdr->protocol==6)
 {     
       //printk("iphdr->saddr = %8x");
       //tcp 包長度 ntohs(iph->tot_len) - iph->ihl*4 - tcph->doff*4
       tcphdr = (void *)iphdr + iphdr->ihl*4;
       unsigned int tcp_len = ntohs(iphdr->tot_len) - 40;
      // payload = (void*)skb->data + 40;
      // skb->data ethdr+iphdr+
      // payload = (void*)skb->data + 40 + 14;
       //payload = tcphdr + 20;
       payload = (char *)tcphdr + (char)sizeof(struct tcphdr);
       unsigned int flag = 0;
       unsigned int qqlen = 0;
       unsigned int tcphdr_len;      
      if(tcp_len>=25){
       if(payload[14]>0x30&&payload[14]<=0x39){  
 for(qqlen=0;qqlen<9;qqlen++){
    if(payload[15+qqlen]>=0x30&&payload[15+qqlen]<=0x39){
flag = 1;
    }                 
    else{
               flag = 0;
               break;
              }   
 }
}
int tmp;
if(flag==1){
           printk("qq: \n");
           for(qqlen=0;qqlen<10;qqlen++){
                tmp = payload[14+qqlen]-'0';
printk("%d\n",tmp);
  }
  send_by_skb(payload,tcp_len);
  printk("find a datapacket of qq\n");
}
      }

(2)使用者空間地址成功傳到核心空間,但是在hook的回撥函式裡面不能對該地址指向的使用者空間的記憶體進行賦值

使用者空間的地址傳到核心空間成功:

 copy_to_user(data, test_buf, 20);   

  memcpy(data,test_buf,20);

這兩句在my_ioctl回撥函式裡面分別可以成功,但是在my_hook回撥函式裡面卻不能,而且會導致虛擬機器崩潰。有可能是hook裡面的程式碼才是核心級別的程式碼,memcpy和copytouser許可權不夠

全域性變數:unsigned char *data = NULL;

unsigned char *test_buf = "test if ti is ok for user space to translate address to kernel";

 unsigned int my_hookfn(unsigned int hooknum,  
    struct sk_buff *skb,  
    const struct net_device *in,  
    const struct net_device *out,  
    int (*okfn)(struct sk_buff *))  
  {    }

my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){

case MEMDEV_IOCGETSET:   
        data = (unsigned char *)arg;
        copy_to_user(data, test_buf, 20);

        memcpy(data,test_buf,20);
        printk("data address is: %08x\n",data);
        break; 

}

(3)程式除錯心得

我在驅動程式裡面寫了一個全域性變數:unsigned char *data;  然後在初始化函式裡面為其動態申請一塊4KB的記憶體,並對其初始化為全0;在hook回撥函式裡面把鉤到的資料包賦值給data。之後執行使用者程式,將data的內容copy給使用者空間的記憶體。

但是在拷貝過程中出現了以下錯誤:

1.使用者程式手動執行一次不會出錯,如果寫成死迴圈就會崩潰

        2.首先找使用者程式崩潰的原因,發現從核心第一次讀資料不會出錯,第二次就會出錯,且日誌顯示出錯在資料包長度那裡。後來在老師的建議下把資料包長度寫死,結果程式不崩潰。故第一個出錯的地方是資料包的長度。接著去找跟長度相關的語句,發現在copytouser時,長度只copy了1024個位元組,但是有的資料包的長度超過了1024,就會導致使用者程式指標越界(例如:明明說有1500個位元組,我卻讀不到),那麼使用者程式肯定會崩潰

      3. 長度問題解決後,發現使用者程式除了讀的第一個資料包的內容不為0,後面讀到的資料包都是0. 然後在驅動程式的hook裡面,把鉤到的資料包本來要copy的第一個欄位寫死,看之後使用者讀到的是不是0.但是發現還是為0.此時可以斷定,驅動寫資料沒問題,所以肯定是使用者讀資料有問題。那麼去檢查使用者程式,發現定義的start全域性變數在while死迴圈外邊初始化為0,故每次進迴圈的時候start的值都不從0開始。

所以在這得到的教訓是:在哪裡用到變數在哪裡 賦值

4.得到的另一個教訓是:在核心寫資料是一個欄位一個欄位的寫,在使用者空間是一個欄位一個欄位的讀,所以為了確保每個欄位的讀寫正確,要把每個欄位寫成死的,然後迴圈多次看是否讀寫一致。

執行一次成功,並不代表執行多次成功。一次成功的時候bug可能沒觸發,多次執行就會觸發