星期四, 十一月 02, 2006

获得网卡ipv6地址列表

MS在IP Helper库中提供的函数使应用程序在运行过程中可以方便的设置和获取系统网络配置信息,IPv4的网卡配置信息获取可以使用GetAdapterInfo函数调用实现,而IPv6的信息获取,则调用GetAdaptersAddresses函数,这个函数在最新的MSDN中有比较完整的示例,但是没有演示如何处理获得的IPv6地址,这里写一下。因为MS在WindowsXP之后才正式支持IPv6,所以此函数仅在WindowsXP以后的操作系统上有效,而且好像VC6也没有这个函数的支持。
GetAdaptersAddresses函数原型为:
ULONG WINAPI GetAdaptersAddresses(
ULONG Family,
ULONG Flags,
PVOID Reserved,
PIP_ADAPTER_ADDRESSES AdapterAddresses,
PULONG SizePointer
);
详细的说明可以参见MSDN,其中一个重要的数据结构是PIP_ADAPTER_ADDRESSES,它包含网卡的配置信息,定义如下:
typedef struct _IP_ADAPTER_ADDRESSES {
//...
struct _IP_ADAPTER_ADDRESSES* Next;
PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
//...
} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES;
上面定义中省略的部分可以参见MSDN,其中Next指向下一个网卡的IP_ADAPTER_ADDRESSES指针,而FirstUnicastAddress就是网卡的ip地址字段,它是一个链接结构的PIP_ADAPTER_UNICAST_ADDRESS,定义如下:
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
//...
struct _IP_ADAPTER_UNICAST_ADDRESS* Next;
SOCKET_ADDRESS Address;
//...
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
其中Address就是我们需要的当前项目的ip地址的内容,而Next包含了当前的网卡(程序里就是当前使用的那个PIP_ADAPTER_ADDRESSES指针)下一个ip地址配置的相关信息。
我们在调用GetAdaptersAddresses成功后(调用需要指定标志AF_INET6或者AF_UNSPEC),获取一个sockaddr_in6(socket函数使用)的变量,可以使用如下强制类型转换语句:
((struct sockaddr_in6 *)AdapterAddresses->FirstUnicastAddress->Address.lpSockaddr);
遍历以AdapterAddresses和FirstUnicastAddress为首的链表,我们就能够得到所有网卡上配置的所有IPv6地址的列表了。

在linux下获取网卡IPv6地址列表好像读取/proc/net/if_inet6

当然了,以上讲的方法都需要首先在系统里添加IPv6支持,Windows在网络配置中添加ipv6协议,而linux运行modprobe ipv6加载ipv6模块(如果没有自动加载的话)。

星期六, 四月 15, 2006

汇编语言的sha1算法实现

手动做了一些优化,比linux上那个sha1sum快一些,但是比rfc上c的代码还是慢,很受打击

用cygwin下的gcc可以编译成dll文件,命令
gcc -shared -mno-cygwin -osha1.dll sha1.s

在cygwin下连同下面的测试程序编译成windows可执行文件用这个命令
gcc -mno-cygwin -osha1.exe main.c sha1.s

导出三个函数给c用

/*sha1.h*/
/*重新开始新的计算*/
int init();
/*输入新数据到缓冲区*/
int input(void* data, int length);
/*取得计算结果*/
int final(void* result);

#下面是汇编的代码,比c还慢的那个
#sha1.s
.equ K0TO19, 0x5A827999
.equ K20TO39, 0x6ED9EBA1
.equ K40TO59, 0x8F1BBCDC
.equ K60TO79, 0xCA62C1D6

.section .bss
.lcomm buffer, 64 #已经输入数据缓冲区
.lcomm length, 4 #已经输入数据长度
.lcomm totalenlow, 4 #数据总长度
.lcomm totalenhi, 4 #数据长度,高32bit
.lcomm stat, 4 #-1=error, 0=ok
.lcomm H0, 4
.lcomm H1, 4
.lcomm H2, 4
.lcomm H3, 4
.lcomm H4, 4
.lcomm W, 320

.section .text
.globl _init, _input, _final
# 填充数据缓冲至512bit
# 函数使用edi作缓冲区地址,ecx为块中已有数据长度,edx为数据总长度(填充末尾需要)
# %eax, %ebx用作临时变量
# 最后64位用来记录数据长度
_pad:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
movl $buffer, %edi
movl length, %ecx #肯定小于64
movb $0x80, %al #第一个填充字节1000 0000B
addl %ecx, %edi #寻址填充起点
cld #增长方向
stosb
movl $64, %eax #块大小,总数
subl %ecx, %eax #应填充数
subl $1, %eax #一个已经填充
cmp $8, %eax #查看预留空间是否够用
jl padbufferfull
jmp padbufferok
padbufferfull:
movl %eax, %ecx
xorl %eax, %eax
rep stosb
call _compute
movl $buffer, %edi
movl $64, %eax
padbufferok:
subl $8, %eax #留出数据长度存放空间
movl %eax, %ecx #准备填充
xorl %eax, %eax #eax清零
rep stosb
#由于计算机内存储方式与rfc中相反,故应作相应
#存放长度
movl totalenhi, %edx
movl %edx, %eax
shr $24, %eax
stosb
movl %edx, %eax
shr $16, %eax
stosb
movl %edx, %eax
shr $8, %eax
stosb
movb %dl, (%edi)
incl %edi
movl totalenlow, %edx
movl %edx, %eax
shr $24, %eax
stosb
movl %edx, %eax
shr $16, %eax
stosb
movl %edx, %eax
shr $8, %eax
stosb
movb %dl, (%edi)
incl %edi
call _compute
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
ret

