分類
Linux

Termux Python3.6安裝paramiko

之前寫過一個python腳本通過paramiko使用SSH鏈接越獄後的iPhone執行一些安裝軟件或重啟SpringBoard之類的操作。後來遇到Termux覺得安卓手機也可以試試,不試不知道,一試坑不少,好在最後還是裝好了。

#首先安裝系統的依賴
apt install libffi-dev clang libsodium libsodium-dev openssl-dev libcrypt-dev python-dev
#然後安裝pynacl,直接pip裝會報錯,指定使用系統的sodium庫即可
SODIUM_INSTALL=system
pip install pynacl
#最後再安裝paramiko和python-nmap
pip install paramiko python-nmap

本文更新於 2018/04/04。

分類
方法

root後的android /system仍然只讀?

前兩天寫了個腳本通過https://freeapi.ipip.net/獲取IP信息,在公司電信網絡沒問題,在家裡有時可以有時不行。對比dns後發現獲得的ip是不同的,於是打算在hosts文件里寫死。安卓的hosts文件在/system/etc/hosts,但是在/etc/hosts也有鏈接,獲取Termux已經在root用戶下,仍然提示文件只讀。搜索後發現我的android6用的可能是system-headless root,也就是/system是已只讀方式掛載的,這樣或許可以保護系統吧。解決辦法在How to edit 'etc/hosts' file?找到:

#for Android 6
su # become the root (don't miss confirmation request!)
mount -o remount,rw /system # allow to write
vi /system/etc/hosts ## edit the file in place - do what you want, then <ESC>:wq ##
mount -o remount,ro /system # get things back to normal
exit # unroot


#for Android 8 with magisk and busybox
su # become the root (don't miss confirmation request!)
busybox mount -o remount,rw /system # allow to write
vi /system/etc/hosts ## edit the file in place - do what you want, then <ESC>:wq ##
busybox mount -o remount,ro /system # get things back to normal
exit # unroot

#for Android 10 
#remount /system may get this error
#mount: '/system' not in /proc/mounts
#just remount the root dir / will be ok

su # become the root (don't miss confirmation request!)
mount -o remount,rw / # allow to write
vi /system/etc/hosts ## edit the file in place - do what you want, then <ESC>:wq ##
mount -o remount,ro /system # get things back to normal
exit # unroot

後來呢,發現有時還時不行,所以不單單是dns的問題。再後來,發現ip-api家的免費接口也很不錯,現在兩個一起用。

本文更新於 2021/07/13。

分類
程序

時間比較




網頁版使用了moment.js。家裡的菲利普電飯鍋的預約功能需要輸入幾個小時後做好飯,而不是幾點幾分做好飯,每次都要扳着指頭數距離明天早上7點還有幾個小時。下面還有一個python版,可以放到termux里跑:

#!/data/data/com.termux/files/usr/bin/python
# -*- coding: utf8 -*-

#時間比較
import sys,datetime

#str轉換成datetime
def strTodaTime(s):
    if len(s)==19:
        return datetime.datetime(int(s[0:4]),int(s[5:7]),int(s[8:10]),
                                      int(s[11:13]),int(s[14:16]),int(s[17:19]))
    else:
        print("格式錯誤,--help查看幫助。")
        
#計算時間
def showTimeDuration(dtStr2='',dtStr1=''):
    if dtStr1 == '':
        datetime1 = datetime.datetime.now()
    else:
        datetime1 = strTodaTime(dtStr1)
    
    if dtStr2 == '':
        date2 = datetime.date.today() + datetime.timedelta(days=1)    
        time2 = datetime.time(7, 0)
        datetime2 = datetime.datetime.combine(date2, time2)
    else:
        datetime2 = strTodaTime(dtStr2)
        
    print(datetime2 - datetime1)

if __name__ == '__main__':
    if len(sys.argv) == 3 :
        showTimeDuration(sys.argv[1],sys.argv[2])
    elif len(sys.argv) == 2 :
        if sys.argv[1]=='--help':
            print('參數1默認是第二天早上七點,如2017-11-16T07:00:00。\n參數2默認是當前時間,格式同參數一。')
        else:
            showTimeDuration(sys.argv[1])
    else:
        showTimeDuration()
分類
方法

從深圳南山去香港趕早班飛機

