Tuesday, July 31, 2012

維基大典

剛剛才發現維基百科有文言版。電子在文言中稱作"子"嗎?


電晶體


電晶體者,電子儀器之本也。蓋西方有人自沙中煉藥,曰為硅。硅有十四子,其外圍有四,與別硅扣環,求其穩定。後西人於硅中參入磷,使其多一子,曰為負型;或參入硼,使其少一子,曰為正型。 後人以兩負型中夾以少量正型,或兩正型中夾以少量負型而用。其外觀如水晶,故名為電晶,取電之晶體也。 電晶有三極,名曰集基射。若以基輸以訊,射接於地,集接於電,可於集取得數倍於基之訊,故可取訊之放大效果也。 或以集輸以電,射接於地,基輸以是,可於射取得否。數電晶相乘,可判是非也。

http://zh-classical.wikipedia.org/wiki/%E9%9B%BB%E6%99%B6%E9%AB%94

Sunday, July 22, 2012

dmenu_wrapper 把dmenu候選按照使用頻率排序

dmenu 是一個輕量級的程式執行器,可以快速找尋你想執行的程式。

網路上其實有不少它的wrapper,可以讓 dmenu 顯示的候選選項按照被選到的頻率排序。不過一直找不到適合的來使用(如 Yeganesh  是用 Haskell 寫的,Haskell套件有點大),所以最後決定自己寫一個。


 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<dirent.h>
#include<limits.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<map>
#include<algorithm>
#include<string>

using namespace std;

char log[PATH_MAX + 1];
char path[65536];
char out[67108864 + 1];
char name[NAME_MAX + 1];
char tmp[4096];
map<string,int> dic;
pair<string,int> lis[262144];

bool cmp(const pair<string,int> &a,const pair<string,int> &b){
    if(a.second == b.second){
        return a.first < b.first;
    }
    return a.second > b.second;
}

int main(int argc,char **argv){
    int i;

    FILE *f;
    int prio;
    char *ptr;
    int fd;
    DIR *dir;
    struct dirent *dirent;
    struct dirent *result;
    struct stat st;
    map<string,int>::iterator it;
    int count;
    char *last;
    FILE *pipe;

    sprintf(log,"%s/.dmenu_wrapper_log",getenv("HOME"));
    if((f = fopen(log,"r")) != NULL){
        while(fgets(name,NAME_MAX,f)){
            name[strlen(name) - 1] = '\0';
            fgets(tmp,4096,f);
            sscanf(tmp,"%d",&prio);
            dic.insert(pair<string,int>(name,prio));
        }
        fclose(f);
    }

    dirent = (struct dirent*)malloc(sizeof(struct dirent) + NAME_MAX + 1);
    strcpy(path,getenv("PATH"));
    ptr = strtok(path,":");
    count = 0;
    while(ptr != NULL){
        fd = open(ptr,O_RDONLY);
        dir = fdopendir(fd);
        while(true){
            readdir_r(dir,dirent,&result);
            if(result == NULL){
                break;
            }
            fstatat(fd,result->d_name,&st,0);
            if(!S_ISREG(st.st_mode) || !(st.st_mode & S_IXUSR)){
                continue;
            }

            prio = 0;
            if((it = dic.find(result->d_name)) != dic.end()){
                prio = it->second;
            }
            lis[count] = pair<string,int>(result->d_name,prio);
            count++;
        }
        closedir(dir);

        ptr = strtok(NULL,":");
    }
    free(dirent);

    sort(lis,lis + count,cmp);
    strcpy(out,"echo -e \"");
    last = out + strlen(out);
    for(i = 0;i < count;i++){
        strcpy(last,lis[i].first.c_str());
        last += strlen(lis[i].first.c_str());
        strcpy(last,"\\n");
        last += 2;
    }
    last -= 2;
    strcpy(last,"\" | dmenu");
    last += strlen(last);
    for(i = 1;i < argc;i++){
        strcpy(last," \"");
        last += 2;
        strcpy(last,argv[i]);
        last += strlen(last);
        *last = '\"';
        last += 1;
    }

    pipe = popen(out,"r");
    name[0] = '\0';
    fgets(name,NAME_MAX,pipe);
    pclose(pipe);

    if(name[0] != '\0'){
        name[strlen(name) - 1] = '\0';
        
        if(fork() == 0){
            execlp(name,name,NULL);
        }

        it = dic.find(name);
        if(it != dic.end()){
            it->second++;
        }else{
            dic.insert(pair<string,int>(name,1));
        }

        if((f = fopen(log,"w")) != NULL){
            for(it = dic.begin();it != dic.end();it++){
                fprintf(f,"%s\n%d\n",it->first.c_str(),it->second);
            }
            fclose(f);
        }
    }

    return 0;
}
 