# 重置各种参数,准备新的计算
# H0 = 67452301
# H1 = EFCDAB89
# H2 = 98BADCFE
# H3 = 10325476
# H4 = C3D2E1F0
# 已经输入数据长度清零
# int init()
_init:
movl $0x67452301, H0
movl $0xefcdab89, H1
movl $0x98badcfe, H2
movl $0x10325476, H3
movl $0xc3d2e1f0, H4
movl $0, length
movl $0, totalenlow
movl $0, totalenhi
movl $0, stat
xorl %eax, %eax
ret

# 散列计算函数
# rfc3174中method1
_compute:
pushl %esi
pushl %edi
pushl %ecx
pushl %eax
pushl %ebx
pushl %edx
pushl %ebp
# a. Divide M(i) into 16 words W(0), W(1), ... , W(15), where W(0)
# is the left-most word.
stepa:
movl $buffer, %esi
movl $W, %edi
movl $16, %ecx
movoneword:
lodsl #load a word to eax
#高位放低位
movl %eax, %ebx
shr $24, %eax
stosb
mov %ebx, %eax
shr $16, %eax
stosb
movl %ebx, %eax
shr $8, %eax
stosb
movb %bl, (%edi)
incl %edi
loop movoneword
# b. For t = 16 to 79 let
# W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)).
# 使用ecx作为计数器
# edx作为索引
movl $16, %ecx
stepb:
movl %ecx, %edx
subl $3, %edx
movl W(, %edx, 4), %eax
movl %ecx, %edx
subl $8, %edx
xorl W(, %edx, 4), %eax
movl %ecx, %edx
subl $14, %edx
xorl W(, %edx, 4), %eax
movl %ecx, %edx
subl $16, %edx
xorl W(, %edx, 4), %eax
rol $1, %eax
movl %eax, W(, %ecx, 4)
inc %ecx
cmp $79, %ecx
jle stepb
# c. Let A = H0, B = H1, C = H2, D = H3, E = H4.
# use registers for a, b, c, d, e
# eax, ebx, ecx, edx, ebp for faster speed
stepc:
movl H0, %eax
movl H1, %ebx
movl H2, %ecx
movl H3, %edx
movl H4, %ebp

# d. For t = 0 to 79 do
# TEMP = S^5(A) + f(t;B,C,D) + E + W(t) + K(t);
# E = D; D = C; C = S^30(B); B = A; A = TEMP;
# then esi and edi are used for counter
# f(t;B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
# f(t;B,C,D) = B XOR C XOR D (20 <= t <= 39)
# f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
# f(t;B,C,D) = B XOR C XOR D (60 <= t <= 79)
# K(t) = 5A827999 ( 0 <= t <= 19)
# K(t) = 6ED9EBA1 (20 <= t <= 39)
# K(t) = 8F1BBCDC (40 <= t <= 59)
# K(t) = CA62C1D6 (60 <= t <= 79)
xorl %esi, %esi
stepd:
stepd1:
movl %eax, %edi
rol $5, %edi #S^5(A)
addl %ebp, %edi #edi=S^5(A)+E
addl W(,%esi,4), %edi #+W(t)
addl $K0TO19, %edi #edi=S^5(A)+E+W(t)+K(t)
#E=ebp可用
#f(f;B,C,D)
#(B AND C) OR ((NOT B) AND D)
movl %ebx, %ebp
andl %ecx, %ebp
pushl %ebx
notl %ebx
andl %edx, %ebx
orl %ebx, %ebp
popl %ebx
addl %ebp, %edi #edi=TEMP, +f(t;B,C,D)
#E = D; D = C; C = S^30(B); B = A; A = TEMP
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $19, %esi
jle stepd1
stepd2:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K20TO39, %edi
movl %ebx, %ebp
xorl %ecx, %ebp
xorl %edx, %ebp
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $39, %esi
jle stepd2
stepd3:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K40TO59, %edi
#f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D)
movl %ebx, %ebp
andl %ecx, %ebp
pushl %ebx
andl %edx, %ebx
orl %ebx, %ebp
popl %ebx
pushl %ecx
andl %edx, %ecx
orl %ecx, %ebp
popl %ecx
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $59, %esi
jle stepd3
stepd4:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K60TO79, %edi
movl %ebx, %ebp
xorl %ecx, %ebp
xorl %edx, %ebp
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $79, %esi
jle stepd4