香港是國際大都市,擁有更多的航線和更廉價的航班,所以我們出國基本都從香港走。下面是一些有用的網站:

  • 香港乘車易,官方提供的公共交通查詢網站,好用。
  • B3X,從深圳灣口岸到屯門市中心,首班車06:45,標稱15分鐘一班,高峰期經常彈性增加班次,票價11元,約24分鐘。
  • B3,從深圳灣口岸經屯門市中心到屯門碼頭,首班車06:55,20或30分鐘一班,票價11元,到屯門市中心約20分鐘,到屯門碼頭約40分鐘。
  • 富裕小輪時刻表,從屯門碼頭到東涌,通常25分鐘18元。
  • S56,從東涌碼頭到機場,首班車05:50,通常15分鐘一班,票價3.8元,約15分鐘。
  • E33,屯門市中心到機場,首班車05:00,十分鍾左右一班,票價13.9元,約70分鐘。還有一個A33,也是從屯門市中心到機場,票價27.7元,約80分鐘。

所以既然是趕飛機的話,早上需要約個出租車以在06:30到達關口,然後趕上06:45或06:55去屯門市中心的巴士,然後約07:20從屯門市中心坐E33去機場,加20分鐘堵車時間(有遇到過),09:00到達機場。如果飛機是10:00的,應該是沒問題的。

如果坐到屯門碼頭坐船,07:40到碼頭,趕上08:00的船,08:30到東涌碼頭,轉S56(或坐出租車(記得給小費))差不多也是09:00到達機場。

其實還有一種辦法,可以趕上更早的飛機,就是從蛇口碼頭直接坐船到香港機場,無奈搜了半小時,找不到蛇口碼頭的網站,只知道票價較貴,時間很短。

分類
方法

python selenium 常用命令記錄

記錄一些常用命令(待更新)。

from selenium import webdriver
#等待
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
#鍵盤
from selenium.webdriver.common.keys import Keys
#select元素
from selenium.webdriver.support.ui import Select

##配置並開啟火狐
firefox_profile = webdriver.FirefoxProfile()
#語言設置為zh-CN,en-US
firefox_profile.set_preference('intl.accept_languages','zh-CN')
#UA設置為iphone6 plus
firefox_profile.set_preference("general.useragent.override", "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1")
#2關閉瀏覽器圖片
firefox_profile.set_preference('permissions.default.image', 2)
driver = webdriver.Firefox(firefox_profile=firefox_profile)  
#窗口大小
driver.set_window_size(414,736) 
#窗口最大化,可解決一些元素因窗口太小為顯示而無法點擊的情況
driver.maximize_window()
#頁面超時
driver.set_page_load_timeout(50)

##給火狐設置 http 或 socks 代理
firefox_profile = webdriver.FirefoxProfile()
proxy_ip = "127.0.0.1"
proxy_port = 18409
firefox_profile.set_preference("network.proxy.type", 1)
#http代理需要設置下面四行
firefox_profile.set_preference("network.proxy.http", str(proxy_ip))
firefox_profile.set_preference("network.proxy.http_port", int(proxy_port))
firefox_profile.set_preference("network.proxy.ssl", str(proxy_ip))
firefox_profile.set_preference("network.proxy.ssl_port", int(proxy_port))
#socks 代理只需設置下面兩行
firefox_profile.set_preference("network.proxy.socks", str(proxy_ip))
firefox_profile.set_preference("network.proxy.socks_port", int(proxy_port))
#可選
firefox_profile.set_preference("network.http.use-cache", False)
#可選
firefox_profile.update_preferences()
driver = webdriver.Firefox(firefox_profile=firefox_profile)
#在無界面模式下運行 headless
from selenium.webdriver.firefox.options import Options as FirefoxOptions
options = FirefoxOptions()
options.add_argument("--headless")
driver = webdriver.Firefox(firefox_profile=firefox_profile,options=options)

##driver的完整初始化參數
webdriver.Firefox(firefox_profile=None, firefox_binary=None,
capabilities=None, proxy=None,
executable_path=DEFAULT_EXECUTABLE_PATH, options=None,
service_log_path=DEFAULT_SERVICE_LOG_PATH,
service_args=None, service=None, desired_capabilities=None,
log_path=DEFAULT_LOG_PATH, keep_alive=True)
#https://github.com/SeleniumHQ/selenium/blob/trunk/py/selenium/webdriver/firefox/webdriver.py#L44
##頁面操作
#打開頁面
try:
    driver.get("https://reportaproblem.apple.com")
