FSH – 助你接入私有网络中的 Linux 终端

Linuxer! 同事、朋友有技术问题需要你远程协助,可是双方计算机都连接在私有网络中怎么办?FSH 能助你接入私有网络中的 Linux 终端。

FSH 是采用服务器中转来穿透 NAT 的一种连接远程 Linux 终端的方案,服务端、客户端的源代码都是开放的,托管在 GitHub 上。

如何编译?

git clone git://github.com/heiher/hev-fsh
cd hev-fsh
git submodule init
git submodule update
make

如何使用?
公共转发服务:222.92.8.138:81

被控端

# 登录模式(root 用户运行)
bin/hev-fsh -s 222.92.8.138 -p 81
 
# 当前用户模式
bin/hev-fsh -s 222.92.8.138 -p 81
 
# 指定用户模块,如 nobody 用户(root 用户运行)
bin/hev-fsh -s 222.92.8.138 -p 81 -u nobody

主控端

bin/hev-fsh -s 222.92.8.138 -p 81 -c TOKEN

自建转发服务端

bin/hev-fsh -a 0.0.0.0 -p 端口
 
# 指定 LOG 文件
bin/hev-fsh -a 0.0.0.0 -p 端口 -l /var/log/fsh.log

Over!

How to write SCGI applications in Python and JavaScript

Download & Install HevSCGIServerLibrary

git clone git://github.com/hev-scgi/hev-scgi-server-library
cd hev-scgi-server-library
make
 
sudo cp bin/libhev-scgi-server.so /usr/lib64/
sudo cp gir/HevSCGI-1.0.gir /usr/share/gir-1.0/
sudo cp gir/HevSCGI-1.0.typelib /usr/lib64/girepository-1.0/

Python Demo
https://github.com/hev-scgi/hev-scgi-server-python

git clone git://github.com/hev-scgi/hev-scgi-server-python
cd hev-scgi-server-python
 
python3 src/main.py

JavaScript Demo
https://github.com/hev-scgi/hev-scgi-server-gjs

git clone git://github.com/hev-scgi/hev-scgi-server-gjs
cd hev-scgi-server-gjs
 
gjs -I src src/main.js

Over!

优化 ibus-table 性能

ibus-table 是 Python 语言实现的 ibus 输入法框架的码表引擎,ibus 的绝大多数形码输入法使用该引擎,如郑码、五笔等等。

使用过该引擎的用户或多或少有这样的感觉,就是反应慢,尤其在低性能的计算机上感觉格外明显。无需复杂的性能分析工具,仅用 top 命令就不难发现,用一个约有20万条记录的郑码码表,在连续输入中文时,ibus-table 进程的CPU使用率几乎100%,这还是在一个 i3 2.5GHz 的 PC 平台上。那么在一个更低性能的平台上,输入体验可以想象。

再通过 pref 工具跟踪可以发现,在连续输入时,大部分CPU使用主要来自于 sqlite 数据库查询操作和候选词排序。在阅读引擎代码后,我做了一个实验,在 tabsqlitedb.py 的 select_words 中删除掉 sqlite 查询与排序,返回静态记录。结果CPU使用率降低到了只有5%左右。

因此,给 select_words 加个缓存应该是个不错的选择,就用 HashMap 来实现这个缓存,按键输入作为 key,排序后的结果作为 value。结果很明显,连接输入中文时的进程CPU使用率从几乎100%下降到了10%。

已经合并至 1.9.18: https://github.com/kaio/ibus-table/releases/tag/1.9.18

Over!

System V AMD64 ABI calling conventions

The calling convention of the System V AMD64 ABI is followed on Solaris, Linux, FreeBSD, Mac OS X, and other UNIX-like or POSIX-compliant operating systems. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments. For system calls, R10 is used instead of RCX. As in the Microsoft x64 calling convention, additional arguments are passed on the stack and the return value is stored in RAX.

Registers RBP, RBX, and R12-R15 are callee-save registers; all others must be saved by the caller if they wish to preserve their values.

Unlike the Microsoft calling convention, a shadow space is not provided; on function entry, the return address is adjacent to the seventh integer argument on the stack.

Over!

Linux 不使用 chroot 临时替换C运行时库

有些时候我们需要在老旧的 Linux 系统上运行一些依赖于较新版本 C 库的应用程序或库,应用程序会因为系统中安装的C库缺少符号还启动失败。解决方法之一就是临时替换使用非系统安装的C运行时库。使用临时C库需要做些什么配置及会带来哪些问题呢?

