企鹅的封包拦截器, 只是技术研究爱好
来源:互联网 发布:淘宝怎么样才不算盗图 编辑:程序博客网 时间:2024/05/27 00:48
当初写这个的时候是因为自己正在研究pc qq的协议, 本想用delphi写,不过没有找到完整的WinCap的delphi头文件, 只好用c++ builder了, 说实在的是第一次用wincap, 都是摸索着写的
UMain.h
//---------------------------------------------------------------------------#ifndef UMainH#define UMainH//---------------------------------------------------------------------------#include <System.Classes.hpp>#include <Vcl.Controls.hpp>#include <Vcl.StdCtrls.hpp>#include <Vcl.Forms.hpp>#include <windows.hpp>#include <pcap.h>#include <Vcl.ExtCtrls.hpp>#include <Vcl.ComCtrls.hpp>//#include <System.StrUtils.hpp>//#include <Winapi.Windows.hpp>//#include <Vcl.XPMan.hpp>#pragma comment(lib, "Packet.lib")#pragma comment(lib, "wpcap.lib")//#pragma resource "WindowsXP.res"#pragma hdrstop//---------------------------------------------------------------------------// 定义一个线程继承自 TThreadclass TMyThread : public TThread{ private:void __fastcall Execute();void StartFunc(); public: __fastcall TMyThread(bool CreateSuspended); public: pcap_t *adhandle;};class TfrmMain : public TForm{__published:// IDE-managed ComponentsTMemo *mmo1;TPanel *pnl1;TPageControl *pgc1;TTabSheet *ts1;TTabSheet *ts2;TPanel *pnl2;TLabel *Label1;TLabel *Label2;TComboBox *cbbAdapteList;TComboBox *cbbProtocol;TButton *btnStart;TButton *btnStop;TSplitter *spl1;TLabel *Label3;TEdit *edtqq;TListView *lvpacket;TButton *btnClear;void __fastcall btnStartClick(TObject *Sender);void __fastcall FormCreate(TObject *Sender);void __fastcall btnStopClick(TObject *Sender);void __fastcall cbbProtocolChange(TObject *Sender);void __fastcall lvpacketClick(TObject *Sender);void __fastcall lvpacketColumnClick(TObject *Sender, TListColumn *Column);void __fastcall FormDestroy(TObject *Sender);void __fastcall btnClearClick(TObject *Sender);private:// User declarationspcap_t *adhandle;TMyThread *Mythread;pcap_if_t *alldevs;pcap_if_t *d;int listCount;char packet_filter[50];private:void __fastcall ListViewColumnSetting(int cbbindex);void __fastcall ListViewAddColumn(TListView *lv, String Caption, int Widht);public:// User declarations__fastcall TfrmMain(TComponent* Owner);protected:void __fastcall RecvWinCapData(TWMCopyData Message);BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_COPYDATA, TWMCopyData, RecvWinCapData);END_MESSAGE_MAP(TForm)};//---------------------------------------------------------------------------/* 4 字节IP地址 */typedef struct ip_address{u_char byte1;u_char byte2;u_char byte3;u_char byte4;}ip_address;/* IPv4 头 */typedef struct ip_header{u_charver_ihl;// Version (4 bits) + Internet header length (4 bits)u_chartos;// Type of serviceu_short tlen;// Total lengthu_short identification; // Identificationu_short flags_fo;// Flags (3 bits) + Fragment offset (13 bits)u_charttl;// Time to liveu_charproto;// Protocolu_short crc;// Header checksumip_addresssaddr;// Source addressip_addressdaddr;// Destination addressu_intop_pad;// Option + Padding}ip_header;/* UDP 头 */typedef struct udp_header{u_short sport;// Source portu_short dport;// Destination portu_short len;// Datagram lengthu_short crc;// Checksum}udp_header;/* 我的数据结构 */typedef struct TMyData{ char timestr[16]; // 时戳 BYTE flags; // 数据标识 0 发送, 1 接收 char sipp[30]; // 格式化后的源IP和端口 char dipp[30]; // 格式化后的目标IP和端口 int datalen; // QQ数据段长度 u_char *data; // 数据指针}TMyData;/* 捕获的回调函数声明 */void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);extern PACKAGE TfrmMain *frmMain;//---------------------------------------------------------------------------#endif
UMain.dfm
object frmMain: TfrmMain Left = 0 Top = 0 Caption = #23553#21253#25429#33719#24037#20855' By:ying32 QQ:396506155 ver 1.0' ClientHeight = 373 ClientWidth = 745 Color = clBtnFace DoubleBuffered = True Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False Position = poScreenCenter WindowState = wsMaximized OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object spl1: TSplitter Left = 0 Top = 237 Width = 745 Height = 3 Cursor = crVSplit Align = alBottom ExplicitTop = 41 ExplicitWidth = 199 end object mmo1: TMemo Left = 0 Top = 240 Width = 745 Height = 92 Align = alBottom Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -13 Font.Name = 'Courier New' Font.Style = [] ParentFont = False ScrollBars = ssVertical TabOrder = 0 end object pnl1: TPanel Left = 0 Top = 332 Width = 745 Height = 41 Align = alBottom BevelOuter = bvNone TabOrder = 1 end object pgc1: TPageControl Left = 0 Top = 61 Width = 745 Height = 176 ActivePage = ts1 Align = alClient TabOrder = 2 object ts1: TTabSheet Caption = #23553#21253#39029#38754 object lvpacket: TListView Left = 0 Top = 0 Width = 737 Height = 148 Align = alClient Columns = < item Caption = #24207 end item Caption = #26631#35782 Width = 60 end item Caption = #26102#25139 Width = 80 end item Caption = #28304'IP:'#31471#21475 Width = 120 end item Caption = #30446#26631'IP:'#31471#21475 Width = 120 end item Caption = #38271#24230 end item Caption = #29256#26412#21495 Width = 60 end item Caption = #21629#20196 Width = 60 end item Caption = #21253#24207 Width = 60 end item Caption = 'QQ'#21495 Width = 110 end item Caption = #22266#23450 Width = 180 end item Caption = 'TEA'#21152#23494#25968#25454#27573 Width = 385 end> DoubleBuffered = True GridLines = True MultiSelect = True ReadOnly = True RowSelect = True ParentDoubleBuffered = False TabOrder = 0 ViewStyle = vsReport OnClick = lvpacketClick OnColumnClick = lvpacketColumnClick end end object ts2: TTabSheet Caption = #20854#23427 ImageIndex = 1 end end object pnl2: TPanel Left = 0 Top = 0 Width = 745 Height = 61 Align = alTop BevelOuter = bvNone TabOrder = 3 DesignSize = ( 745 61) object Label1: TLabel Left = 5 Top = 13 Width = 96 Height = 13 Caption = #36873#25321#19968#20010#36866#37197#22120#65306 end object Label2: TLabel Left = 399 Top = 14 Width = 36 Height = 13 AutoSize = False Caption = #21327#35758#65306 end object Label3: TLabel Left = 8 Top = 40 Width = 129 Height = 13 AutoSize = False Caption = #21482#25235#21462'QQ('#20026'0'#21017#20840#37096')'#65306 end object cbbAdapteList: TComboBox Left = 107 Top = 11 Width = 278 Height = 21 Style = csDropDownList TabOrder = 0 end object cbbProtocol: TComboBox Left = 433 Top = 11 Width = 112 Height = 21 Style = csDropDownList ItemIndex = 0 TabOrder = 1 Text = 'PCQQ' OnChange = cbbProtocolChange Items.Strings = ( 'PCQQ' #23433#21331'QQ' 'IPhoneQQ' #33258#23450#20041) end object btnStart: TButton Left = 585 Top = 10 Width = 75 Height = 25 Anchors = [akRight] Caption = #24320#22987 TabOrder = 2 OnClick = btnStartClick end object btnStop: TButton Left = 666 Top = 10 Width = 75 Height = 25 Anchors = [akRight] Caption = #20572#27490 Enabled = False TabOrder = 3 OnClick = btnStopClick end object edtqq: TEdit Left = 139 Top = 37 Width = 140 Height = 21 MaxLength = 11 NumbersOnly = True TabOrder = 4 Text = '2482241557' end object btnClear: TButton Left = 666 Top = 37 Width = 75 Height = 25 Anchors = [akRight] Caption = #28165#38500 TabOrder = 5 OnClick = btnClearClick end endend
UMain.cpp
//---------------------------------------------------------------------------#include <vcl.h>#include <commctrl.h>#pragma hdrstop#include "UMain.h"//---------------------------------------------------------------------------#pragma package(smart_init)#pragma resource "*.dfm"TfrmMain *frmMain;// 定义一个接收消息窗口句柄变量HWND g_hWnd = 0;int g_Protocolflag = 0;DWORD g_onlyQQ = 0;bool g_BSort = false; // ListView升序降序控制//int ColumnToSort = 0;// 定义一个自定义消息//#define MYCOPYDATA (WM_USER + 0x23)//---------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner): TForm(Owner){}// 转换字节到十六进制文本String __fastcall ByteArrToString(BYTE *qqdata, int len){ String ret = ""; BYTE *a; for (int i = 0; i < len; i++) { if (i > 0) if (i % 16 == 0) ret = ret + "\r\n"; a = (BYTE*)(qqdata + i); ret = ret + IntToHex(*(a), 2) + " "; } return ret;}// 高与低位字节互换int __fastcall LittleOrBigEndian(int c, int Len){ BYTE a[4] = {0}, b[4] = {0}; int result = 0; memcpy(&a, &c, Len); switch(Len) {case 2: b[0] = a[1]; b[1] = a[0]; break;case 4: b[0] = a[3]; b[1] = a[2]; b[2] = a[1]; b[3] = a[0]; break; } memcpy(&result, &b, 4); return result;}// 发送数据void __fastcall SendWinCapData(HWND hWd, void *Data, int Len){ TCopyDataStruct D; D.dwData = 0; D.cbData = Len; D.lpData = Data; SendMessage(hWd, WM_COPYDATA, 0, (int)&D);}// 添加柱头void __fastcall TfrmMain::ListViewAddColumn(TListView *lv, String Caption, int Widht){ TListColumn *Col = lv->Columns->Add(); Col->Caption = Caption; Col->Width = Widht;}// 设置柱头void __fastcall TfrmMain::ListViewColumnSetting(int cbbindex){ char *lvCaption[6] = {"序", "标识", "时戳", "源IP:端口", "目标IP:端口", "长度"}; int lvWidth[6] = {50, 60, 80, 120, 120, 50}; if (cbbindex == -1) return; if (cbbindex == cbbProtocol->Tag) return; lvpacket->Columns->Clear(); for (int i = 0; i < 6; i++) ListViewAddColumn(lvpacket, lvCaption[i], lvWidth[i]); switch(cbbindex) { // PC QQ协议默认 case 0: ListViewAddColumn(lvpacket, "版本号", 60); ListViewAddColumn(lvpacket, "命令" , 60); ListViewAddColumn(lvpacket, "包序" , 60); ListViewAddColumn(lvpacket, "QQ号" , 110); ListViewAddColumn(lvpacket, "固定", 180); ListViewAddColumn(lvpacket, "TEA加密数据段", 385); break; default : ListViewAddColumn(lvpacket, "封包数据", 855); break; } // 保存最后更改的 ItemIndex cbbProtocol->Tag = cbbProtocol->ItemIndex;}// 接收WinCap数据void __fastcall TfrmMain::RecvWinCapData(TWMCopyData Message){TListItem *list;TMyData *data = (TMyData*)Message.CopyDataStruct->lpData;list = lvpacket->Items->Add();// 自序String s;s.printf(TEXT("%.4d"), lvpacket->Items->Count);list->Caption = s;//list->Caption = RightStr(list->Caption, 4);// 标识switch(data->flags){ case 0: list->SubItems->Add("SEND"); break; case 1: list->SubItems->Add("RECV"); break;}// 时戳list->SubItems->Add(data->timestr);// 源IPlist->SubItems->Add(data->sipp);// 目标IPlist->SubItems->Add(data->dipp);// 数据长度list->SubItems->Add(IntToStr(data->datalen));switch(cbbProtocol->ItemIndex){ case 0: {// 版本WORD verid = 0;memcpy(&verid, (void*)(data->data + 1), 2);list->SubItems->Add(IntToHex(LittleOrBigEndian(verid, 2), 4));// 命令WORD cmd = 0;memcpy(&cmd, (void*)(data->data + 3), 2);list->SubItems->Add(IntToHex(LittleOrBigEndian(cmd, 2), 4));// 包序WORD seq = 0;memcpy(&seq, (void*)(data->data + 5), 2);list->SubItems->Add(IntToHex(LittleOrBigEndian(seq, 2), 4));// QQ号DWORD QQNum = 0;memcpy(&QQNum, (void*)(data->data + 7), 4);QQNum = LittleOrBigEndian(QQNum, 4);//String QQStr;//QQStr.printf("%ld", QQNum);// 我去这里可以直接转换哦list->SubItems->Add(QQNum);// 位置偏移int offset; // 固定字段BYTE *FixedValue;switch(data->flags){ case 0: FixedValue = new BYTE[11]; memcpy(FixedValue, data->data + 11, 11); list->SubItems->Add(ByteArrToString(FixedValue, 11)); delete [] FixedValue; offset = 11 + 11; break; case 1: FixedValue = new BYTE[3]; memcpy(FixedValue, data->data + 11, 3); list->SubItems->Add(ByteArrToString(FixedValue, 3)); delete [] FixedValue; offset = 11 + 3; break;}int TEALen = data->datalen - offset - 1;BYTE *TEAData = new BYTE[TEALen];memcpy(TEAData, data->data + offset, TEALen);list->SubItems->Add(ByteArrToString(TEAData, TEALen));delete [] TEAData; break; } default : list->SubItems->Add(ByteArrToString(data->data, data->datalen)); break;}}// ListView排序过程int __stdcall ListViewSort(long ItemA, long ItemB, long ParamSort){ TListItem *Item1 = (TListItem*)ItemA; TListItem *Item2 = (TListItem*)ItemB; if (ParamSort != 0) { try { String txt1 = Item1->SubItems->Strings[ParamSort - 1]; String txt2 = Item2->SubItems->Strings[ParamSort - 1]; if (g_BSort) return CompareText(txt1, txt2); else return -CompareText(txt1,txt2); } catch(...){}; }else { if (g_BSort) return CompareText(Item1->Caption, Item2->Caption); else return -CompareText(Item1->Caption, Item2->Caption); } return 0;}// 定义一个捕获的线程__fastcall TMyThread::TMyThread(bool CreateSuspended) : TThread(CreateSuspended){}void __fastcall TMyThread::Execute(){ FreeOnTerminate = true; //OutputDebugStringA("线程已经进入"); StartFunc();}void TMyThread::StartFunc(){ pcap_loop(adhandle, 0, packet_handler, NULL);}//---------------------------------------------------------------------------/* 回调函数调用为每个传入数据包libpcap */void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data){struct tm *ltime;ip_header *ih;udp_header *uh;u_int ip_len;u_short sport,dport;time_t local_tv_sec; // 我的发送数据定义TMyData SendData;(VOID)(param);/* 将时间戳到可读的格式 */local_tv_sec = header->ts.tv_sec;ltime = localtime(&local_tv_sec);strftime(SendData.timestr, sizeof(SendData.timestr), "%H:%M:%S", ltime);// 取回 ip报头的位置ih = (ip_header *) (pkt_data +14 /*14为以太网头长度*/);// 取回 UDP报头的位置ip_len = (ih->ver_ihl & 0xF) * 4;uh = (udp_header *) ((u_char*)ih + ip_len);// 从网络字节顺序转换为主机字节顺序sport = ntohs(uh->sport);dport = ntohs(uh->dport);// 源IP和端口sprintf(SendData.sipp, "%d.%d.%d.%d:%d",ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,sport); // 目标IP和端口sprintf(SendData.dipp, "%d.%d.%d.%d:%d",ih->daddr.byte1,ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4,dport); // 根据定义的协议发送数据switch(g_Protocolflag){ case 0: // 这里过滤掉一些其它端口的包if (sport >= 4000 && sport <= 8000){// 只捕获的QQ不为0if (g_onlyQQ != 0){ DWORD tmpqq = 0; // 读取QQ号 memcpy(&tmpqq, pkt_data + ip_len + 14 + sizeof(uh) + 4 + 7, 4); // 如果等于当前只抓取的QQ号 if (tmpqq == g_onlyQQ) { // 确定QQ封包的位置SendData.data = (char*)(pkt_data + ip_len + 14 + sizeof(uh) + 4);SendData.datalen = header->len - (ip_len + 14 + sizeof(uh) + 4);SendData.flags = dport == 8000 ? 0 : 1;// 发送数据SendWinCapData(g_hWnd, (void*)&SendData,sizeof(SendData) + header->len - 4);//OutputDebugString(ByteArrToString((char*)SendData.data, SendData.datalen).w_str()); }}else{SendData.data = (char*)(pkt_data + ip_len + 14 + sizeof(uh) + 4);SendData.datalen = header->len - (ip_len + 14 + sizeof(uh) + 4);SendData.flags = dport == 8000 ? 0 : 1;// 发送数据SendWinCapData(g_hWnd, (void*)&SendData,sizeof(SendData) + header->len - 4);}}break;default:break;}}void __fastcall TfrmMain::btnStartClick(TObject *Sender){ char errbuf[PCAP_ERRBUF_SIZE];u_int netmask;int i;int inum;struct bpf_program fcode;String ErrMsg;if (cbbProtocol->ItemIndex == -1){ ShowMessage("请选择一个协议进行捕获。"); return;}switch(cbbProtocol->ItemIndex){ case 0 : memset(packet_filter, 0, sizeof(packet_filter));strcpy(packet_filter, "ip and udp port 8000");break; case 1 : case 2 : memset(packet_filter, 0, sizeof(packet_filter));strcpy(packet_filter, "ip and tcp port 8080"); // or port 14000break;}this->Caption = packet_filter;inum = cbbAdapteList->ItemIndex + 1;if(inum < 1 || inum > listCount){ShowMessage("请选择一个适配器。");return;}for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);if ((adhandle= pcap_open_live(d->name,// 驱动名称 65536,// 捕获的数据包大小// 65536 最大值 1,// 混合模式 (非0为混合模式) 1000,// 读取超时以毫秒计算 errbuf// 错误信息 )) == NULL){ErrMsg.printf(TEXT("无法打开适配器 %s WinPcap不支持"), errbuf);ShowMessage(ErrMsg);return;}/* 检查链路层。为简单起见,我们只支持以太网. */if(pcap_datalink(adhandle) != DLT_EN10MB){ShowMessage("这个程序只能在以太网中使用.");return;}if(d->addresses != NULL)/* 检索第一个地址的掩码的接口 */netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;else/* 如果界面是没有地址,我们假设在一个C类网络 */netmask=0xFFFFFF;//编译过滤器if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 ){ShowMessage("无法编译包过滤设置字符串, 请检查语法。");return;}//设置过滤器if (pcap_setfilter(adhandle, &fcode)<0){ShowMessage("设置过滤出错。");return;}//pcap_freealldevs(alldevs);if (adhandle){ //TThread.CreateAnonymousThread( g_hWnd = this->Handle; g_Protocolflag = cbbProtocol->ItemIndex; if (edtqq->Text.Length() == 0) g_onlyQQ = 0; else // 我kao ToInt 不能用,整数大于原来的了位数不够了 g_onlyQQ = LittleOrBigEndian((DWORD)edtqq->Text.ToDouble(), 4); Mythread = new TMyThread(true); Mythread->adhandle = adhandle; Mythread->Start(); btnStart->Enabled = false; cbbAdapteList->Enabled = false; cbbProtocol->Enabled = false; edtqq->Enabled = false; btnStop->Enabled = true;}else ShowMessage("错误,未选择一个适配器或者适配器不支持等。");}//---------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender){char errbuf[PCAP_ERRBUF_SIZE];listCount = 0; String ErrMsg;/* 检索设备列表 */if(pcap_findalldevs(&alldevs, errbuf) == -1){ ErrMsg.printf(TEXT("检索列表设置出错,位于pcap_findalldevs: %s"), errbuf); ShowMessage(ErrMsg); return;}/* 显示列表 */for(d=alldevs; d; d=d->next){++listCount;cbbAdapteList->Items->Add(d->description);if (!d->description) cbbAdapteList->Items->Add(d->name);}if(listCount == 0){mmo1->Lines->Add("没有接口发现!确保WinPcap安装");return;}}//---------------------------------------------------------------------------void __fastcall TfrmMain::btnStopClick(TObject *Sender){ if (adhandle) pcap_close(adhandle); if (Mythread) { if (Mythread->Suspended) {Mythread->WaitFor();Mythread->Terminate(); } } btnStart->Enabled = true; cbbAdapteList->Enabled = true; cbbProtocol->Enabled = true; edtqq->Enabled = true; btnStop->Enabled = false;}//---------------------------------------------------------------------------void __fastcall TfrmMain::cbbProtocolChange(TObject *Sender){ if (cbbProtocol->ItemIndex == cbbProtocol->Items->Count - 1) { AnsiString s = InputBox("自定义过滤规则", "WinCap过滤封包规则:", ""/*"ip and udp port 8000"*/); if (s.Length() == 0) { cbbProtocol->ItemIndex = 0; cbbProtocol->OnChange; }else{ // 最讨厌警告了 OutputDebugString(TEXT("fffff")); if (s.Length() < (int)sizeof(packet_filter)) { memset(packet_filter, 0, sizeof(packet_filter)); strcpy(packet_filter, s.c_str()); //OutputDebugStringA(packet_filter); } else { cbbProtocol->ItemIndex = 0; cbbProtocol->OnChange; } } } ListViewColumnSetting(cbbProtocol->ItemIndex);}//---------------------------------------------------------------------------void __fastcall TfrmMain::lvpacketClick(TObject *Sender){ int Index = lvpacket->ItemIndex; if (Index != -1) { mmo1->Text = lvpacket->Items->Item[Index]->SubItems->Strings[10]; lvpacket->Tag = Index; }}//---------------------------------------------------------------------------void __fastcall TfrmMain::lvpacketColumnClick(TObject *Sender, TListColumn *Column){ lvpacket->CustomSort(&ListViewSort, Column->Index); g_BSort = !g_BSort;}//---------------------------------------------------------------------------void __fastcall TfrmMain::FormDestroy(TObject *Sender){ pcap_freealldevs(alldevs);}//---------------------------------------------------------------------------void __fastcall TfrmMain::btnClearClick(TObject *Sender){ lvpacket->Clear();}//---------------------------------------------------------------------------
附上一张截图吧
0 0
- 企鹅的封包拦截器, 只是技术研究爱好
- 编程只是爱好~~
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- 拦截其它程序的网络数据封包
- (转)拦截其它程序的网络数据封包
- [草稿]关于冒险岛封包拦截程序的开发计划
- Delphi中拦截其它程序的网络数据封包
- 求助 vb6拦截 发送封包代码 和wpe一样的
- 利用HOOK拦截封包原理
- 利用HOOK拦截封包原理
- 利用HOOK拦截封包原理
- 吝啬的国度(DFS)
- JDK支持的字符集
- Ubuntu 关机、重启、注销 命令
- 基于数组和链表的队列实现
- unity 打包资源及网络请求资源包
- 企鹅的封包拦截器, 只是技术研究爱好
- SFINAE使用
- C++ 内联inline
- QT中文显示问题
- Unity中物体的遮挡和显隐效果
- Cocos2d-x3.0 不规则Button
- 行转列
- 背包问题 nyoj106
- javascript转换日期格式