except:
    try:
        #長時間沒加載完頁面時,可能是卡在了某個資源,按下Esc停止等待
        driver.find_element_by_xpath('//body').send_keys(Keys.ESCAPE)
    except:
        return 'fail'

#有時鏈接會打開新窗口,切換到新窗口
driver.switch_to_window(driver.window_handles[-1])
#當前頁面鏈接
driver.current_url
#當前頁面標題
driver.title

#等待元素出現
try:
    WebDriverWait(driver, 60).until(lambda the_driver: the_driver.find_element_by_xpath("//iframe[@id='aid-auth-widget-iFrame']").is_displayed())
except:
    pass
##定位元素
#frame
driver.find_element_by_xpath("//iframe[@id='aid-auth-widget-iFrame']")
#ID
driver.find_element_by_id("appleId")
#根據屬性定位元素
captchaInput = driver.find_element_by_xpath("//input[@placeholder='键入图中的字符']")
#獲取輸入框中的值
inputValue = captchaInput.get_attribute('value')
#向輸入框中寫入字符
captchaInput.send_keys("你好")
#清空輸入框
captchaInput.clear()
#按下Tab鍵
driver.find_element_by_xpath('//body').send_keys(Keys.TAB)

capDivImg=driver.find_element_by_xpath("//div[@class='form-cell']//img")
#獲取圖片的鏈接
imgSrc=capDivImg.get_attribute('src')

#點擊包含“繼續”的按鈕
driver.find_element_by_xpath("//button[contains(text(),'继续')]").click()
#根據CSS定位button
driver.find_element_by_xpath("//button[@class='button button-primary last nav-action']")

#點擊值為“步行”的選項
driver.find_element_by_xpath("//option[text()='步行']").click()

#定位一組元素
labels = driver.find_elements_by_css_selector("label.labelClass")
answerInputs = driver.find_elements_by_xpath("//input[@placeholder='答案']")
questions = []
for label in labels:
    questions.append( label.get_attribute('innerHTML') )

#等待包含特定字符的元素
WebDriverWait(driver, 3).until(lambda the_driver: the_driver
           .find_element_by_xpath("//p[@class='subtitle content-item tk-label'][contains(text(),'已超时')]")
           .is_displayed())

#獲取select元素
typeS = Select(driver.find_element_by_id("searchDropdownBox"))
#根據值來選中select元素
typeS.select_by_visible_text('行山')
#獲取元素顯示的文字
driver.find_element_by_xpath("//tr[@class='updateblock']//td[@class='timer']").get_attribute("innerText")
driver.find_element_by_xpath("//tr[@class='updateblock']//td[@class='timer']").get_attribute("innerHTML")

命令行啟動瀏覽器

firefox -width 1440 -height 960
google-chrome --window-size="1440,960"

本文更新於 2024/09/01。

分類
方法 软件

在安卓手機上使用xprivacy保護隱私

使用xprivacy需要root權限和xposed框架。獲取root權限和安裝xposed框架請參考https://forum.xda-developers.com/

以下內容機翻自yannick.12的貼子,原文地址https://forum.xda-developers.com/showpost.php?p=68744809&postcount=7

讓我們舉一個簡單的例子, 比如位置。

首先, 讓我們假設第二列是未選中的。這是最容易理解的情況。然後會發生什麼取決於第一列。

第一列-如果它選中, 那麼xprivacy將始終拒絕訪問的位置, 並把xprivacy里設置的假冒資訊給應用程式。

然而, 如果第一列沒有被選中, 那麼應用程式將能夠獲取您的實際位置。

這就是你對一個應用程式權限做出的回答, 它的答案是 "可以使用這個許可權嗎?" 總是相同的 (無論是 "總是" 還是 "從不")。第二列未選中, 第一列選項告訴應用程式是或否。

第二列控制您在xprivacy中看到的彈窗。如果選中第二列, 您將得到一個彈窗, 詢問是否允許該應用程式獲得許可權 (是否選中了第一列)。

彈窗有四種情況-"允許", "拒絕", "不知道", "哎呀我超時"。

"哎呀, 我超時了" 將給app第一列的答案。

如果您在彈窗中按一下 "允許", 然後 xprivacy 取消勾選設置中的第二列, 再取消勾選第一列, 並讓應用程式訪問您的真實位置。除非重新勾選 xprivacy 設置中的第二列, 否則彈窗不會再次出現。

