jQuery调用Servlet方法及注意事项

看本篇文章前先看下一点基础知识: C++中函数参数和局部变量的栈位置 


简单总结下一般的指令:

add  a,b    表示将a+b赋值给a

sub  a,b    表示将a-b赋值给a,当a<b的时候,会产生借位,CF=1

详情:这里

cmp a,b    表示比较a和b的大小,之后紧跟着这样的指令:

jne  0x0f0f0f0f  表示a,b不相等的时候,跳转到0x0f0f0f0f  处进行继续执行

或者

je   0x0f0f0f0f   表示a,b相等的时候,跳转到0x0f0f0f0f  处进行继续执行

或者

jle 0x0f0f0f0f    表示a,b小于等于的时候,跳转到0x0f0f0f0f  处进行继续执行

更多cmp后边的操作可以自己搜索一下

lea a,b   将b 的数值赋值给a

test a,b  将a跟b进行与操作,结果的判断与cmp一样,比如:

test  a,08h,就是检测a&08h的结果,之后用jne,je等等进行处理

更多看这里这里

movzx  ax,al   表示无符号扩展并传递,也就是将al的字节宽度扩展一下赋值给ax

参考见这里

mov dword ptr[eax],ebx    内存单元操作方法,将寄存器ebx中的数据,保存到eax指向的内存单元中,同理:

mov ebx,dword ptr[eax]    将eax指向的内存单元的数据存储到ebx中

详情见这里

现在开始分析一段汇编代码:

013C1550  push        ebp  
013C1551  mov         ebp,esp 
013C1553  sub         esp,0E4h 
013C1559  push        ebx  
013C155A  push        esi  
013C155B  push        edi  
013C155C  lea         edi,[ebp-0E4h] 
013C1562  mov         ecx,39h 
013C1567  mov         eax,0CCCCCCCCh 
013C156C  rep stos    dword ptr es:[edi] 
013C156E  cmp         dword ptr [ebp+8],0 
013C1572  jne         13C1578h
013C1574  xor         eax,eax 
013C1576  jmp         13C15BCh 
013C1578  mov         eax,dword ptr [ebp+8] 
013C157B  mov         dword ptr [ebp-0Ch],eax 
013C157E  mov         eax,dword ptr [ebp-0Ch] 
013C1581  movzx       ecx,word ptr [eax] 
013C1584  cmp         ecx,5A4Dh 
013C158A  je          13C1590h
013C158C  xor         eax,eax 
013C158E  jmp         13C15BCh
013C1590  mov         eax,dword ptr [ebp-0Ch] 
013C1593  mov         ecx,dword ptr [ebp-0Ch] 
013C1596  add         ecx,dword ptr [eax+3Ch] 
013C1599  mov         dword ptr [ebp-10h],ecx 
013C159C  mov         eax,dword ptr [ebp-10h] 
013C159F  mov         ecx,dword ptr [eax+00000080h] 
013C15A5  mov         dword ptr [ebp-08h],ecx 
013C15A8  mov         eax,dword ptr [ebp+0Ch] 
013C15AB  mov         ecx,dword ptr [ebp-10h] 
013C15AE  mov         edx,dword ptr [ecx+00000084h] 
013C15B4  mov         dword ptr [eax],edx 
013C15B6  mov         eax,dword ptr [ebp+8] 
013C15B9  add         eax,dword ptr [ebp-08h] 
013C15BC  pop         edi  
013C15BD  pop         esi  
013C15BE  pop         ebx  
013C15BF  mov         esp,ebp 
013C15C1  pop         ebp  
013C15C2  ret    

整体分析思路:

1:调用方式

看到最后是ret,不需要调整栈平衡,所以是_cdecl调用方式,当遇到:

ret  number,则是_stdcall或者_fastcall,后两者,区别在于_fastcall调用的时候最多参数可以有4个,因为它会自动将前两个参数用寄存器保存,执行起来更快,参数太多的时候就不行了。

2:函数参数数量

找ebp+i 都有哪些,看到该例子中有:

ebp+8,ebp+0ch,所以是两个参数的函数

3:函数是否有返回值