配置步骤
1. 下载与目标应用程序版本相匹配的临时C库,解压缩到临时位置 A。
2. 需要设置 LD_LIBRARY_PATH 环境变量指向目标临时C库的存储位置 A。
3. 需要通过与临时C库匹配的 ld.so 启用应用程序。因为应用程序默认是链接了一个绝对路径的 ld.so,如 x86_64 是 /lib64/ld-linux-x86-64.so.2

衍生问题
使用临时C库的 ld.so 启动的应用程序执行系统标准命令的子进程出错,原因是因为环境变量 LD_LIBRARY_PATH 被子进程继承,从而导致子进程在执行系统C库的ld.so中加载了版本不匹配的临时C库。

解决办法
在合适的时机清除环境变量 LD_LIBRARY_PATH,最合适的时机应用就是执行目标应用程序 main 函数之前啦。这里又要用到了之前写过的方法 => Linux 平台一种进程代码注入方法

/* fakemain.c
 * Heiher <admin@heiher.info>
 */
 
#include <stdio.h>
#include <stdlib.h>
 
#define __USE_GNU
#include <dlfcn.h>
 
int
__libc_start_main(int (*main)(int, char **, char **),
			int argc, char **ubp_av, void (*init)(void),
			void (*fini)(void), void (*rtld_fini)(void),
			void (*stack_end))
{
	int (*__libc_start_main_real)(int (*main) (int, char **, char **),
				int argc, char **ubp_av, void (*init)(void),
				void (*fini)(void), void (*rtld_fini)(void),
				void (*stack_end));
 
	unsetenv ("LD_PRELOAD");
	unsetenv ("LD_LIBRARY_PATH");
 
	__libc_start_main_real = dlsym(RTLD_NEXT, "__libc_start_main");
 
	return __libc_start_main_real(main, argc, ubp_av, init, fini,
				rtld_fini, stack_end);
}
gcc -fPIC -O3 -shared -o libfakemain.so fakemain.c -ldl

设置环境变量 LD_PRELOAD=/xxx/libfakemain.so,运行目标应用程序在执行 main 之前即会清除 LD_PRELOAD 和 LD_LIBRARY_PATH 变量。

为了方便使用我还写了个 wrapper,使用方法是将真实的目标应用程序 xxx 重命令为 xxx.bin,然后创建个符号链接 xxx 指向 wrapper,执行时直接执行 xxx,wrapper 会自动设置所需要的环境变量。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
int
main (int argc, char *argv[])
{
	int i;
	char buf[1024], path[1024];
	char *str, *root, *args[512];
 
	/* get FAKE_ROOT */
	root = getenv ("FAKE_ROOT");
	if (!root) {
		fprintf (stderr, "Please set environment FAKE_ROOT!\n");
		return -1;
	}
 
	/* export PATH */
	str = getenv ("PATH");
	if (!str) {
		fprintf (stderr, "Get environment PATH failed!\n");
		return -2;
	}
	if (NULL == strstr (str, root)) {
		strcpy (buf, root);
		strcat (buf, "/bin:");
		strcat (buf, str);
		if (0 != setenv ("PATH", buf, 1)) {
			fprintf (stderr, "Set environment PATH failed!\n");
			return -3;
		}
	}
 
	/* export LD_PRELOAD */
	strcpy (buf, root);
	strcat (buf, "/lib64/libfakemain.so");
	if (0 != setenv ("LD_PRELOAD", buf, 1)) {
		fprintf (stderr, "Set environment LD_PRELOAD failed!\n");
		return -4;
	}
 
	/* export LD_LIBRARY_PATH */
	strcpy (buf, root);
	strcat (buf, "/lib64");
	if (0 != setenv ("LD_LIBRARY_PATH", buf, 1)) {
		fprintf (stderr, "Set environment LD_LIBRARY_PATH failed!\n");
		return -5;
	}
 
	/* set new path */
	strcpy (path, root);
	strcat (path, "/lib64/ld-2.20.so");
	args[0] = path;
 
	/* set real program path */
	strcpy (buf, root);
	strcat (buf, "/bin/");
	strcat (buf, argv[0]);
	strcat (buf, ".bin");
	args[1] = buf;
 
	/* copy arguments */
	for (i=1; i<argc; i++)
	      args[i+1] = argv[i];
	args[i+1] = NULL;
 
	/* run real program */
	return execv (path, args);;
}
gcc -O3 -o wrapper wrapper.c

