这里说的“后门”并不是教你做坏事,而是让你做好事,搭建自己的调试工具更好地进行调试开发。我们都知道,当程序发生异常错误时,我们需要定位到错误,有时我们还想,我们在不修改程序的前提下,就能通过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 -;
}
二、创建FIFO
为什么要创建FIFO(有名管道)?因为我们需要跟我们的程序进行通信,我们需要把我们的指令告诉程序,那就需要一个通信途径,FIFO就是一个很好的选择。我们把我们的指令写进管道,程序将指令从管道出,然后执行该指令,这样子我们程序后门的通信模型就出来了。why解决了,是时候解决how了。
 
对于管道的操作,我是这么做的:
system("rm /vob/ljsdpoenew3/exercise/debug_log");   //每次进入debug center我们都将原来的fifo文件删除,避免影响后面操作
rc = mkfifo("/vob/ljsdpoenew3/exercise/debug_log", ); //创建fifo
if(rc < )
{
MY_LOG(DEBUG, "make fifo fail!\n");
pthread_exit();
} fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r"); //打开fifo,读取指令
if(fp == NULL)
{
MY_LOG(DEBUG, "open debug_log fail!\n");
pthread_exit();
}

读fifo我们解决了,那怎么将我们的指令写进fifo呢?这里我打算使用shell的read指令,文章后面会解释如何实现。

三、解析指令
解析指令又可以分为两个步骤:
  1. 将从fifo取得数据进行格式解析,比如我定义了d d的意思是display debug,即显示现在的debug级别,那么我们程序就得首先对原始数据进行格式处理。
  2. 将指令进行命令解析,执行相应操作。
 
格式处理:
static int get_args(FILE *inputFile)
{
char tmpBuffer[];
char *line = tmpBuffer;
char separator[] = " ,\n\t";
char *token;
int i;
char eof; int num = ; 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][] = ; return num;
}
 
命令解析:
        switch(args[][])  //解析指令,看每个指令对应哪些意思
{
case 'd': //display
switch(args[][])
{
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[][])
{
case 'd': //set debug level
n = atoi(args[]); //将字符串转化为整数
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();
} 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
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到目的主机再运行脚本操作比较安全。
 
 
 
最后给出源代码框架
 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include "msg_def.h"
#include "global.h" extern Queue_t MsgQueue;
extern dashboard_t global; #define MAX_NUM_ARGS 20
#define MAX_ARGS_SIZE 56 static char args[MAX_NUM_ARGS][MAX_ARGS_SIZE]; static int get_args(FILE *inputFile,FILE *fd)
{
char tmpBuffer[];
char tmpBuffer2[];
char *line = tmpBuffer;
char separator[] = " ,\n\t";
char *token;
int i;
char eof; int num = ; eof = !fgets(line, sizeof(tmpBuffer), inputFile);
if (eof)
return num; memcpy(tmpBuffer2,tmpBuffer,); 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][] = ; if(num > )
{
fprintf(fd, "%s", tmpBuffer2);
} return num;
} static void help_manual(FILE* fd)
{
fprintf(fd,"\nd d : display current debug level\n");
fprintf(fd,"d q : display msg queue length, head and tail\n");
fprintf(fd,"s d [level] : set debug [level] \n");
} static void show_MQ(FILE* fd)
{
fprintf(fd," msg queue length:%d head:%d tail:%d \n",abs(MsgQueue.head-MsgQueue.rear),MsgQueue.head,MsgQueue.rear);
} static void show_debug_level(FILE* fd)
{
fprintf(fd," current debug level: %d\n", global.debug_level);
} void debug_center()
{
int rc,num,n;
FILE* fp;
FILE* fd; MY_LOG(DEBUG,"Hi,debug!\n"); system("rm /vob/ljsdpoenew3/exercise/debug_log");
system("rm /vob/ljsdpoenew3/exercise/debug_log2");
rc = mkfifo("/vob/ljsdpoenew3/exercise/debug_log", );
if(rc < )
{
MY_LOG(DEBUG, "make fifo fail!\n");
pthread_exit();
} fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r");
if(fp == NULL)
{
MY_LOG(DEBUG, "open debug_log fail!\n");
pthread_exit();
} fd = fopen("/vob/ljsdpoenew3/exercise/debug_log2", "w+");
if(fd == NULL)
{
MY_LOG(DEBUG, "open debug_log2 fail!\n");
pthread_exit();
}
//freopen("/vob/ljsdpoenew3/exercise/debug_log2.txt", "w+", fd); fprintf(fd," \n==================================================\n");
fprintf(fd," Welcme to Debug Center!");
fprintf(fd," \n==================================================\n\n");
help_manual(fd);
//fflush(fd); while()
{
fflush(fd);
fprintf(fd,"\n\nmy_diag>");
num = get_args(fp,fd);
if(num < )
{
freopen("/vob/ljsdpoenew3/exercise/debug_log", "r", fp);
fflush(fd);
continue;
}
fprintf(fd,"\n\nmy_diag>");
switch(args[][])
{
case 'd': //display
switch(args[][])
{
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[][])
{
case 'd': //set debug level
n = atoi(args[]);
fprintf(fd," debug level change from %d to %d",global.debug_level,n);
global.debug_level = n;
break; default:
help_manual(fd);
break;
}
break; default:
help_manual(fd);
break;
} }
}