看最后退出前十都特意将一个数据保存到eax中,因为函数返回值是以该寄存器返回的,该例子有,所以是有返回值的函数

4:分析局部变量有多少

找ebp-i

该例子有:

ebp-0ch,ebp-08h,ebp-10h,ebp-04h,一般是4个参数,但是有时候ebp-4是系统自定义的局部变量,用来交换数据使用的,不是开发者定义的,所以这样可能只有3个局部变量,而不是4个。

5:找找有没有敏感信息,一般里边的硬编码(固定数据)会有些提示(不是ebp,esp,是对其他寄存器的操作),比如该例子:

5A4Dh

eax+3Ch

eax + 00000080h

ecx+00000084h

其实,5A4D是PE文件的标志,3C是PE文件头指针的偏移量,80是导出表偏移量,84是导出表的size,剩下的,就是对里边的一点点理解了:

013C1550  push        ebp  
013C1551  mov         ebp,esp 
;所有函数的入口,都要有这两行,因为汇编执行的时候需要寻找下一条执行的地址,需要有定位的变量来时刻去寻找,而ebp指向的始终是栈底,esp指向的是栈顶,这样两头都有了,剩下的无非就是它们去加减相对偏移位数了,函数退出的时候在还原,本例中在013C15BF处。
013C1553  sub         esp,0E4h ;
;在这个函数中开辟一块大小为0E4h,也就是12*16+4=196字节的内存空间,以便函数中的变量使用,这个大小是进入函数之前编译器计算的,全部分配在栈上,在函数进入的时候开辟,函数退出的时候自动回收,大小一旦进入函数后就不再改变。所以里边如果有需要动态开辟内存,比如使用malloc、new等等,便会去其他的地方(堆)中开辟。
013C1559  push        ebx  
013C155A  push        esi  
013C155B  push        edi  
;保存其他有必要的寄存器,以便函数退出的时候还原,本例中在013C15BC处
013C155C  lea         edi,[ebp-0E4h] 
013C1562  mov         ecx,39h 
013C1567  mov         eax,0CCCCCCCCh 
013C156C  rep stos    dword ptr es:[edi] 
;寄存器初始化,具体没去深究

013C156E  cmp         dword ptr [ebp+8],0 
;比较第一个dword区块参数的数据是否为0,0可能是false,NULL等等
013C1572  jne         13C1578h
;不为零,则跳转到13C1578h继续执行
;否则,返回,返回数值都是存放在eax中
013C1574  xor         eax,eax 
; 这里xor eax,eax做异或操作后,eax=0,也就是返回0,false,NULL 之类的
013C1576  jmp         13C15BCh 
;调到函数结束位置,结束函数
013C1578  mov         eax,dword ptr [ebp+8] 
上边如果不结束函数,则进行上边这一步,取第一个参数的数据到寄存器eax中
013C157B  mov         dword ptr [ebp-0Ch],eax 
;将寄存器eax中的数据保存到第2个局部变量所在的栈单元
013C157E  mov         eax,dword ptr [ebp-0Ch] 
;由于汇编操作时候,不能取内存嵌套,所以将内存数据用临时存储器保存再操作是经常会用到的
013C1581  movzx       ecx,word ptr [eax] 
;将eax的数值作为内存地址,之后再取值给ecx,有点编程基础的人都至少会猜到,这是获取指针指向的内存的数值
013C1584  cmp         ecx,5A4Dh 
;将得到指针指向的数据后与0x5A4Dh进行比较,没有经验的我,开始还以为这个是随便的东西,后来才知道是PE文件的文件头标志:MZ
013C158A  je          13C1590h
;当是与0x5A4Dh相等(是PE文件),则跳转到执行位置,否则将返回数值变为0,并跳转到结束位置
013C158C  xor         eax,eax 
013C158E  jmp         13C15BCh
013C1590  mov         eax,dword ptr [ebp-0Ch] 
013C1593  mov         ecx,dword ptr [ebp-0Ch] 
;表示将相对于第二个局部变量(指针)指向的位置赋值给寄存器
013C1596  add         ecx,dword ptr [eax+3Ch] 
;取指针指向的位置加上相对于它偏移3Ch位置的数据,实际上是两段便宜地址想加,也就是dword ptr [ebp-0Ch]和dword ptr [eax+3Ch]都是偏移量
013C1599  mov         dword ptr [ebp-10h],ecx 
;之后将结果赋值给第3个局部变量
013C159C  mov         eax,dword ptr [ebp-10h] 
013C159F  mov         ecx,dword ptr [eax+00000080h] 
013C15A5  mov         dword ptr [ebp-08h],ecx 
;以上3行表示取第三个局部变量指向的位置的偏移80h的数据给第1个局部变量
013C15A8  mov         eax,dword ptr [ebp+0Ch] 
;取第二个函数参数到eax
013C15AB  mov         ecx,dword ptr [ebp-10h] 
013C15AE  mov         edx,dword ptr [ecx+00000084h] 
013C15B4  mov         dword ptr [eax],edx 
;以上4行表示将相对于第3个局部变量指向位置偏移84h的数据存到第二个函数参数,这样的做法一般是回调、指针操作、引用操作,也就是将函数得到的数据通过参数返回去毕竟函数返回数据只有一个,有时候不够用也不想用多余的结构体
013C15B6  mov         eax,dword ptr [ebp+8] 
013C15B9  add         eax,dword ptr [ebp-08h] 
;同理,将第一个函数参数的数据取出来,并加上第3个局部变量的数值,将整个和作为地址,取该地址的数据给eax,因为eax是函数返回值保存的位置,所以显然,该函数是有返回值的。
013C15BC  pop         edi  
013C15BD  pop         esi  
013C15BE  pop         ebx  
013C15BF  mov         esp,ebp 
013C15C1  pop         ebp  
013C15C2  ret    

