Linux編程之給你的程式開後門

来源:http://www.cnblogs.com/skyfsm/archive/2017/01/14/6284964.html
-Advertisement-
Play Games

這裡說的“後門”並不是教你做壞事,而是讓你做好事,搭建自己的調試工具更好地進行調試開發。我們都知道,當程式發生異常錯誤時,我們需要定位到錯誤,有時我們還想,我們在不修改程式的前提下,就能通過log來定位錯誤呢?有人會說,我在我的程式裡加多點列印就好了,程式每做一步我就加一行列印,到時一查log就知道 ...


這裡說的“後門”並不是教你做壞事,而是讓你做好事,搭建自己的調試工具更好地進行調試開發。我們都知道,當程式發生異常錯誤時,我們需要定位到錯誤,有時我們還想,我們在不修改程式的前提下,就能通過log來定位錯誤呢?有人會說,我在我的程式裡加多點列印就好了,程式每做一步我就加一行列印,到時一查log就知道程式在哪一步死掉的了。這個方法在小程式里也許會行得通,但是,在一個大型系統,每秒的log達到幾百條,那時我們怎麼能在這繁多的log里找出我們想要的那條的log的?這工作量大得誇張。工程中的解決方法就是給自己的程式開個後門專門給開發人員來調試程式。   我在上篇文章:《Linux編程之定製帶級別的log》里介紹瞭如何自定義自己的帶級別log,我們就可以在我們後門程式里修改log 的level,從而使得log的級別發生改變,而無需重新修改程式。比如我們初始化我們的log級別是FATAL,那麼此時只會列印出FATAL的信息,但是有一次程式發生了錯誤,但是我們無法從FATAL的log里看出問題,那我們通過後臺修改log的級別為ALARM,我們通過ALARM的log查出了程式錯誤的原因。當然我們通過後門能做的不僅僅是這些,具體來說,後門就是我們程式眼和跑著的程式交流的一道門。   搭建這麼一個程式後門主要有這麼幾個關鍵點:
  • 在進程里開一個線程用於充當debug center
  • 該線程通過fifo接收開發人員傳給它的命令
  • 解析這些命令
  • 用腳本搭建簡單的命令行界面

 

一、創建debug center線程 這個就沒什麼好說了,我使用了上篇文章《Linux編程之自定義消息隊列》所搭建的消息隊列框架,將其中的msg_sender1改造為debug_center線程,作為我們的程式後門,我們跟程式交互就是從這裡開始的。
if(pthread_create(&debug_thread_id, NULL, (void*)debug_center, NULL))
{
    MY_LOG(FATAL,"create debug center fail!\n");
    return -1;
}

 

 

二、創建FIFO 為什麼要創建FIFO(有名管道)?因為我們需要跟我們的程式進行通信,我們需要把我們的指令告訴程式,那就需要一個通信途徑,FIFO就是一個很好的選擇。我們把我們的指令寫進管道,程式將指令從管道出,然後執行該指令,這樣子我們程式後門的通信模型就出來了。why解決了,是時候解決how了。   對於管道的操作,我是這麼做的:
system("rm /vob/ljsdpoenew3/exercise/debug_log");   //每次進入debug center我們都將原來的fifo文件刪除,避免影響後面操作
rc = mkfifo("/vob/ljsdpoenew3/exercise/debug_log", 0666);  //創建fifo
if(rc < 0)
{
   MY_LOG(DEBUG, "make fifo fail!\n");
   pthread_exit(0);
}
 
fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r");  //打開fifo,讀取指令
if(fp == NULL)
{
    MY_LOG(DEBUG, "open debug_log fail!\n");
    pthread_exit(0);
}

讀fifo我們解決了,那怎麼將我們的指令寫進fifo呢?這裡我打算使用shell的read指令,文章後面會解釋如何實現。

 

三、解析指令 解析指令又可以分為兩個步驟:
  1. 將從fifo取得數據進行格式解析,比如我定義了d d的意思是display debug,即顯示現在的debug級別,那麼我們程式就得首先對原始數據進行格式處理。
  2. 將指令進行命令解析,執行相應操作。
  格式處理:
