使用ld的wrap选项替换已有库函数

很多时候,可能需要替换已有库函数,或者对其库函数进行修改。为了避免对那些静态链接库或者动态链接库文件大动干戈,我们可以使用ld提供的–wrap选项。

例如,想把所有的malloc函数都作修改,以便让malloc出的内存都是32字节对齐的。我们可以给ld传选项“­­wrap=malloc”, 告诉ld,我们将替换名称为malloc的函数。接着再如下定义一个新的malloc函数:

void * __wrap_malloc( size_t size) {
  void* result;
  result=memalign(32, size);
  return result;
}

可以看到,程序中有个类似库函数名称的__wrap_malloc。ld在遇到__wrap选项后,会使用__wrap_malloc函数名替换所有对malloc的调用。并以此实现替换的作用。

那麽,如果还向调用原函数怎么办呢?可以使用__real_malloc,这个函数名称就对应了原来的malloc。每次调用malloc时都打印生成的指针地址。

void * __wrap_malloc( size_t size) {
  void* result;
  result= __real_malloc( size);
  printf("Malloc: allocate %d byte mem, start at %p", size, result);
 
  return result;
}

附完整程序:

#include <stdio.h>
#include <stdlib.h>
 
void * __wrap_malloc( size_t size) {
  void* result;
  //result= __real_malloc( size);   result = memalign(32, size);
  printf("Malloc: allocate %d byte mem, start at %p", size, result);
 
  return result;
}
 
int main ()
{
  int i,n;
  char * buffer;
 
  printf ("How long do you want the string? ");
  //scanf ("%d", &i);   i = 200;
 
  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);
 
  for (n=0; n<i; n++)
    buffer[n]=rand()%26+'a';
  buffer[i]='\0';
 
  printf ("Random string: %s\n",buffer);
  free (buffer);
 
  return 0;
}

编译选项:

gcc test_malloc.c -Wl,--wrap=malloc

From: http://www.lingcc.com/2011/12/23/11907/
Over!

使用 RInput 实现电脑键盘鼠标操作 Android 手机

RInput 实现了两台 Linux 主机之间共享输入设备(只要是 event device 都行),它通过UDP/IP 传输数据。Android 只是使用的 Linux 内核,理论上可以使用 RInput。

二进制程序
http://heiher.info/sftp/files/rinput/

依赖条件

  1. Android 有 root 权限。
  2. Android 内核编译了 uinput 支持(存在 /dev/uinput 设备文件)。

使用方法
可通过 USB Tether 或 Wifi 建立网络连接,然后分别在手机和PC上运行服务端和客户端。
Server (Android):

/data/local/rinputd 192.168.42.129 8080

Client (PC):

/data/local/rinput 192.168.42.129 8080 \
    /dev/input/event0 /dev/input/event1 # (实际的 event 设备)

启动完成后就可以通过 Pause 键盘切换控制权了。

Over!

Android NativeActivity samples crashes

Log

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.
native_activity/android.app.NativeActivity}: java.lang.IllegalArgumentException
: Unable to find native library: native-activity

Answer

cd native-activity
 
ant clean
ndk-build clean
 
mkdir src
sed -i 's/hasCode="false"/hasCode="true"/g' AndroidManifest.xml
 
ndk-build
ant debug

Over!

Linux 输入设备共享程序: RInput

是否在调试中因频繁切换 Target 和 Host 的输入设备而感觉头大?是否因为 Target 主机的键盘不适应还感到不爽?RInput
帮你解决这个问题,它可以通过定义的一个按键来将 Host 主机的输入设备在两个主机间切换。

源代码:http://heiher.info/sftp/repos/rinput.git
二进制程序:http://heiher.info/sftp/files/rinput/

使用方法:
Target: (Server)
./rinputd bind_address port

Host: (Client)
./rinput server_address port event_device_path … (<= 10) 默认 Switch Key Pause Over!

Build fastboot for Loongson(MIPS)

Step 1: Clone source code from git repos

git clone https://android.googlesource.com/platform/system/core.git

Step 2: Write a standalone Makefile

vim core/fastboot/Makefile
# Makefile for fastboot
 
SRCS+=protocol.c
SRCS+=engine.c
SRCS+=bootimg.c
SRCS+=fastboot.c
SRCS+=usb_linux.c
SRCS+=util_linux.c
 
VPATH+= ../libzipfile
SRCS+= centraldir.c
SRCS+= zipfile.c
 
CPPFLAGS+= -DADB_HOST=1
CPPFLAGS+= -DHAVE_FORKEXEC=1
CPPFLAGS+= -DHAVE_SYMLINKS
CPPFLAGS+= -DHAVE_TERMIO_H
CPPFLAGS+= -D_GNU_SOURCE
CPPFLAGS+= -D_XOPEN_SOURCE
CPPFLAGS+= -I.
CPPFLAGS+= -I../include
CPPFLAGS+= -I../mkbootimg
CPPFLAGS+= -I../../../external/zlib
 
CFLAGS+= -O2 -Wno-unused-parameter
LDFLAGS= 
LIBS= -lrt -lpthread -lz
 
TOOLCHAIN= mipsel-unknown-linux-gnu-
CC= $(TOOLCHAIN)gcc
LD= $(TOOLCHAIN)gcc
 
OBJS= $(SRCS:.c=.o)
 
all: fastboot
 
fastboot: $(OBJS)
	$(LD) -o $@ $(LDFLAGS) $(OBJS) $(LIBS)
 
