Linux系統中軟件簡單License的實現

概述linux

       目前,不少商用應用系統是運行在Linux系統之上的,爲了維護開發者利益,有必要爲軟件添加license功能,防止軟件被盜用和挪用。本文探討如何在Linux軟件中添加license功能,用到的算法是MD5算法。git

 

關鍵字: MD5算法

 

一 目的和思路windows

       設置License就是要將軟件和運行該軟件的機器進行簡單「綁定」,該軟件只能在某臺指定機器上使用,若是將該軟件挪動到其餘機器上則沒法運行。網絡

       根據以上目的,那麼思路就很清晰,則咱們須要讀取該指定機器的某些特徵,這裏咱們只抓取兩個特徵,即CPU特徵和機器MAC地址。數據結構

這樣,咱們須要一個得到機器特徵的程序,一個生成license的程序以及在所要加license的程序中嵌入檢查license的代碼。生成license程序能夠是在windows操做系統下編寫,也能夠在Linux系統下編寫,本文只討論後面一種。socket

 

二 關鍵技術函數

       這裏的三個關鍵技術是得到本機的CPU信息、得到網卡MAC地址信息以及MD5加密。oop

1. 得到CPU信息ui

兼容x86的CPU的信息存儲在數據結構:


struct cpuinfo_x86 {
__u8 x86;        /* CPU family */
__u8 x86_vendor; /* CPU vendor */
__u8 x86_model;
__u8 x86_mask;
char wp_works_ok;    /* It doesn't on 386's */
char hlt_works_ok;    /* Problems on some 486Dx4's and old 386's */
char hard_math;
char rfu;
       int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
unsigned long x86_capability[7];
char x86_vendor_id[16];
char x86_model_id[64];
int x86_cache_size;  /* in KB - valid for CPUS which support this
                   call  */
int x86_cache_alignment; /* In bytes */
int fdiv_bug;
int f00f_bug;
int coma_bug;
unsigned long loops_per_jiffy;
unsigned char x86_num_cores;
};

中,對於咱們來講,只要得到x86_vendor_id和x86_model_id兩項信息便可,前者記錄的是CPU的製造商的信息,後者記錄CPU的記錄信息。在Linux下,能夠經過如下代碼得到:

 

static inline void
cpuid(int op, unsigned int *eax, unsigned int *ebx,
                      unsigned int *ecx, unsigned int *edx)
{
       __asm__("cpuid"
              : "=a" (*eax),
                "=b" (*ebx),
                "=c" (*ecx),
                "=d" (*edx)
              : "0" (op));
}
 
static unsigned int
get_cpu_id()
{
       unsigned int *v;
 
       cpuid(0x00000000, &cpu_info.cpuid_level,
             (int *)&cpu_info.x86_vendor_id[0],
             (int *)&cpu_info.x86_vendor_id[8],
             (int *)&cpu_info.x86_vendor_id[4]);
 
       v = (unsigned int *) cpu_info.x86_model_id;
       cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
       cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
       cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
 
       return 0;
}

 

       這樣,CPU的信息就存儲在cpu_info中。

 

2. 得到MAC地址

網卡信息是存儲在數據結構:

struct ifconf ifc;

中,該結構包含在頭文件

#include <net/if.h>

中,在編寫代碼時須要將它包含。得到MAC地址的代買以下:

 

#define IFRSIZE      ( ( int )( size * sizeof( struct ifreq ) ) )
 
