2015/11/23

C, Read text file and process line by line.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#define _BUFFER_SIZE_   2048
 
FILE    *fd;
int     line;
char    buf[_BUFFER_SIZE_];
 
fd = fopen("filename", "r");
     
if (fd) {
    line = 0;
    while(fgets(buf, _BUFFER_SIZE_, fd) != NULL) {
        /* process line by line */
        line++;
    }
    fclose(fd);
} else {
    /* open file error handle */
}

2015/03/18

bash 預設變數

$0 : 執行的命令全名
$1, $2... : 各項參數
$? : 前一個指令執行的傳回值,例如某執行檔的 int main() { return 1; },則 $? 就是 1
$# :代表後接的參數『個數』,命令本身不算在內;
$@ :代表『 "$1" "$2" "$3" "$4" 』之意,每個變數是獨立的(用雙引號括起來);
$* :代表『 "$1c$2c$3c$4" 』,其中 c 為 $IFS 指定的分隔字元,預設為空白, 所以本例中代表『 "$1 $2 $3 $4" 』之意。
$! :取得前一個指令的 PID

例如:
IFS_SAVE="$IFS"
IFS="."
/tmp/scripttest opt1 opt2 opt3
$0 = "/tmp/scripttest"
$1 = "opt1"
$2 = "opt2"
$3 = "opt3"
$# = 3
$@ = "opt1" "opt2" "opt3"
$* = "opt1.opt2.opt3"
IFS="$IFS_SAVE"

bash 流程控制

Include

我們的 shell script 大到一定程度時,用單一檔案會變得不好管理維護,於是會把 shell script 寫成多個檔案,再用 include 的方式把其他檔案加進來。
bash 的 include 是用 . 開頭,後面接檔名。例如
#!/bin/sh
. /etc/init.d/getmibs
就會將 /etc/init.d/getmibs 的內容插進來。而取得 mibs 內容的 script 就用 /etc/init.d/getmibs 來維護就好,整個 script 看起來會變得很乾淨。

Function

例用 function 來結構化 script,可以增加 script 的可讀性,例如:
#!/bin/sh
hello()
{
    echo "Hello World!"
}
hello

夾帶參數

呼叫 function 時如果有參數,會在 function 裡面以 $1, $2.... 呈現,因為字串在 shell script 的定義並不是很嚴謹,所以要特別注意字串的傳遞。例如:
#!/bin/sh
hello()
{
    echo "----------"
    echo "var1 = $1"
    echo "var2 = $2"
}
hello Hello World!
hello "Hello World!"
從上面的例子觀察其結果,會發現第二次呼叫會把 "Hello World!" 當成一個變數來用,而第一是呼叫的會變成兩個,不得不防。

if 條件式

if [ 條件 ]; then
    條件成立時執行的內容
fi
if [ 條件 ]; then
    條件成立時執行的內容
else
    條件不成立時執行的內容
fi
if [ 條件1 ]; then
    條件1 成立時執行的內容
elif [ 條件2 ]; then
    條件1 不成立而條件2 成立時執行的內容
else
    條件1 跟條件2 都不成立時執行的內容
fi

case 條件式

case "$變數" in
    "值1")
        當 "$變數" == "值1" 時執行的內容
        ;;
    "值2")
        當 "$變數" == "值2" 時執行的內容
        ;;
    ... ...
    *)
        以上都不滿足時執行的內容
        ;;
esac

while / until 迴圈

while [ 條件 ]
do
    迴圈執行的內容
done
until [ 條件 ]
do
    迴圈執行的內容
done
while 是當條件成立時,執行回圈的內容,而 until 則是執行到條件成立時退出。迴圈執行期間也可以用 break 跳離迴圈。

for xxx in ooo 迴圈

for 變數 in 值1 值2 值3 ...
do
    迴圈執行內容
done
第一次執行迴圈內容時 $變數=值,第二次 $變數=值2 ...,值到所有變數都跑過之後結束。同樣要注意字串的處理。例如
#!/bin/sh
DNS1=8.8.8.8
DNS2=8.8.4.4
DNS3=0.0.0.0
for DNS_ADDR in $DNS1 $DNS2 $DNS3
do
    echo "DNS_ADDR=$DNS_ADDR"