如果你點擊 "拒絕" 然後 xprivacy 取消勾選第二列, 勾選第一列並給應用程式假位置。然後, 你將不會看到彈出。

如果您按一下 "不知道", 那麼我認為 * xprivacy 拒絕訪問 (無論第一列是否未選中), 並將第二列保留為選中狀態, 因此下次將再次詢問。

我怎麼發現的?嗯, 我不是從常見問題解答中讀到的!我昨天剛剛下載 xprivacy , 我發現它令人難以置信的困難。最後, 我下載了一個應用程式, 它顯示出你的 gps 位置, 沒有別的, 我只是測試。以上是我的總結。我希望它能幫助別人, 因為我希望我昨天讀過這個帖子。

請注意, 其他許可權的工作方式可能稍有不同。例如, 它不可能完全虛擬一整套互聯網資訊。您可以通過查看 xprivacy 設置來快速瞭解哪些資料可以偽造。例如, 你可以偽造你的電話號碼和你的 MAC 位址。但正如我已經說過, 你不能假冒你的互聯網, 你也不能偽造你的存儲-這是一個很好的主意, 因為如果你假裝讓一個應用程式寫到您的 SD 卡, 然後假裝讓它讀它, 它不能找到它剛剛寫過的東西, 這勢必會導致麻煩, 可能會比你剛拒絕它的時候更麻煩。


2021年更新:由於維護一個安全的 root 過的安卓系統越來越困難,而我也隨著年齡的增加而喪失了一部分好奇心,所以我現在使用開源應用 Shelter 來將自己不信任的應用隔離到獨立的區域。Shelter 雖不如 xprivacy 功能強大,但對於基本的隱私防護(通訊錄、地理位置、手機內的文件資料、避免應用後台運行)是沒有問題的。安裝使用也非常簡單方便,推薦大家試用。

本文更新於 2021/02/05。

分類
程序

使用xtrabackup備份mysql數據庫

本文主要記錄下使用percona xtrabackup 2.4來備份mysql數據。最終效果是實現mysql所在主機定時進行全量備份和增量備份並自動刪除舊備份文件。另有一台數據存儲機,定時通過ssh抓取各個mysql主機上的備份文件,保存更長時間後刪除舊文件。

用到的軟件和技術有:xtrabackup(免費的mysql備份程序),qpress(xtrabackup壓縮需要的依賴,據說壓縮比很高),python3.5(ssh的遠程抓取,因為我不會bash)。

mkdir -p /root/42/script
cd /root/42/script
#安裝qpress
wget http://www.quicklz.com/qpress-11-linux-x64.tar
tar xf qpress-11-linux-x64.tar
mv qpress /usr/local/bin/
#centos6安裝xtrabackup
yum install http://www.percona.com/downloads/percona-release/redhat/0.1-4/percona-release-0.1-4.noarch.rpm
yum install libev -y
yum install percona-xtrabackup-24 -y
#新建backup.sh(來源未知)內容如下:
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
 
BACKUP_BASE_DIR="/data/backup/xtrabackup"
INC_BASE_LIST="${BACKUP_BASE_DIR}/inc_list.txt"
XTRABACKUP_PATH="/usr/bin/innobackupex"
 
MYSQL_CNF="/etc/my.cnf"
MYSQL_HOSTNAME=127.0.0.1
MYSQL_USERNAME=root
MYSQL_PASSWORD=""
 
LOCK_FILE="/tmp/innobackupex.lock"
THREAD=3
 
mkdir -p ${BACKUP_BASE_DIR}
CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M)"
[[ -d ${CURRENT_BACKUP_PATH} ]] && CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M-%S)"
 
print_help(){
    echo "--------------------------------------------------------------"
    echo "Usage: $0 full | inc | help               "
    echo "--------------------------------------------------------------"
    exit 1
}
 