# e. Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4
# + E.
stepe:
addl %eax, H0
addl %ebx, H1
addl %ecx, H2
addl %edx, H3
addl %ebp, H4
popl %ebp
popl %edx
popl %ebx
popl %eax
popl %ecx
popl %edi
popl %esi
ret

# 添加数据,数据到512bit时进行一次计算,并重新开始填充
# int input(void* data, int len);
_input:
movl stat, %eax
cmp $-1, %eax
je inputstaterr
jmp inputstatok
inputstaterr:
ret
inputstatok:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %edi
pushl %ecx
pushl %edx
pushl %eax
pushl %ebx
movl length, %edx #缓冲区已有数据长度(字节)
movl totalenlow, %eax #总长
movl 12(%ebp), %ebx #新送进来数据长度
movl 8(%ebp), %esi #用户缓冲区地址
movl totalenhi, %ebp
movl $buffer, %edi
addl %edx, %edi #定位填充起点
#%esi为源起点,%edi目的
movl $64, %ecx
subl %edx, %ecx #缓冲区剩余空间
#计算过程中ecx用作缓冲区剩余空间计数
#ebx未复制数据计数
#eax为总长度计数
inputnextbyte:
movsb
#inc %eax #总长度加一
addl $8, %eax
jo inctotalenhi
jmp countbuf
inctotalenhi:
inc %ebp
jo toolong
jmp countbuf
toolong:
movl $-1, %eax
movl %eax, stat
jmp finish
countbuf:
dec %ecx
jz bufferfull
jmp bufferok
bufferfull:
movl $64, %ecx #缓冲区空
movl $buffer, %edi
call _compute
bufferok:
dec %ebx
jz finish
jmp inputnextbyte
finish:
movl %eax, totalenlow
movl %ebp, totalenhi
movl $64, %edx
subl %ecx, %edx
movl %edx, length
popl %ebx
popl %eax
popl %edx
popl %ecx
popl %edi
popl %esi
pop %ebp
movl stat, %eax
ret

# 完成计算,取得最后散列结果
# int final(void* result);
_final:
movl stat, %eax
cmp $-1, %eax
je finalstaterr
jmp finalstatok
finalstaterr:
ret
finalstatok:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %edi
pushl %ecx
pushl %eax
pushl %ebx
call _pad
movl 8(%ebp), %edi
movl $H0, %esi
movl $5, %ecx
movoners:
pushl %ecx
lodsl
movl %eax, %ebx
shr $24, %eax
stosb
movl %ebx, %eax
shr $16, %eax
stosb
movl %ebx, %eax
shr $8, %eax
stosb
movb %bl, (%edi)
incl %edi
popl %ecx
loop movoners
popl %ebx
popl %eax
popl %ecx
popl %edi
popl %esi
popl %ebp
xorl %eax, %eax
ret

/*再给个测试使用的例子*/
/*main.c*/
#include
#include
#include "sha1.h"
int main()
{
char *buffer = "this is a test";
int len = 0;
char result[20];
int i;
init();
len = strlen(buffer);
input(buffer, len);
final(result);
for(i=0; i<20; i++)
{
printf("%02X ", 0x0ff & result[i]);
}
printf("\n");
return 0;
}

星期一, 一月 09, 2006

使用at&t汇编写x86启动扇区

At&t的汇编语言语法在类unix操作系统中使用很多,linux内核新的源代码全部都是使用at&t汇编完成的,at&t汇编使用编译器是gas,但是,使用at&t汇编与使用nasm或masm编写启动扇区略有不同。新版本的gas不支持直接生成可执行的二进制代码文件,所以还需使用ld进行连接或使用objcopy将其生成的目标文件进行转换才可以使用,下面给出一个例子:
Boot sector的源代码:

START_SEG = 0x07c0
.code16
.text
.global _start
_start:

ljmp $START_SEG, $_start2

_start2:
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0x7c00, %sp
sti
cld

movw $hellomsg, %si

msg_loop:
lodsb
andb %al, %al
jz die
movb $0xe, %ah
int $0x10
jmp msg_loop

die:
jmp die

hellomsg:
.ascii "Hello, World!\r\n"
.byte 0

.org 510
boot_flag: .word 0xaa55

这个代码仿照linux内核的bootsect.s写成。编译连接步骤如下:
1. as –o boot1.o boot1s
这句不用解释了
2. 这里有两种方法可以实现
a. ld –o boot1.bin –Ttext 0x0 --oformat binary boot1.o
其中的-Ttext 0x0指定代码中的text段偏移量从0开始
--oformat binary指定输出文件是纯二进制形式
b. objcopy -O binary boot1.o boot1.bin
objcopy的作用是转换文件格式,上面的命令将as生成的目标文件直接转换成对应的二
进制文件,因为此文件是一个自包含即不依赖于任何外界库的文件,所以可以直接转换。
以上两句的结果是等价的,都生成了一个512字节的boot1.bin,在vmware中直接指定floppy image开机就可以看到效果了。在使用cygwin的系统中a方法似乎会失败。
这里主要介绍使用at&t编写boot sector的编译和连接工作,具体的boot sector的内容可以在网上找到。