不能隨便將字串丟入strtok

雖然知道strtok會把傳入的字串切亂,剛剛還是把getenv("PATH")所取回的環境變數PATH丟進strtok,結果就發生奇怪的Bug

char *path;
char *ptr;

path = getenv("PATH");
ptr = strtok(path,":");
while(ptr != NULL){
    printf("%s\n",ptr);

    ptr = strtok(NULL,":");
}
//此程式的環境變數PATH已經被strtok切亂

execlp("ps","ps",NULL));
//execlp取到不正確的PATH,找不到ps這個程式

Saturday, July 14, 2012

Linux硬碟磁區讀寫保護

Block Driver層磁區讀寫保護,其實不太確定這個可以做甚麼...

用fdisk將原有的分割表清空,測試磁區 0 讀寫保護(MBR磁區)




















Reboot後正常啟動,分割表沒有被清空

Wednesday, July 11, 2012

Linux隱藏程序且不被Unhide偵測

透過一些不太標準的方法,可以隱藏指定程序,並且繞過隱藏程序檢查工具 Unhide 的偵測。

Source Code中隱藏程序的部份在hp_hook函式,其他的部份是把整個OS暫停。


由於隱藏程序過程如果發生進程切換,可能導致系統崩潰,但找不到新版本Linux Kernel中關閉整個OS進程切換的內建函式(以前有,但是新版Linux Kernel不希望開發者這麼做),只能關閉CPU單一核心進程切換(preempt_disable或是指令cli)。因此透過切換各CPU核心並執行preempt_disable,確定每個核心的進程切換都被關閉後才執行hp_hook函式。


目前發現在隱藏有視窗的程序時,執行Unhide檢測後,會出現奇怪的結果,原本該程序的PID沒被檢測到隱藏,但是檢測到原本不存在PID,原因不明。


Source Code:http://www.mediafire.com/?mroby9rj1uk2qc5
  1. 執行make編譯Driver,執行gcc -O2 app.c -o app編譯隱藏工具
  2. 先執行insmod hpmod.ko載入Driver,並執行./app (root權限),輸入要隱藏的PID
Hide vim前

Hide vim後,並確定vim正常運作

Sunday, July 8, 2012

FILCO Majestouch Ninja 87鍵茶軸開箱

Blog也多少應該發一些比較不那麼學術的東西:)


外盒,FILCO Majestouch NINJA Tenkeyless,還寫了一個"忍"


底面,有一朵有點詭異的紅花





















側面,型號是FKBN87M/EFB2,美式配置,茶軸,側刻鍵帽




台灣製造



盒子內部,配件蠻少的(跟Ducky比起來),有附上兩顆側刻版的Win鍵,忘記拍了...




鍵盤本體





















英刻的側刻鍵帽,其實打的時候除非光線真的很暗,不然還是喵的到字,不知道鍵位時(或是突然忘記?),足以辨識了





















跟Ducky一樣,都是斜的設計





















鍵盤燈其實不是很明顯,打字的時候會有點被鍵帽擋住





















拆下所有鍵帽,下面有一層塑膠底板,鐵板應該是在下面(之前用的Ducky 1087沒有塑膠板,鐵板直接裸露在外面)





















長鍵帽用的是平衡桿,Ducky是用衛星軸(一個鍵帽三個軸)





















長鍵帽下有扣環用來扣住平衡桿的鐵絲,應該可以拔下來(但是剛剛拔不下來,有點緊= =)





















最後是FILCO側邊低調的Logo,似乎是因為Ninja的關係,做成黑色的,其他款的好像是銀色的Logo






















用了一小段時間,感覺FILCO的品質真的相當不錯,做工細緻,多少會影響到打字的感受。
例如它的鍵帽就還蠻穩的,不會左右搖晃。側刻的設計其實打起來真的不錯,鍵帽摸起來很平滑舒服,突然忘記鍵位時還可以看一下。


我還是比較喜歡之前用的Ducky 1087的衛星軸設計,平衡桿沒弄好有時候會有怪聲,現在這把Backspace按鍵的平衡桿就有奇怪的咭咭聲XD,不知道是潤滑油不足還是甚麼原因。


跟同樣是87鍵的Ducky 1087比起來,這把鍵盤明顯比較重,多了一層塑膠上蓋應該也增加不少重量。

另外它的USB線是固定在鍵盤上的(Ducky是可以拔下來的設計),有時候帶出去線會卡到會比較不方便。

還有它的背面有兩根螺絲,直接被保固貼紙跟產品貼紙蓋住了(如下圖),根本就是不想讓你拆阿XD,不過據台灣FILCO代理商的售後服務非常好,還是不要亂動好了。


















不過FILCO沒有Ducky拉風的鍵盤絨布套阿XD (上面還有燙金的Ducky Logo),帶出去如果沒背背包的話,就只能直接拿在手上了。