[[ $# -lt 1 || "$1" == "help" ]] && print_help
 
[[ -f "$LOCK_FILE" ]] && echo -e "Usage: rm -f $LOCK_FILE\nUsage: chattr -i $LOCK_FILE && rm -f $LOCK_FILE" && exit 1
 
FullBackup(){
    touch $LOCK_FILE
    chattr +i $LOCK_FILE
    local rc=0
    ${XTRABACKUP_PATH} \
    --defaults-file=${MYSQL_CNF} \
    --user=${MYSQL_USERNAME} \
    --password=${MYSQL_PASSWORD} \
    --host=${MYSQL_HOSTNAME} \
    --compress \
    --compress-threads=${THREAD} \
    --parallel=${THREAD} \
    --no-timestamp ${CURRENT_BACKUP_PATH} > ${CURRENT_BACKUP_PATH}_full.log 2>&1
    grep ".*\ completed\ OK\!" ${CURRENT_BACKUP_PATH}_full.log > /dev/null 2>&1
    if [ $? -ne 0 ];then
        rc=1
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && rm -rf ${CURRENT_BACKUP_PATH}
    else
        echo "NULL|${CURRENT_BACKUP_PATH}|full" >> ${INC_BASE_LIST}
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && chattr +i ${CURRENT_BACKUP_PATH} || rc=1
    fi
    chattr -i ${LOCK_FILE}
    rm -f $LOCK_FILE
    chattr +a ${INC_BASE_LIST}
    return $rc
}
 
IncBackup(){
    touch $LOCK_FILE
    chattr +i $LOCK_FILE
    local rc=0
    PREV_BACKUP_DIR=$(sed '/^$/d' ${INC_BASE_LIST} | tail -1 | awk -F '|' '{print $2}')
    ${XTRABACKUP_PATH} \
    --defaults-file=${MYSQL_CNF} \
    --user=${MYSQL_USERNAME} \
    --password=${MYSQL_PASSWORD} \
    --host=${MYSQL_HOSTNAME} \
    --compress \
    --compress-threads=${THREAD} \
    --no-timestamp --incremental ${CURRENT_BACKUP_PATH} \
    --incremental-basedir=${PREV_BACKUP_DIR} > ${CURRENT_BACKUP_PATH}_inc.log 2>&1
    grep ".*\ completed\ OK\!" ${CURRENT_BACKUP_PATH}_inc.log > /dev/null 2>&1
    if [ $? -ne 0 ];then
        rc=1
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && rm -rf ${CURRENT_BACKUP_PATH}
    else
        echo "${PREV_BACKUP_DIR}|${CURRENT_BACKUP_PATH}|inc" >> ${INC_BASE_LIST}
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && chattr +i ${CURRENT_BACKUP_PATH} || rc=1
    fi
    chattr -i ${LOCK_FILE}
    rm -f $LOCK_FILE
    chattr +a ${INC_BASE_LIST}
    return $rc
}
 
## 全量備份
if [ "$1" == "full" ];then
    FullBackup
fi
 
## 增量備份
if [ "$1" == "inc" ];then
    ## 若全量備份不存在,則執行全量備份
    if [[ ! -f ${INC_BASE_LIST} || $(sed '/^$/d' ${INC_BASE_LIST} | wc -l) -eq 0 ]];then
        FullBackup
    else
        IncBackup
    fi
fi
 
## 刪除22天前的備份
if [[ -d ${BACKUP_BASE_DIR} && $(pwd) != "/" ]];then
    find ${BACKUP_BASE_DIR} -name "$(date -d '22 days ago' +'%F')_*" | xargs chattr -i
    find ${BACKUP_BASE_DIR} -name "$(date -d '22 days ago' +'%F')_*" | xargs rm -rf
fi

#可能需要修改backup.sh中的如下參數
BACKUP_BASE_DIR="/data/backup/xtrabackup"
XTRABACKUP_PATH="/usr/bin/innobackupex"
 
MYSQL_CNF="/etc/my.cnf"
MYSQL_HOSTNAME=127.0.0.1
MYSQL_USERNAME=root
MYSQL_PASSWORD=""
#給腳本添加執行權限
chmod 755 /root/42/script/backup.sh
#执行一次全量备份
/root/42/script/backup.sh full
#如果沒有錯誤信息,就可以看到BACKUP_BASE_DIR下生成了備份文件
#檢查下cron是否啟動
service crond status
#如果未啟動則將cron設置為開機啟動並手動啟動下
chkconfig crond on
service crond start
#添加定時任務
crontab -e
#在底部新增內容
## 每周六凌晨3:30一次全量备份
## 每周二、四、日的凌晨3:30点执行增量备份
30 3 * * 6 /root/42/script/backup.sh full
30 3 * * 2,4,7 /root/42/script/backup.sh inc

本文更新於 2017/09/25。