对于PE不了解的时候反汇编的结果:

struct A
{
	DWORD *_1;
	DWORD *_2;
	DWORD *_3;
};
typedef struct A _A;
DWORD * _stdcall showstatic(BYTE *p,DWORD *q)//char s[],DWORD *p)//(_B _b,char *p)
{
	_A a;
	if (p == NULL)
	{
		return NULL;
	}

	a._2=(DWORD *)p;


	if(*(WORD*)a._2!=0x5A4D)
	{
		return NULL;
	}
	a._1 = (DWORD*)((DWORD)a._2 + *((DWORD*)((DWORD)a._2 + (DWORD)0x3c)));

	a._3 = (DWORD*)((*(DWORD*)((DWORD)a._1+(DWORD)0x80)));

	*q  = ((*(DWORD*)((DWORD)a._1+(DWORD)0x84)));

	return (DWORD *)((long)(DWORD *)p+(long)a._3);
}
对PE了解一些后的结果:

PIMAGE_EXPORT_DIRECTORY _cdecl GetExportTableBaseAddr(BYTE *modBaseAddr,DWORD *size)
{
	PIMAGE_NT_HEADERS _1;
	PIMAGE_DOS_HEADER _2;
	DWORD _3;

	if (modBaseAddr == NULL)
	{
		return NULL;
	}

	_2=(PIMAGE_DOS_HEADER)modBaseAddr;


	if(_2->e_magic!=IMAGE_DOS_SIGNATURE)//0x5A4D
	{
		return NULL;
	}

	_1		= (PIMAGE_NT_HEADERS)((long)_2 + (long)_2->e_lfanew);

	_3		= _1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

	*size	= _1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
	
	return (PIMAGE_EXPORT_DIRECTORY)((long)(PIMAGE_DOS_HEADER)modBaseAddr+(long)_3);
}
当然,这两个运行结果肯定都一样。

整个可执行程序:

#include<windows.h>
#include<stdio.h>
#include <string>
#include <TCHAR.H>
#include "psapi.h" 
#pragma   comment   (lib, "psapi.lib ")
#include "tlhelp32.h"
#include <imagehlp.h>
#pragma   comment   (lib, "Dbghelp.lib ")
#include <iostream>
#include <winnt.h>
using namespace std;
using namespace std;