補充一下,FILCO有出 Majestouch BLACKMajestouch NINJA 兩個系列的鍵盤,看起來都差不多,一樣是側刻,其實差異在BLACK是日文鍵盤,有多一些按鍵,NINJA則是一般比較常見的美式配置鍵盤。

Friday, July 6, 2012

Linux System API的分歧

Linux System API也太分歧了,同樣一個取得檔案屬性的API stat,system call就可以有
  1. stat
  2. lstat
  3. fstatat
  4. fstat
不同Linux平台還可以分64bit跟32bit版本(這可能是為了讓Linux Kernel可以在各種環境運作的關係,是頗有道理)

其實會發現fstatat只要傳入某些參數,其實幾乎完全可以取代stat跟lstat。
從Kernel Code來看,其實前3個函式實作上都只是簡單的前置處理,接著傳給一個 vfs_fstatat 函式作真正的處理。

不只stat,如getxattr,setxattr,listxattr,removexattr等都有這種狀況。

Windows的這方面就簡潔許多,Widnows有關檔案管理操作一切都從 CreateFile 開始,它會傳回一個Handle,之後不論任何有關該檔案的操作,皆使用這個Handle,並從這個Handle檢查權限,最後透過 CloseHandle 關閉Handle。

結果導致光虛擬化*stat跟*xattr就變成這樣。(如果在Windows下,其實*stat跟*xattr這類函式都不需要特別虛擬化,因為都是用Handle在操作,只要虛擬化 CreateFile 即可)


Thursday, July 5, 2012

部份完成ls指令相關虛擬化

經過多次的失敗跟嘗試,同功能的一段Code寫了數次後,完成虛擬化動作讓ls指令可以在虛擬化環境下正常顯示資料。

 完成syscall open,openat,lstat,getxattr,列舉資料夾檔案(readdir,getdents等)


Monday, July 2, 2012

Linux寫入資料到其他進程的記憶體

如果Process A要在Process B的某記憶體位置寫入資料,在Windows下可以使用Windows API WriteProcessMemory 。但在Linux下似乎找不到內建的相關函式可以用,或是相關的函式並沒有在Kernel中被導出(Export),因此經過測試,發現可以透過以下方法來實現。

Linux Driver函式透過syscall被呼叫時,虛擬記憶體User Mode部分是映射到呼叫syscall者的Process記憶體內容,因此像是copy_from_user之類的函式可以直接複製呼叫者的記憶體資料。

但是在這種情況下如果要寫入資料到其他Process的記憶體,就必須切換User Mode的映射。一般CPU的虛擬記憶體與物理記憶體映射對應是透過記憶體映射表,由CPU的CR3暫存器指向當前的記憶體映射表。


因此透過更改CR3暫存器,我們就可以暫時將User Mode的映射切換到其他Process,並透過copy_from_user或copy_to_user讀取或寫入到該Process。

部份Code(將Process A的資料複製到當前Process):
 
void *src;    //指向Process A的User Mode資料記憶體位置
void *dst;    //指向要寫入到當前(current)process的User Mode記憶體位置
void *buf;    //臨時緩衝區
int size;     //要複製的大小
struct task_struct *a_task;    //Process A的task_struct,a_task =  pid_task(find_vpid(Process A的PID),PIDTYPE_PID)
struct mm_struct *oldmm;

oldmm = current->mm;    //保存當前mm_struct
current->mm = a_task->mm;
current->active_mm = current->mm;    //如果當前Process不是一個Kernel Thread,active_mm = current->mm
load_cr3(current->mm->pgd);    //更新CR3
     
copy_from_user(buf,src,size);    //從Process A複製資料到緩衝區

current->mm = oldmm;    //恢復當前mm_struct
current->active_mm = current->mm;
load_cr3(current->mm->pgd);    //恢復CR3
     
copy_to_user(dst,buf,size);    //將緩衝區資料寫到當前Process的目標位置
 

一開始,我沒對current->mm和current->active_mm作替換,但是由於Linux在進程調度時,會根據current->mm載入CR3,為了避免呼叫完load_cr3後發生進程調度,導致CR3被改回去(經過測試,確定會發生此狀況),用preempt_disable()暫時關閉搶佔,保證從load_cr3到copy_to_user結束都不會發生進程調度。

但是後來測試時,發現對於size比較大時,會發生copy_to_user失敗的情形。追究原因,應該是因為copy_from_user和copy_to_user皆會發生Page Fault(複製時剛好該記憶體頁被置換出物理記憶體,這時會發生Page Fault,然後Linux去把該記憶體頁重新映射回物理記憶體中),但是關閉搶佔的情況下,是無法處理Page Fault,導致讀寫失敗。