Over!

mips64el toolchain for x86_64

mips64el toolchain 是用于在 x86_64 平台交叉编译 mips64el 目标程序的工具集,该工具集分为两种大版本:odd-spreg 和 no-odd-spreg,其中龙芯仅适用 no-odd-spreg 版本。系统库包含 mips64el o32, n32 和 n64 多种版本的库,分别有依赖于 Linux 2.6 内核和 Linux 3.4 内核的两种版本。另外还有支持 x86_64 交叉编译 Mozilla JS 引擎的支持包。

下载
Source: mips64el-toolchain-2.src.tar.xz
no-odd-spreg
toolchain: mips64el-toolchain-2.x64.tar.xz
system libaries (Linux 2.6): mips64el-toolchain-linux-2.6-2.x64.tar.xz
system libaries (Linux 3.4): mips64el-toolchain-linux-3.4-2.x64.tar.xz
system libaries (Linux 3.4 MozJS): mips64el-toolchain-linux-3.4-mozjs-2.x64.tar.xz
odd-spreg
toolchain: mips64el-toolchain-2.x64.tar.xz
system libaries (Linux 2.6): mips64el-toolchain-linux-2.6-2.x64.tar.xz
system libaries (Linux 3.4): mips64el-toolchain-linux-3.4-2.x64.tar.xz

安装

sudo tar --numeric-owner -xf xxxx -C /

配置
设置环境变量

export PATH=${PATH}:/opt/mips64el-toolchain/bin

切换系统库

sudo ln -s -f linux-2.6 /opt/mips64el-toolchain/platforms/current

编译

# MIPS32 o32
mips64el-unknown-linux-gnu-gcc -march=mips32r2 -mabi=32 -o test test.c
# MIPS64 n32
mips64el-unknown-linux-gnu-gcc -march=mips64r2 -mabi=n32 -o test test.c
# MIPS64 n64
mips64el-unknown-linux-gnu-gcc -march=mips64r2 -mabi=64 -o test test.c

Over!

GCC 预定义宏: Endian

#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
// little endian
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
// big endian
#elif (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__)
// pdp endian, The bytes in 16-bit words are laid out in
// a little-endian fashion, whereas the 16-bit subwords of
// a 32-bit quantity are laid out in big-endian fashion. 
#endif

Over!

Run command ‘su’ in PHP

升级 VPS 后,由于 Ubuntu 的 upstart 与 OpenVZ 的兼容问题,导致 sshd 服务不自动启动了,在尝试了 vePortal 的 console 与 file manager 及提交技术支持后都不能解决问题之后。

只能靠自己了,大概的思路是在 PHP 中进行 su 命令以执行 sshd 服务,因为 WordPress 还活着,并且可以在后台直接编辑主题相关的 PHP 脚本。只要把准备好的代码片断插入到 header.php 中,并在浏览器中访问一下主页即可。

相关的代码逻辑
1. 使用 PHP 的 proc_open 打开一个进程,重定向 stdin, stdout, stderr, 这里会执行一个 python 程序。
2. 在这个 python 程序中打开一个 pty,并运行一个 sh。
3. 利用步骤 1 中重定向的 stdin pipe 向 python 程序发送 su 命令, python 会将来自 stdin 的命令数据写到入 ptmx,而这时 sh 的 stdin, stdout 及 stderr 是重定向到与 python 打开的 ptmx 配对的 pts 上的。也就是说 su 命令最终会转给 sh 进程处理。
4. sh 进程自然的执行了 su 命令,这时 su 进程的 stdin, stdout, stderr 也会被重定向到那个 pts 上。
5. 在 sleep 一段时间后(主要是等 su 真的跑起来了),再写入密码,数据流过程与步骤 3、4 一致。

相关的代码片断

<?php
  $descriptorspec = array(
    0 => array("pipe", "r"),  // stdin
    1 => array("pipe", "w"),  // stdout
    2 => array("pipe", "w")   // stderr
  );
  $process = proc_open("python -c 'import pty; pty.spawn(\"/bin/sh\")'", $descriptorspec, $pipes);
  if (is_resource($process)) {
    fwrite($pipes[0], "su -c 'service ssh start' root\n");
    fflush($pipes[0]);
    sleep(3);
    fwrite($pipes[0], "PASSWORD\n");
    fflush($pipes[0]);
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
  } 
?>

Over!