static int get_args(FILE *inputFile)
{
    char tmpBuffer[100];
    char *line = tmpBuffer;
    char separator[] = " ,\n\t";
    char *token;
    int  i;
    char eof;
 
    int num = 0;
 
    eof = !fgets(line, sizeof(tmpBuffer), inputFile);
    if (eof)
        return num;
 
    token = strtok(line, separator);
    while (num < MAX_NUM_ARGS && token)
    {
        strcpy(args[num], token);
        num++;
        token = strtok(NULL, separator);
    }
 
    for (i = num; i < MAX_NUM_ARGS; i++)
        args[i][0] = 0;
 
    return num;
}

 

  命令解析:
        switch(args[0][0])  //解析指令,看每個指令對應哪些意思
        {
            case 'd':    //display
                switch(args[1][0])
                {
                    case 'q':   //display queue
                    show_MQ(fd);
                    break;
                    case 'd':   //display debug
                    show_debug_level(fd);
                    break;
 
                    default:
                        help_manual(fd);
                        break;
                }
                break;
 
            case 's':    //set
                switch(args[1][0])
                {
                    case 'd': //set debug level
                    n = atoi(args[2]);  //將字元串轉化為整數
                    fprintf(fd," debug level change from %d to %d",global.debug_level,n);
                    global.debug_level = n;  //更改log級別
                    break;
 
                    default:
                        help_manual(fd);
                        break;
                }
                break;
 
            default:
                help_manual(fd);
                break;
        }
 

 

四、搭建debug界面 先上界面圖:

  我們敲的每個命令都是通過該界面傳遞到程式那頭的,比如“d d”就表示展示出現在系統運行時的log級別,而“s d”就是設置我們想要看的log級別,這樣我們就可以實現通過程式後門動態修改程式走向了。   那該如何實現這個看似簡單的交互界面呢?實現該交互界面,有幾個關鍵點:
  • 我們需將程式的列印輸出重定向到一個文件里
  • 使用shell腳本讀出文件的內容
  • 我們輸入的命令需寫入到fifo中
以上三點就是做成界面的最重要的技術問題。  
  1. 重定向輸出
  一開始我是打算將標準輸出、標準錯誤都重定向到文件里的額,但是想了想,還是想直接把列印內容直接輸出到文件就好了。比如這樣:
     fd = fopen("/vob/ljsdpoenew3/exercise/debug_log2", "w+");
    if(fd == NULL)
    {
        MY_LOG(DEBUG, "open debug_log2 fail!\n");
        pthread_exit(0);
    }

fprintf(fd," debug level change from %d to %d",global.debug_level,n);
    這樣我們在debug center產生的列印都輸出到文件debug_log2上了。  

  2.利用腳本讀出內容且將內容寫入fifo

  要做到這點需要shell編程的簡單知識,我們怎麼將我們的輸入放到fifo呢?我們怎麼把log文件的內容讀出來顯示在顯示屏呢?我想到首先是再寫個client,進行進程間通信嘛,將命令寫到fifo,同時也讀出log文件的內容。但是,我發現利用shell就可以做到以上的事了,而且只需花費幾行代碼。  
#!/bin/bash
 
my_inp_fifo=/vob/ljsdpoenew3/exercise/debug_log    # 指定fifo文件
stty erase ^H    
 
while [ true ];
do
 
        read inp    #迴圈讀入用戶輸入
        if [ "$inp" == "quit" ];then   #quit代表結束該界面
                exit 0
        fi
        echo $inp > $my_inp_fifo   #寫入fifo
        cat debug_log2.txt         #顯示log的內容