PIMAGE_EXPORT_DIRECTORY _cdecl GetExportTableBaseAddr(BYTE *modBaseAddr,DWORD *size)//,DWORD *q
{
	PIMAGE_NT_HEADERS _1;
	PIMAGE_DOS_HEADER _2;
	DWORD _3;

	if (modBaseAddr == NULL)
	{
		return NULL;
	}

	_2=(PIMAGE_DOS_HEADER)modBaseAddr;


	if(_2->e_magic!=IMAGE_DOS_SIGNATURE)//0x5A4D
	{
		return NULL;
	}

	_1		= (PIMAGE_NT_HEADERS)((long)_2 + (long)_2->e_lfanew);

	_3		= _1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

	*size	= _1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
	
	//PIMAGE_EXPORT_DIRECTORY p = (PIMAGE_EXPORT_DIRECTORY)((long)(PIMAGE_DOS_HEADER)modBaseAddr+(long)_3);
	return (PIMAGE_EXPORT_DIRECTORY)((long)(PIMAGE_DOS_HEADER)modBaseAddr+(long)_3);
}
struct A
{
	DWORD *_1;
	DWORD *_2;
	DWORD *_3;
};
typedef struct A _A;
DWORD * _stdcall showstatic(BYTE *p,DWORD *q)//char s[],DWORD *p)//(_B _b,char *p)
{
	_A a;
	if (p == NULL)
	{
		return NULL;
	}

	a._2=(DWORD *)p;


	if(*(WORD*)a._2!=0x5A4D)
	{
		return NULL;
	}
	a._1 = (DWORD*)((DWORD)a._2 + *((DWORD*)((DWORD)a._2 + (DWORD)0x3c)));//a._2+sizeof(s);//(char*)((DWORD*)a._2 + (DWORD*)(a._2+0x3c));

	a._3 = (DWORD*)((*(DWORD*)((DWORD)a._1+(DWORD)0x80)));

	*q  = ((*(DWORD*)((DWORD)a._1+(DWORD)0x84)));

	return (DWORD *)((long)(DWORD *)p+(long)a._3);
}
char* wcharTochar(const wchar_t *wchar)//, char *chr, int length)  
{  
	// WideCharToMultiByte( CP_ACP, 0, wchar, -1      ,chr , length, NULL, NULL );  
	int len= WideCharToMultiByte( CP_ACP, 0, wchar	, wcslen(wchar),NULL, 0     , NULL,NULL); 
	char* m_char =new char[len+1]; 
	WideCharToMultiByte( CP_ACP, 0, wchar , wcslen(wchar),m_char,len,NULL,NULL); 
	m_char[len]=‘\0‘; 
	return m_char; 
} 

int main()
{
	//FILE * pp = fopen("C:\\Users\\1\\Desktop\\ntdll.dll","r");
	HMODULE hModule = LoadLibrary(L"C:\\Users\\1\\Desktop\\ntdll.dll");

	HANDLE h = INVALID_HANDLE_VALUE;
	h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());

	MODULEENTRY32 me32;
	me32.dwSize = sizeof( MODULEENTRY32 );

	if( !Module32First( h, &me32 ) )
	{
		CloseHandle( h );
		return( FALSE );
	}

	DWORD size = 0;         
	do{
		if(strcmp(wcharTochar(me32.szModule),"ntdll.dll")==0)
		{
			PIMAGE_EXPORT_DIRECTORY p = GetExportTableBaseAddr(me32.modBaseAddr,&size);
			cout << "export table address : 0x"<<hex<<p<<" size:"<<dec<<size<<endl;

			size = 0;
			DWORD *p1 = showstatic(me32.modBaseAddr,&size);
			cout << "export table address : 0x"<<hex<<p1<<" size:"<<dec<<size<<endl;
		}
	}while(Module32Next(h,&me32));
	
	CloseHandle( h );
	
	return 0;
}



C++反汇编

C++类一定有拷贝构造函数吗 

一个感染PE文件的例子(1)    赞

CALL是如何炼成的~~   赞

如果获取其它进程的DLL内存地址?





jQuery调用Servlet方法及注意事项

上一篇:web项目中获取各种路径的方法


下一篇:linux下网站压力测试工具webbench