Linux编程之给你的程序开后门的更多相关文章

  1. 从裸机编程到嵌入式Linux编程思想的转变------分而治之:驱动和应用程序

    笔者学习嵌入式Linux也有一段时间了,很奇怪的是很多书讲驱动编程方面的知识,也有很多书将ARM9方面的知识,但是从以前51形式的(对寄存器直接操作,初始化芯片的功能模块)编程方法,和思维模式,变换为 ...

  2. 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结 转载

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  3. 【转】牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  4. Linux 编程中的API函数和系统调用的关系【转】

    转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...

  5. linux编程获取本机网络相关参数

    getifaddrs()和struct ifaddrs的使用,获取本机IP 博客分类: Linux C编程   ifaddrs结构体定义如下: struct ifaddrs { struct ifad ...

  6. 面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  7. Linux编程简介

    Linux编程可以分为Shell(如BASH.TCSH.GAWK.Perl.Tcl和Tk等)编程和高级语言(C语言,C++语言,java语言等)编程,Linux程序需要首先转化为低级机器语言即所谓的二 ...

  8. 在桌面Linux环境下开发图形界面程序的方案对比

    在Linux下开发GUI程序的方法有很多,比如Gnome桌面使用GTK+作为默认的图形界面库,KDE桌面使用Qt作为默认的图形界面库,wxWidgets则是另一个使用广泛的图形库,此外使用Java中的 ...

  9. Linux编程return与exit区别

    Linux编程return与exit区别 exit  是用来结束一个程序的执行的,而return只是用来从一个函数中返回. return return 表示从被调函数返回到主调函数继续执行,返回时可附 ...

随机推荐

  1. PHP模板引擎正则替换函数 preg_replace 与 preg_replace_callback 使用总结

    在编写PHP模板引擎工具类时,以前常用的一个正则替换函数为 preg_replace(),加上正则修饰符 /e,就能够执行强大的回调函数,实现模板引擎编译(其实就是字符串替换). 详情介绍参考博文:P ...

  2. (PHP)程序中如何判断当前用户终端是手机等移动终端

    推荐: Mobile-Detect:https://github.com/serbanghita/Mobile-Detect/blob/master/Mobile_Detect.php Detect ...

  3. 老毛桃u盘装系统制作工具

    老毛桃[url=http://www.laomaotao.cn.com/]一键u盘装系统下载[/url]告别繁琐,简单易用,一盘两用,携带方便.不需要任何技术基础,一键制作,自动完成制作,平时当U盘使 ...

  4. Flask 应用上下文

    应用上下文(application context) 它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy ...

  5. php 图形用户界面GUI 开发

    php 图形用户界面GUI 开发 一.下载指定系统扩展 1 2 http://pecl.php.net/package/ui http://pecl.php.net/package/ui/2.0.0/ ...

  6. BZOJ3712[PA2014]Fiolki——并查集重构树

    题目描述 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤 ...

  7. Oracle EBS 查看请求执行情况

    SELECT s.* FROM fnd_concurrent_requests r, v$session v, v$sql s WHERE r.oracle_session_id = v.audsid ...

  8. [温故]图解java多线程设计模式(二)

    join & interrupt 这俩方法属于线程对象里的方法,属于线程本身的操作. join方法 用于等待一个线程的终止,等待期间将会阻塞,直到被等待的线程终止结束. 所以join可以用来做 ...

  9. 条款22:将成员变量声明为private

    protected成员变量的封装性并非高于public变量. 如果有个public的成员变量,一旦其需要改变,那么所有使用它的代码都需要改变. 如果有个protected的成员变量,一点其需要改变,那 ...

  10. nandflash裸机程序分析

    它包含7个文件: head.S init.c main.c Makefile nand.c nand.lds 我们之前的程序都是在nandflash的前4k放代码,上电后自动拷贝到SRAM中,之后将S ...