done

 

  那看看這個命令行界面跑起來是怎麼的吧!   首先我們運行server進程
  sever進程不斷產生消息並處理消息。   我們打開另一個視窗,並執行腳本./test.sh,進入界面         我們使用了d d命令看到了程式此時的debug級別,用d q看出了程式消息隊列的情況。       我們使用了s d指令將debug level設置為0,此時屏幕沒有任何列印輸出,當我們在使用s d指令將level設置為-1(即將所有位置一),此時所有列印級別都打開了,屏幕又開始瘋狂列印了。也就說,我們通過後門操控了程式,這裡我們只是僅僅修改了程式的log級別,當然我們還可以做更多的事,只要依照這個框架往裡面加指令,以及對應的處理操作,就可以實現了。   五、總結 所謂後門,就是一個可以操控程式的介面,這個介面僅僅用於開發者調試開發,不會開放給客戶。所以這個後門的作用非常巨大,所以是開發者調試程式的一大利器。有人會想,我想用socket來代替fifo進行進程通信可以不,這樣就可以做到遠程主機操控程式了。我覺得是可以的,但是感覺利用telnet到目的主機再運行腳本操作比較安全。       最後給出源代碼框架
  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <math.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <sys/prctl.h>
  8 #include "msg_def.h"
  9 #include "global.h"
 10  
 11 extern Queue_t MsgQueue;
 12 extern dashboard_t global;
 13  
 14  
 15 #define  MAX_NUM_ARGS  20
 16 #define  MAX_ARGS_SIZE  56
 17  
 18 static char args[MAX_NUM_ARGS][MAX_ARGS_SIZE];
 19  
 20 static int get_args(FILE *inputFile,FILE *fd)
 21 {
 22     char tmpBuffer[100];
 23     char tmpBuffer2[100];
 24     char *line = tmpBuffer;
 25     char separator[] = " ,\n\t";
 26     char *token;
 27     int  i;
 28     char eof;
 29  
 30     int num = 0;
 31  
 32     eof = !fgets(line, sizeof(tmpBuffer), inputFile);
 33     if (eof)
 34         return num;
 35  
 36     memcpy(tmpBuffer2,tmpBuffer,100);
 37  
 38  
 39     token = strtok(line, separator);
 40     while (num < MAX_NUM_ARGS && token)
 41     {
 42         strcpy(args[num], token);
 43         num++;
 44         token = strtok(NULL, separator);
 45     }
 46  
 47     for (i = num; i < MAX_NUM_ARGS; i++)
 48         args[i][0] = 0;
 49  
 50     if(num > 0)
 51     {
 52         fprintf(fd, "%s", tmpBuffer2);
 53     }
 54  
 55     return num;
 56 }
 57  
 58  
 59 static void help_manual(FILE* fd)
 60 {
 61     fprintf(fd,"\nd d         :           display current debug level\n");
 62     fprintf(fd,"d q         :           display msg queue length, head and tail\n");
 63     fprintf(fd,"s d [level] :           set debug [level] \n");
 64 }
 65  
 66 static void show_MQ(FILE* fd)
 67 {
 68     fprintf(fd," msg queue length:%d  head:%d  tail:%d \n",abs(MsgQueue.head-MsgQueue.rear),MsgQueue.head,MsgQueue.rear);
 69 }
 70  
 71 static void show_debug_level(FILE* fd)
 72 {
 73     fprintf(fd," current debug level: %d\n", global.debug_level);
 74 }
 75  
 76  
 77  
 78 void debug_center()
 79 {
 80     int rc,num,n;
 81     FILE* fp;
 82     FILE* fd;
 83  
 84     MY_LOG(DEBUG,"Hi,debug!\n");
 85  
 86  
 87     system("rm /vob/ljsdpoenew3/exercise/debug_log");
 88     system("rm /vob/ljsdpoenew3/exercise/debug_log2");
 89     rc = mkfifo("/vob/ljsdpoenew3/exercise/debug_log", 0666);
 90     if(rc < 0)
 91     {
 92        MY_LOG(DEBUG, "make fifo fail!\n");
 93        pthread_exit(0);
 94     }
 95  
 96     fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r");
 97     if(fp == NULL)
 98     {
 99         MY_LOG(DEBUG, "open debug_log fail!\n");
100         pthread_exit(0);
101     }
102  
103     fd = fopen("/vob/ljsdpoenew3/exercise/debug_log2", "w+");
104     if(fd == NULL)
105     {
106         MY_LOG(DEBUG, "open debug_log2 fail!\n");
107         pthread_exit(0);
108     }
109     //freopen("/vob/ljsdpoenew3/exercise/debug_log2.txt", "w+", fd);
110  
111     fprintf(fd,"  \n==================================================\n");
112     fprintf(fd,"             Welcme to Debug Center!");
113     fprintf(fd,"  \n==================================================\n\n");
114     help_manual(fd);
115     //fflush(fd);
116  
117     while(1)
118     {
119         fflush(fd);
120         fprintf(fd,"\n\nmy_diag>");
121         num = get_args(fp,fd);
122         if(num < 1)
123         {
124             freopen("/vob/ljsdpoenew3/exercise/debug_log", "r", fp);
125             fflush(fd);
126             continue;
127         }
128         fprintf(fd,"\n\nmy_diag>");
129         switch(args[0][0])
130         {
131             case 'd':    //display
132                 switch(args[1][0])
133                 {
134                     case 'q':   //display queue
135                     show_MQ(fd);
136                     break;
137                     case 'd':   //display debug
138                     show_debug_level(fd);
139                     break;
140  
141                     default:
142                         help_manual(fd);
143                         break;
144                 }
145                 break;
146  
147             case 's':    //set
148                 switch(args[1][0])
149                 {
150                     case 'd': //set debug level
151                     n = atoi(args[2]);
152                     fprintf(fd," debug level change from %d to %d",global.debug_level,n);
153                     global.debug_level = n;
154                     break;
155  
156                     default:
157                         help_manual(fd);
158                         break;
159                 }
160                 break;
161  
162             default:
163                 help_manual(fd);
164                 break;
165         }
166  
167     }
168 }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在紅帽RHEL7系統中firewalld服務取代了iptables服務,但依然可以使用iptables命令來管理內核的netfilter。 iptables命令中則常見的控制類型有: 規則鏈則依據處理數據包的位置不同而進行分類: PREROUTING:在進行路由選擇前處理數據包 INPUT:處理入站 ...
  • IP:10.104.0.101 [root@localhost network-scripts]# cat ifcfg-em1 [root@localhost network-scripts]# iptables -F [root@localhost network-scripts]# iptabl ...
  • 配置環境: 操作系統:Windows Server 2003 sp2企業版 Web伺服器:系統自帶的IIS6.0 所需工具: PHP:php-5.2.12-Win32.zip(官方網址:http://www.php.net) 資料庫:mysql-5.0.22-win32.zip(官方網址:http: ...
  • 用MSI安裝包安裝 根據自己的操作系統下載對應的32位或64位安裝包。按如下步驟操作: MySQL資料庫官網的下載地址http://dev.mysql.com/downloads/mysql,第一步: 安裝許可 雙擊安裝文件,在如下圖所示界面中勾選“I accept the license term ...
  • 這篇文章將對linux下udp socket編程重要知識點進行總結,無論是開發人員應知應會的,還是說udp socket的一些偏僻知識點,本文都會講到。儘可能做到,讀了一篇文章之後,大家對udp socket有一個比較全面的認識。本文分為兩個專題,第一個是常用的upd socket框架,第二個是一些 ...
  • redhat 的更新包只對註冊的用戶生效,所以我們需要自己手動更改成CentOS 的更新包,CentOS幾乎和redhat是一樣的,所以無需擔心軟體包是否可安裝,安裝之後是否有問題。 ...
  • linux教學輔助訓練(第二階段) 標簽(空格分隔):Linux輔助訓練 [更多資料點我查看][1] 提示 :本階段性練習題是對《實戰教學筆記》相應章節知識的歸納與擴展部分,必須要會,是面試前必須重溫的一套基礎練習。(第一階段即為實戰教學筆記第四節 Linux命令基礎)。 一,問答考試 1.1 解釋 ...
  • 第十節 正則表達式 標簽(空格分隔):Linux實戰教學筆記 [更多資料點我查看][1] 第1章 什麼是正則表達式 1. 正則表達式就是為了處理大量的文本|字元串而定義的一套規則和方法 2. 通過定義的這些特殊符號的輔助,系統管理員就可以快速過濾,替換或輸出需要的字元串。Linux正則表達式一般以行 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...