clean:
	rm -rf $(OBJS)

Step 3: Build

cd core/fastboot
make

Over!

在 MIPS 平台上运行时动态生成指令并执行 (2)

上次是直接在可执行的栈中写入指令并转移至执行的,这次在当栈和堆都不可执行的情况下,分配空间、写入指令并执行。还是直接看代码吧。

#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
 
int main(int argc, char *argv[])
{
	unsigned int *insn = NULL;
 
	insn = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
				MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(MAP_FAILED != insn)
	{
		printf("%p\n", insn);
		insn[0] = 0x1000ffff;
		insn[1] = 0x00000000;
		__asm__ volatile (
					"jr %0 \n\t"
					"nop \n\t"
					::"r"(insn)
				);
		munmap(insn, 4096);
	}
	else
	{
		printf("%s\n", strerror(errno));
	}
 
	return 0;
}

在上面的代码中,使用系统调用 mmap 分配了一块 4096 字节的可读写、可执行的私有、匿名 VMA,并在此 VMA 中写入指令并转移至执行了。

我们来看一下此程序的运行时输出与 maps:

0x2ac88000
00400000-00404000 r-xp 00000000 08:04 262145                             /home/heiher/tmp/test
00410000-00414000 rw-p 00000000 08:04 262145                             /home/heiher/tmp/test
2ac64000-2ac88000 r-xp 00000000 08:02 917518                             /lib/ld-2.14.so
2ac88000-2ac8c000 rwxp 00000000 00:00 0 
2ac94000-2ac98000 rw-p 00020000 08:02 917518                             /lib/ld-2.14.so
2ac98000-2aca8000 rw-p 00000000 00:00 0 
2acb4000-2ae0c000 r-xp 00000000 08:02 918040                             /lib/libc-2.14.so
2ae0c000-2ae18000 ---p 00158000 08:02 918040                             /lib/libc-2.14.so
2ae18000-2ae1c000 r--p 00154000 08:02 918040                             /lib/libc-2.14.so
2ae1c000-2ae20000 rw-p 00158000 08:02 918040                             /lib/libc-2.14.so
2ae20000-2ae24000 rw-p 00000000 00:00 0 
7fc04000-7fc28000 rwxp 00000000 00:00 0                                  [stack]
7fff4000-7fff8000 r-xp 00000000 00:00 0                                  [vdso]

我们看到确定多出了一个我们所需要的 VMA:

2ac88000-2ac8c000 rwxp 00000000 00:00 0

但其长度却不是 4096 字节,而是 16K(16384)。这是因为我们当前的操作系统页面长度是 16K(16384),分配一个VMA至少需要一个完整的页面,当小于它的时候也需要分配一个完整的页面。

如何让进程的栈和堆都不可执行呢?有没有办法可以修改代码段的数据?在上面分配了 4096 字节的内存,而实际则是 16K,那么其 4096 之后的空间是否可以使用呢?动态生成指令随后立即执行是否有问题呢?

Over!

在 MIPS 平台上运行时动态生成指令并执行 (1)

昨天晚上回去后实在无聊睡不着,拿着身边的 Yeeloong 笔记本随便找点事干干,想到了 JIT 的原理,于是简单的验证了一下运行时动态生成指令并执行。

我先找了一下进程中哪个 VMA 是可以执行的,使用命令 cat /proc/self/maps 可以找到:

00400000-0040c000 r-xp 00000000 08:02 524311                             /bin/cat
00418000-0041c000 rw-p 00008000 08:02 524311                             /bin/cat
0041c000-00440000 rwxp 00000000 00:00 0                                  [heap]
2b9c8000-2b9ec000 r-xp 00000000 08:02 917518                             /lib/ld-2.14.so
2b9f8000-2b9fc000 rw-p 00020000 08:02 917518                             /lib/ld-2.14.so
2ba18000-2bb70000 r-xp 00000000 08:02 918040                             /lib/libc-2.14.so
2bb70000-2bb7c000 ---p 00158000 08:02 918040                             /lib/libc-2.14.so
2bb7c000-2bb80000 r--p 00154000 08:02 918040                             /lib/libc-2.14.so
2bb80000-2bb84000 rw-p 00158000 08:02 918040                             /lib/libc-2.14.so
2bb84000-2bb88000 rw-p 00000000 00:00 0 
2bb88000-2bd88000 r--p 00000000 08:02 786854                             /usr/lib/locale/locale-archive
7ff70000-7ff94000 rwxp 00000000 00:00 0                                  [stack]
7fff4000-7fff8000 r-xp 00000000 00:00 0                                  [vdso]

从上面的结果中可以看到 heap 和 stack 两个 VMA 的权限属性都是 rwxp,意味着我可以向里面存入指令并执行。

我选择了在栈中存入两条指令,然后转过去执行。代码如下:

#include <stdio.h>
 
int main(int argc, char *argv[])
{
	unsigned int insn[] =
	{
		0x1000ffff,
		0x00000000
	};
 
	__asm__ volatile (
				"jr %0 \n\t"
				"nop \n\t"
				::"r"(insn)
			);
 
	return 0;
}

编译程序后,执行这个程序就会进入栈里的死循环了。

你可能会想到这个操作系统的 heap 和 stack 具有可执行权限是不安全的,我确认了一下 x86 里没有可执行权限的,那么当没有可执行权限又应该怎么办呢?我们下次再说。

Over!