done
for DNS_ADDR in "$DNS1 $DNS2 $DNS3"
do
    echo "DNS_ADDR=$DNS_ADDR"
done

數字迴圈
for 變數 in {開始..結束}
do
    迴圈執行內容
done

例如:
#!/bin/sh
for i in {1..65535}
do
   echo "$i time"
done

bash 判斷式

在 bash 環境下,可以使用 test 條件判斷 && 成立時執行 || 不成立時執行,例如
test 1 -eq 1 && echo "good" || echo "bad"
因為 1 永遠等於 1 ,所以上式會輸出 good
寫在 shell script 裡面,會用 [ ] 來表示判斷式,例如上面的例子就會變成
[ 1 -eq 1 ] &&
{
    echo "good"
} || {
    echo "bad"
}

或用另外一種 if 條件判斷的結構
if [ 1 -eq 1 ]; then
    echo "good"
else
    echo "bad"
fi

檔案存在

-e    檔名是否存在(無論是檔案或資料夾)
    [ -e "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,存在則傳回 .TRUE. 不存在則傳回 .FALSE.
-f    檔名是否以檔案存在
    [ -f "/etc/resolv.conf" ] 會去檢查 /etc/resolve.conf 是否存在而且為檔案
-d    檔名是否以資料夾存在
    [ -d "/etc/ppp" ] 會去檢查 /etc/ppp 是否存在而且是個資料夾
-b    檔名是否以 block device 存在
    [ -b "/dev/sda" ] 會去檢查 /etc/sda 是否存在,而且是個 block device
-c    檔名是否以 character device 存在
    [ -c "/dev/tty" ] 會去檢查 /dev/tty 是否存在,而且是個 character device
-S    檔名是否以 socket 存在
    [ -S "/tmp/.X11-unix/X0" ] 會去檢查 /tmp/.X11-unix/X0 是否存在,而且是個 socket file
-p    檔名是否以 FIFO (pipe) 存在
    [ -p "/tmp/mypipe" ] 會去檢查 /tmp/mypipe 是否存在,而且是個 FIFO file
-L    檔名是否以連結方式存在
    [ -L "/tmp" ] 會去檢查 /tmp 是否存在,而且是個連結
block device 是一種以 block 為單位傳輸的裝置,因此每次傳輸都會是 block 的倍數,屬於固定長度傳輸。主要用於隨機存取裝置,例如硬碟、光碟機。而 Character Device 則是以字元為傳輸單位,屬於非固定長度傳輸。主要用於印表機,終端機...。

檔案權限與屬性

-r    檔名是否存在,而且具有讀取權限
    [ -r "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且具有可讀取的權限
-w    檔名是否存在,而且具有寫入權限
    [ -w "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且具有可寫入的權限
-x    檔名是否存在,而且具有執行權限
    [ -x "/bin/busybox" ] 會去檢查 /bin/busybox 是否存在,而且具有可執行的權限
-u    檔名是否存在,而且具有 SUID 屬性
    [ -u "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且具有 SUID 屬性
-g    檔名是否存在,而且具有 SGID 屬性
    [ -g "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且具有 SGID 屬性
-k    檔名是否存在,而且具有 Sticky bit 屬性
    [ -k "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且具有 Sticky bit 屬性
-s    檔名存在,而且不是空白檔案
    [ -s "/etc/resolv.conf" ] 會去檢查 /etc/resolv.conf 是否存在,而且不是空白檔案

整數比較判斷

-eq    兩個整數是否相等 (equal)
    [ $n1 -eq $n2 ]
-ne    兩個整數是否不相等 (not equal)
    [ $n1 -ne $n2 ]
-gt    n1 是否大於 n2 (greater then)
    [ $n1 -gt $n2 ]
-lt    n1 是否小於 n2 (lesser then)
    [ $n1 -lt $n2 ]
-ge    n1 是否大於等於 n2 (greater then or equal)
    [ $n1 -ge $n2 ]
-le    n1 是否小於等於 n2 (lesser then or equal)
    [ $n1 -le $n2 ]

字串判斷

-z    判斷是否為空字串
    [ -z "$str1" ]    檢查 str1 是不是空字串,是空字串傳回 .TRUE.,否則傳回 .FALSE.
-n    判斷是否不是空字串,跟 -z 剛好相反
    [ -n "$str1" ]    檢查 str1 是不是空字串,是空字串傳回 .FALSE.,否則傳回 .TRUE.
==    判斷字串是否相等
    [ "$str1" == "$str2" ]
!=    判斷字串是否不相等
    [ "$str1" != "$str2" ]

其他邏輯

-a    前後兩個條件用 and 作邏輯運算
    [ $n1 -eq $n2 -a "$str1" == "$str2" ]
-o    前後兩個條件用 or 作邏輯運算
    [ $n1 -eq $n2 -o "$str1" == "$str2" ]
!    反相邏輯運算
    [ ! -e "/etc/resolve.conf" ]

習慣與注意事項

因為 bash 在很多地方會把空格當成隔開的符號,尤其是條件判斷式的每個單元都要用空格格開,包含前後的括號 [ ] ,所以 [ 1 -eq 1 ] 不能寫成 [1 -eq 1]。而變數內往往也會存在空白符號,所以用在字串判斷的時候,最好能養成習慣把變數用 "" 刮起來,例如 "$str1"。舉個例子來說
    str1="This is a book."
    [ $str1 == "This is a book" ]

因為 $str1 會變成 This is a book 而不是 "This is a book" 所以上式會變成 [ This is a book == "This is a book" ],會造成參數解析上的錯誤,所以還是養成習慣,只要遇到字串變數,都加上 "",如上例寫成 [ "$str1" == "This is a book." ] 就不會出錯了。

bash 字串切割

設定值的內容使用某個字符區隔各單項內容是很常見的,如何在 script 將這些內容切割出來使用是很重要的課題,需要使用到的是內建的 $IFS 這個變數。嘗試執行下面的 script 可以很容易理解 $IFS 的運作。
#!/bin/sh
set - $(IFS=" "; echo This is a book)
echo $1
echo $2
echo $3
echo $4

得到的結果是 $1="This", $2="is", $3="a", $4="book",所以 set - $(IFS=" "; echo This is a book) 就是把後面的字串用空白字元 " " 當作區隔,把切割後的內容放到 $1, $2...。
以下的範例是利用 $IFS 來尋找執行檔存在哪個 $PATH 資料夾內,我習慣取名為 findpath
if [ 1 -eq $# ]; then
    RESULT="not found!"
    FILENAME=$1
    set - $(IFS=":"; echo $PATH)
    i=1
    eval "DIR=\$$i"
    until [ "" == "$DIR" ]
    do
        if [ -x "$DIR/$FILENAME" ]; then
            RESULT="$DIR/$FILENAME"
            DIR=""
        else
            i=$(($i+1))
            eval "DIR=\$$i"
        fi
    done
    echo $RESULT
else
    echo "Syntax: $0 [filename]"
    echo "    filename: The filenam you want to find in PATH"
fi

應用在 for 迴圈

findpath 的另一個寫法,先把原本的 IFS 存到 IFS_SAVE,然後指定冒號 ":" 為切割符號,這時候 for DIR in $PATH 就會把 $PATH 用冒號將每個單位切出來使用。
if [ 1 -eq $# ]; then
    RESULT="not found!"
    IFS_SAVE="$IFS"
    IFS=":"
    for DIR in $PATH
    do
        if [ -x "$DIR/$1" ]; then
            RESULT="$DIR/$1"
            break
        fi
    done
    IFS="$IFS_SAVE"
    echo $RESULT
else
    echo "Syntax: $0 [filename]"
    echo "    filename: The filenam you want to find in PATH"
fi

IFS 的初始值

執行 set | grep IFS 可以看到目前的 IFS 設定值,在還沒變更之前就是初始值,一般是
IFS=$' \t\n'

bash 用變數內容當變數名稱取值

在很多情況下我們會遇到無法預知個數的變數群,而且變數名稱最後是採用數字來當作區別,因為是未知個數,所以要使用一個動態迴圈來處理,如何取得個別數值來處理變得很重要。參考下面的範例:
DNS1="8.8.8.8"
DNS2="8.8.4.4"
DNS3="0.0.0.0"
DNS4="139.175.150.20"
DNS5="168.95.1.1"
DNS_CNT=5
RESOLV_CONF="/etc/resolv.conf"
i=1
echo "; generated by my script" > $RESOLV_CONF
while [ $i -le $DNS_CNT ]
do
    eval "DNS_ADDR=\$DNS$i"
    if [ "0.0.0.0" != "$DNS_ADDR" ]; then
        echo "nameserver $DNS_ADDR" >> $RESOLV_CONF
    fi
done

最主要看 do 迴圈的第一行,eval "DNS_ADDR=\$DNS$i" ,因為 eval 會去執行它後面帶出來的那一行,而 \$ 會是 $ 符號,$i 則是目前 i 的值,因此當 i=1 時 "DNS_ADDR=\$DNS$i" 就會是 DNS_ADDR=$DNS1,透過 eval 執行,就會把 DNS1 的值帶給 DNS_ADDR 以利後面處理。

bash echo 顏色控制

語法

echo -e "\33[??;??;??;??m Message \33[0m"
-e 告訴 echo 要作控制
\33[??m 表示要作 ?? 的控制,每個 ?? 控制碼中間用 ; 隔開,最後用 m 當結尾,\33[0m 也是其中一種,而 0 表示恢最原始的狀態,以下列出 ?? 的控制說明

文字顏色

30    黑色
31    紅色
32    綠色
33    黃色
34    藍色
35    紫色
36    天藍色
37    白色

背景顏色

40    黑底
41    紅底
42    綠底
43    黃底
44    藍底
45    紫底
46    天藍底
47    白底

其他控制

0        關閉所有控制屬性
1        設置高亮度
4        底線
5        閃爍
7        反顯(前景背景屬性互換)
8        消除隱藏
2J       清除螢幕
?25l     隱藏游標
?25h     顯示游標
nA       游標上移 n 行
nB       游標下移 n 行
nC       游標右移 n 字元
nD       游標左移 n 字元
x;yH     游標移動到座標 (x,y)
K        清除游標到行尾的內容
s        保存游標位置
u        將游標恢復到之前用 s 保存的位置
...

2015/03/16

node.js 初探 event

先來看看下列程式 event.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// event.js

var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

setTimeout(function() {
 event.emit('event1');
}, 1000);

setTimeout(function() {
 event.emit('event2');
}, 2000);

event.on('event1', function() {
 console.log('1');
});

Line 3~4 建立一個新的 event
Line 6~8 對 event 設定一個 timeout 計時器,等待 1000 ms 之後觸發 'event1'
Line 10~12 對 event 設定一個 timeout 計時器,等待 2000 ms 之後觸發 'event2'
Line 14~16 當 event 的 'event1' 被觸發的時候,顯示 '1'

執行 node event.js ,一秒後因為觸發 event1 而顯示 1,兩秒後 'event2' 被觸發,但是沒有告訴 node.js 當 event2 觸發時要作什麼,所以沒事作。然後因為事件佇列沒有其他事件待處理而結束程式。

2015/03/04

node.js 筆記

Debian 7 安裝

apt-get install curl
curl -sL https://deb.nodesource.com/setup | bash -
apt-get install -y nodejs

開啟 HTTP 服務

//app.js
var http = require('http');
http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('<h1>Node.js</h1>');
  res.end('<p>Hello World</p>');
}).listen(80);
console.log("HTTP server is listen at port 80.");

執行

node app.js

不用每次關閉程式都重新執行

supervisor 會偵測 app.js 是否有變更,如果有變更會自動重啟
安裝 supervisor
npm install -q supervisor
執行
supervisor app.js

同步與非同步

同步讀取檔案
//readFileSync.js
var fs = require('fs'):
var content = fs.readFileSync('test.txt', 'utf-8');
console.log(content);
console.log('done.');
執行結果
I'm a test file.
done.
非同步讀取檔案
//readFile.js
var fs = require('fs');
fs.readFile('test.txt', 'utf-8', function(err, content) {
        if (err) {
                console.error(err);
        } else {
                console.log(content);
}
});
console.log('done.'); 
執行結果
done.
I'm a test file.
nde.js 的非同步運作是使用事件觸發 callback function 來達成,fs.readFile 登錄一個讀檔案的事件,當事件觸發時才執行後面寫的 callback function。所以 node.js 應該有個處理各事件的狀態機循環。利用這個機制,node.js 可以單一執行序完成多線運作,省下不少開執行序耗費的系統資源。

2015/03/03

x86 軟體逆向工程,以 x64_dbg 為例

什麼是逆向工程?不解釋。

需要什麼工具?反組譯工具,在 PC-based 上跑的程式,需要用 x86 反組譯程式,目前我在 Windows 上用過的三套 IDA pro freeollydbgx64_dbg,都是免費的,而其中 x64_dbg 還是 Open Source,也是我取得的軟體之中唯一支援 x86 64 bits 反組譯(IDA pro 6 也有支援,但是是收費版本),ollydbg 的 64 bits 支援還在穩定發展中。而 IDA pro free 可以解析資源檔,其他兩套則不行。

還需要什麼呢?初步的逆向工程不需要高深的 x86 組合語言能力,只要知道 call、ret 還有 j 系列的指令集,如 je、jmp...

本篇將以 x64_dbg 的 32 bit 版本 x32_dbg 來實做一次逆向工程,以某 shareware 為例而不是自己寫一個來操作。

x64_dbg 的操作就不贅述,本篇也不介紹比較進階的搜尋作為,只從程式進入點開始以苦力的方式進行逆向工程。

1.

首先安裝 shareware,然後執行,並觀察其執行流程。後續稱此程式為 A

執行時,出現對話窗,告知此軟體不是免費軟體,當按下 I agree 之後開始讀秒,3 秒讀完之後就進入程式了。

這個流程非常單純,這次逆向工程的目標也很單純,跳過這個宣告,直接進入程式。

2.

首先執行 x32_dbg,然後載入 A 程式,反組譯過的程式碼映入眼簾,多美麗啊!

3.

接著就是按住 [Shift] + [F8] 不斷地執行 step-over,[F8] step-over 跟 [F7] step-into 的差別在 [F8] 遇到 call 指令集時(function call),會完整執行該 call 回到下一個指令,而 [F7] 則會進入該 call 位址繼續執行。按住 [Shift] 則可以不理會 call 裡面的例外,要不然遇到例外是會中斷在例外點,那就壞了 step-over 的優勢了。

在這個 routine 的 step-over 進行的過程中,要特別留意 j 系列的指令沒有往回跳的情形,遇到這種狀況通常是迴圈,應該要設法直接跑到迴圈結束的地方,而不是 step-over 慢慢把迴圈跑完。

3.1

x64_dbg 的介面會標示  j 系列指令集的跳躍位置,執行到該指令時,該線條是紅色的就表示要跳躍,如果是深灰色的就表示不跳躍。

上圖是一個迴圈結構,從 0046C9AF 開始到 0046CA3E 跳回。
回圈內有以下跳出點
0046C9CA | JNB a.46CAAB                            |
0046CA1B | JE a.46CA43                             |
第一個跳出點比較遠,所以取出回圈起點 0046C9AF(D1)到第一個跳點目標 0046CAAB(D2)之間的所有程式出來看,要留意各個 J 的目標是不是介於 D1 跟 D2 之間
0046C9AF | MOV ECX,DWORD PTR SS:[EBP-A80]          |
0046C9B5 | ADD ECX,1                               |
0046C9B8 | MOV DWORD PTR SS:[EBP-A80],ECX          |
0046C9BE | MOV EDX,DWORD PTR SS:[EBP-A80]          |
0046C9C4 | CMP EDX,DWORD PTR SS:[EBP-A88]          |
0046C9CA | JNB a.46CAAB                            |
0046C9D0 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9D6 | CALL a.46F570                           |
0046C9DB | MOV DWORD PTR SS:[EBP-4],0              |
0046C9E2 | MOV EAX,DWORD PTR SS:[EBP+C]            |
0046C9E5 | PUSH EAX                                |
0046C9E6 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9EC | PUSH ECX                                |
0046C9ED | CALL a.46BE70                           |
0046C9F2 | ADD ESP,8                               |
0046C9F5 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046C9FB | CALL a.470870                           |
0046CA00 | ADD EAX,270                             |
0046CA05 | MOV DWORD PTR SS:[EBP-A8C],EAX          |
0046CA0B | MOV ECX,DWORD PTR SS:[EBP-A8C]          |
0046CA11 | CALL a.394830                           |
0046CA16 | MOVZX EDX,AL                            |
0046CA19 | TEST EDX,EDX                            |
0046CA1B | JE a.46CA43                             |
0046CA1D | MOV EAX,DWORD PTR SS:[EBP-A84]          |
0046CA23 | ADD EAX,1                               |
0046CA26 | MOV DWORD PTR SS:[EBP-A84],EAX          |
0046CA2C | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA33 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CA39 | CALL a.3EF400                           |
0046CA3E | JMP a.46C9AF                            |
0046CA43 | PUSH 0                                  |
0046CA45 | PUSH a.9DF040                           | ;9DF040:"64.14.19.49"
0046CA4A | MOV ECX,DWORD PTR SS:[EBP-A8C]          |
0046CA50 | CALL a.395150                           |
0046CA55 | CMP EAX,DWORD PTR DS:[9D3630]           |
0046CA5B | JE a.46CA83                             |
0046CA5D | MOV ECX,DWORD PTR SS:[EBP-A7C]          |
0046CA63 | ADD ECX,1                               |
0046CA66 | MOV DWORD PTR SS:[EBP-A7C],ECX          |
0046CA6C | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA73 | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CA79 | CALL a.3EF400                           |
0046CA7E | JMP a.46C9AF                            |
0046CA83 | PUSH 1                                  |
0046CA85 | LEA EDX,DWORD PTR SS:[EBP-A78]          |
0046CA8B | PUSH EDX                                |
0046CA8C | MOV ECX,DWORD PTR SS:[EBP+8]            |
0046CA8F | CALL a.470420                           |
0046CA94 | MOV DWORD PTR SS:[EBP-4],FFFFFFFF       |
0046CA9B | LEA ECX,DWORD PTR SS:[EBP-A78]          |
0046CAA1 | CALL a.3EF400                           |
0046CAA6 | JMP a.46C9AF                            |
0046CAAB | PUSH a.9DF2FC                           | ;9DF2FC:" records."
因為所有 J 系列指令的目標都在範圍內,所以這個迴圈可以封閉,我們可以放心的讓他跑完,直接到 0046CAAB 等他。方法是點擊 0046CAAB 把焦點轉到該位址,然後按下 [F4] run until selection,如此就不必耗費太多時間在這個迴圈內。

後面又遇到一個迴圈,比上面那個迴圈稍微複雜一點點
003EE21A | LEA ECX,DWORD PTR SS:[EBP-4]            |
003EE23F | JE a.3EE2A8                             |
003EE265 | JE a.3EE2A3                             |
003EE289 | JE a.3EE2A3                             |
003EE2A1 | JMP a.3EE2AA                            |
003EE2A3 | JMP a.3EE21A                            |
003EE2A8 | XOR AL,AL                               |
003EE2AA | MOV ESP,EBP                             |
迴圈中有兩個跳出點,一個到 3EE2A8,另一個到 3EE2AA,由此判斷 3EE2A8 不是必經途徑,而 3EE2AA 才是必經途徑,所以我們在 3EE2AA 等。同樣祭出 [F4] 伺候。

4.

注意注意!發現目標。
執行到 0081153E 這個 call 時,終於出現了對話窗,趕快先按下 [F2] 設定中斷點。然後觀察,跑完流程,連主程式都跑完,才回到 dbg 控制。所以下次要進入這個中斷點 call 的目標裡面去看。

這裡有一個方便記憶的工具,在該 call 加註釋會很好用,方法是點選該指令,按下 [;] 分號,就可以給他加註解了,因為是第一個,我給他加註 1st,後面會用到。

按下左上角的按鈕,重新載入。

這次直接按下 [F9] 執行,程式會直接跑到中斷點,然後千萬別按 [F8] ,而是按  [F7],因為我們要跑進去看了。

4.1

又發現一個目標。
同樣按下 [F2] 加入中斷點並加註,然後切換到中斷點頁籤,把上一個中斷點設定成 disable,這樣下次重啟後按下 [F9] 就會跳過前一個中斷點,直接跑到最後一個中斷點,不把他刪除是因為說不定還會用到。

繼續 routine 執行,要找到只關閉對話框,而還沒把主程式跑完的地方。

4.2

皇天不負苦力人,第三次就遇到出現對話窗,而還沒進入主程式,同樣設中斷點並加註。接下來要好好利用這個中斷點。

5.

重啟後,在這個中斷點我們故意給他跳過不執行,然後觀察程式的行為。到 command 執行 eip=中斷點的下一個位址,然後按下 [F9]。

真好運,直接進入主程式了。

6.

既然直接跳過對話窗還是進入主程式,那就直接把對話窗跳開吧。
在 4.2 的中斷點按下滑鼠右鍵,選擇 binary->Fill with NOP,就可以把 call 指令變成 NOP 了

然後點顯上方快速按鈕中間稍微偏右的出現 pach 對話框
點選右下角的 Patch File,對執行檔進行 patch,程式會問你要存成什麼檔名,就存成 B.exe 吧,執行 B.exe,直接就進入主畫面了。

7.

預期評估日期結束後,會有其他流程檢查日期,屆時可以再苦力一次。

8.

x64_dbg 的資料夾裡面有一個子資料夾叫做 [db],用來儲存 x64_dbg 的操作,包括中斷點跟註解,下次載入該執行檔時,就能直接使用,非常方便。裡面的檔案不要刪掉,評估日期結束後,會需要再用的。實際上 x64_dbg  有把定字串顯示出來,對程式流程控制有概念的話,在 4.2 那附近就能把評估日期也一併處理掉了,那會需要更多的經驗,這裡只做入門苦力,就不提了,而且幾乎每支程式都會不一樣,多提也沒什麼意思。

2015/01/29

ISC DHCP server 設定 static route (option 33, 121)

使用 Debian 的 ISC DHCP 套件,本身沒有提供 Option 121,不過幸運的是 ISC 提供自定義的功能,而且還挺方便的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ddns-update-style none;
default-lease-time 6000;
max-lease-time 72000;
authoritative;
 
option classless-static-routes code 121 = array of integer 8;
 
subnet 192.168.116.0 netmask 255.255.255.0 {
        range 192.168.116.10 192.168.116.19;
        option routers 192.168.116.254;
        option domain-name-servers 172.24.1.2;
 option static-routes 33.1.1.1 192.168.116.33
 option classless-static-routes 24, 172, 20, 3, 192, 168, 116, 3, 28, 172, 20, 4, 0, 192, 168, 116, 4;
}

Option 33 

Line 12,指定 static-routes,參數是兩個 IP,第一個是 DestIP,第二個是 Roter 的 IP,如範例所示是 33.1.1.1 要經由 192.168.116.33

Option 121 

需要自定義此 Option,如 Line 6
    名稱: classless-static-routes
    Option Code: 121
    參數內容: 8 個 Integer

設定範例如 line 13, 指定兩個 subnet 的 static route
    第一個是 172.20.3.0/24 走 192.168.116.3
    第二個是 172.20.4.0/28 走 192.168.116.4

2015/01/26

Linux 環境透過需要認證的 SMTP 寄信

近日同學公司的網頁找了一家新的設計公司設計,但是在留言板寄信功能出了點問題,如果寄到設計公司內部的信箱就可以收到,但是寄到同學公司的信箱會就收不到。最後發現是因為收信的信箱會對郵件來源進行網域反查,所以最終的解決方法是要真正登錄 SMTP 去寄發郵件,而不是單純 sendmail,所以寫這個筆記來記憶。

首先是 /etc/mail.rc 的設定,以下是簡單帳密登入的設定
set asksub append dot save crt=20
ignore Received Message-Id Resent-Message-Id Status Mail-From Return-Path Via
set from=寄件者信箱 smtp=寄件者的 SMTP 伺服器位址
set smtp-auth-user=登陸帳號 smtp-auth-password=登錄密碼
set smtp-auth=login
 
設定好了,那要怎麼寄信呢?在 shell 環境執行
echo "郵件內文" | mail -s "郵件主題" 收件者信箱