static int
get_mac_addr(void)
{
       int                sockfd, size = 1;
 
       if (0 > (sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
              logit(LOG_SYS, LEVEL_ERR, "get_mac_addr: cannot open socket.\n" );
              return -1;
       }
       ifc.ifc_req = NULL;
 
       do {
              ++size;
              /* realloc buffer size until no overflow occurs  */
              if (NULL == (ifc.ifc_req = realloc( ifc.ifc_req, IFRSIZE))) {
                     logit(LOG_SYS, LEVEL_ERR,  "get_mac_addr: out of memory.\n" );
                     return -1;
              }
              ifc.ifc_len = IFRSIZE;
              if (ioctl(sockfd, SIOCGIFCONF, &ifc)) {
                     logit(LOG_SYS, LEVEL_ERR, "get_mac_addr: ioctl SIOCFIFCONF");
                     return -1;
              }
       } while (IFRSIZE <= ifc.ifc_len);
 
       ifr = ifc.ifc_req;
       for (; ( char * )ifr < ( char * )ifc.ifc_req + ifc.ifc_len; ++ifr) {
              if (ifr->ifr_addr.sa_data == ( ifr + 1 )->ifr_addr.sa_data) {
                     continue;  /* duplicate, skip it */
              }
              if ( ioctl( sockfd, SIOCGIFFLAGS, ifr ) ) {
                     continue;  /* failed to get flags, skip it */
              }
/*           printf( "Interface:  %s\n", ifr->ifr_name );*/
              if ( 0 == ioctl( sockfd, SIOCGIFHWADDR, ifr)) {
                     switch ( ifr->ifr_hwaddr.sa_family ) {
                     case ARPHRD_NETROM:
                     case ARPHRD_ETHER:
                     case ARPHRD_PPP:
                     case ARPHRD_EETHER:
                     case ARPHRD_IEEE802:
                            break;
                     default:
/*                         printf( "\n" );*/
                            continue;
                     }
                     mac_addr = ( unsigned char * )&ifr->ifr_addr.sa_data;
                     if ( mac_addr[0] + mac_addr[1] + mac_addr[2] + mac_addr[3]
                            + mac_addr[4] + mac_addr[5] ) {
                            return 0;
                     } else {
                            return -1;
                     }
              }
/*          printf( "\n" ); */
       }  /* end of for */
       close(sockfd);
       return 0;
}

 

       這樣,MAC地址就放在mac_addr中,mac_addr 的定義是:

unsigned char *mac_addr;

 

3. MD5算法加密

MD5算法實際上是一個用來產生隨機數的算法,已經有了現成的源碼(在RFC中有詳細描述),它最主要的接口主要是函數:

UINT4B  md_32(char *string, int length)

該函數傳入一串字符串,而後返回一個32字節的帶符號位整型數。這裏,字符串的內容能夠是任意的,而一旦內容固定,那麼返回的整型數就是惟一的。因而,咱們就能夠利用這個函數來驗證輸入的字符串的正確性。

 

四 流程

      

1. 生成license

首先是得到CPU信息,則經過上述代碼,將CPU信息存入cpu_info。

其次是得到MAC地址,則經過上述代碼,將MAC地址存入mac_addr中,

最後就是調用函數md_32,得到一個隨機數,將該隨機數寫入到某個文件,能夠稱之爲驗證碼,那麼該文件即生成爲license文件。

 

2. 驗證license

驗證license的一部分步驟和生成license是如出一轍的,即驗證時,也須要先讀取CPU和MAC地址信息,而後也經過md_32函數生成一個驗證碼。此時,驗證過程須要讀入license文件中的數字,而後將二者進行比較,若是相同,則表示license正確,能夠繼續如下流程,若是不同,則代表license出現問題,程序終止。

 

五 拓展

1. 加入其餘限制

由於生成license的原理是經過一個字符串生成一個驗證碼,因而,這個字符串的內容是能夠任意修改的。也就是說,能夠根據須要加入一些限制條件。

例如,要限制某個程序的最大網絡鏈接數爲200,那麼就能夠加入200這個數字在字符串中,生成一個新的驗證碼,而在license文件中寫入相似:max_conns = 200的內容。當驗證license時,驗證流程讀入max_conns的值做爲全局變量,同時再經過計算license的驗證碼是否正確。一旦正確,那麼max_conns的值(200)就成爲程序的一個全局變量,能夠用它來限制最大鏈接數。若是有人修改了license文件,使得max_conns的值發生變化,那麼驗證過程根據新的max_conns值所獲得的驗證碼就跟license文件的驗證碼不同,這樣驗證不過關,表示license文件是錯誤的。

另外,在license中加入驗證時間的有效性也是經常使用的方法。

 

2. 生成license文件的變化

上面討論的生成license和驗證license的過程,都是在Linux操做系統中生成的。在生成license時,由於CPU和MAC地址的關係,必須在同一臺機器上操做,這樣帶來了很大不方面,並且不利於保密。

一個改進的辦法是,將生成license文件的過程拆成兩個步驟:

第一步,將得到的CPU和MAC地址信息放入一個文件;

第二步,將文件傳遞到某個平臺,由該平臺生成license文件。

這樣的好處是,生成license文件的執行程序能夠是Linux系統的,也能夠是Windows系統下的,並且不須要在同一臺機器上運行。

       附錄的源碼就是這樣一個過程。

 

 (相關附錄的源碼放在http://laopang.vip.sina.com/articles/own/Tech/linux.license.rar)

相關文章
相關標籤/搜索