首页
社区
课程
招聘
[原创]突破封锁线:第五章--外围篇
发表于: 2006-5-21 03:15 13435

[原创]突破封锁线:第五章--外围篇

2006-5-21 03:15
13435

【文章标题】: 突破封锁线:第五章--外围篇
【文章作者】: Austin
【作者邮箱】: austiny.cn@gmail.com
【软件名称】: 神州数码网络客户登陆程序
【软件大小】: 不重要
【下载地址】: 自己搜索下载
【加壳方式】: UltraProtect 1.x
【保护方式】: 加壳 网络封禁
【编写语言】: VC6.0
【使用工具】: VC6.0
【操作平台】: Windows2003
【软件介绍】: 神州数码小区宽带网络登陆客户端,诸多限制
【作者声明】: 技术交流中。。。
--------------------------------------------------------------------------------
【详细过程】
  网络采用了神州数码公司的网络管理产品。主要弊端是:1.封禁代理;2.封禁路由器;3.封禁BT。
  (以下省略声讨文字若干),总之,有压迫的地方就有反抗。让我们来解决它。
  
  一、知己知彼
  
  神州数码采用了802.1x的认证方式,即认证流于数据流分开,
  这样降低了认证服务器的负担,提高了网络通讯效率,也为我们破解提供了方便。
  
  下面描述一下正常情况下,使用客户端登陆的流程:
    1.打开客户端(废话)
    2.选择网卡,申请服务(此时客户端会向认证服务器发送第一个数据包,我们叫它服务包)
    3.填写正确的用户名和密码,登陆(此时客户端会向服务器发送第二个数据包,认证包)
    4.此后每隔三十秒,客户端会向服务器发送保持登陆的数据
     (这就是第三个包,连接包,根据收到的反馈信息,由客户端会随机产生)
    5.在客户端运行过程中,它会不断检验代理和BT软件的运行,一旦发现,主动断开连接。
  
  有上述分析,可以看到,路由器的封禁是在第3步实现的(通过读取网卡信息),而代理和BT的封禁在第5步实现。
  
  二、弱点分析
  
    1.在认证方式的设计中,一旦登陆成功,其连接包将不会改变,
      所以只要截下此包,以三十秒的周期向服务器发送,便可以强行关闭客户端,其代理和BT封禁便迎刃而解。
    2.对于路由器的封锁稍微麻烦些,关键是正确登陆信息的取得和发出,和保证客户端算出对应的连接信息,
      所以在此过程中,不可以抛开客户端。
  
  三、作战部署
  
  由前面的分析可以看到,破解的核心在于截包(网络嗅探)。
  通常的编程方法是利用wincap等开发库监视底层通讯,或者将网卡置于混杂模式,过滤通过其所有数据。
  但这些方法太面向底层,复杂且开销过大。杀鸡焉用牛刀?
  
  对付小小的神州数码,决定采用一种较为轻便优雅的方法----API拦截(或叫API钩子)。
  因为在Windows平台上,程序的功能最终都是通过调用Windows API来实现的。
  如这里的网络通讯,收发都使用了UDP协议,典型的由Windows API函数sendto和recvfrom来实现。
  所以截获了这两个函数,就能轻松完成截包工作。
  (这样,我们的任务就和大多数网游外挂要完成的任务相仿了)
  
  不过,要截获API函数,也不是一件简单的工作。
  因为API钩子的编写不像键盘钩子(用于偷密码),鼠标钩子(察看*密码、屏幕取词等)有现成的钩子函数可以使用,
  它是一种非常规编程技术。说白了,就是要我们自己的代码在别的程序的空间内运行。
  我们知道,在Windows平台下,使用了虚拟地址空间技术将不同进程的代码和数据分离,
  但dll文件在整个内存中是共享的,所以,通过使目标进程加载我们自己编写的dll文件,
  就有可能让自己的代码在目标进程中执行,这就是所谓的dll注入技术。
  
  四、瞒天过海
  
  我们提到要使用dll注入技术来完成我们的工作。
  幸好,Windows API中为我们提供了一个美妙的函数CreateRemoteThread,恰好可以为我们所用。
  这个函数用来在目标进程中运行一个线程,而如果这个线程所完成的任务就是加载我们的dll文件,
  那注入的任务不就正好搞定了吗?
  但是还有一个问题,我们可以让目标进程运行LoadLibraryA这个函数,因为它在每个进程空间里的地址都是一样的,
  但我们的dll路径如何传给目标呢?
  不用急,Windows也为我们准备好了,
  VirtualAllocEx和WriteProcessMemory函数正好完成了在目标进程里内存分配和写入数据的任务。
  这样,只要三个函数配合使用,目标进程就乖乖的加载好了我们的dll。
  
  BOOL WINAPI InjectLib(DWORD dwProcessId, LPCSTR strLibFile)
  {
          BOOL result=FALSE;
          HANDLE hProcess=NULL, hThread=NULL;
          LPCSTR strLibFileRemote=NULL;
          __try
          {
                  hProcess=OpenProcess(
                          PROCESS_CREATE_THREAD|
                          PROCESS_VM_OPERATION |
                          PROCESS_VM_WRITE,
                          FALSE,dwProcessId);
                  if(hProcess==NULL)__leave;
  
                  int cch=1+strlen(strLibFile);
                  int cb= cch*sizeof(CHAR);
  
                  strLibFileRemote=(LPCSTR)
                          VirtualAllocEx(hProcess,NULL,cb,MEM_COMMIT,PAGE_READWRITE);
                  if(strLibFileRemote==NULL)__leave;
  
                  if(!WriteProcessMemory(hProcess,(PVOID)strLibFileRemote,
                          (PVOID)strLibFile,cb,NULL))__leave;
  
                  PTHREAD_START_ROUTINE pfnThreadRtn=(PTHREAD_START_ROUTINE)
                          GetProcAddress(GetModuleHandle("Kernel32"),"LoadLibraryA");
                  if(pfnThreadRtn==NULL)__leave;
  
                  hThread=CreateRemoteThread(hProcess,NULL,0,pfnThreadRtn,(PVOID)strLibFileRemote,0,NULL);
                  if(hThread==NULL)__leave;
  
                  WaitForSingleObject(hThread,INFINITE);
  
                  result=TRUE;
          }
          __finally
          {
                  if(strLibFileRemote!=NULL)
                          VirtualFreeEx(hProcess,(PVOID)strLibFileRemote,0,MEM_RELEASE);
                  if(hThread!=NULL)
                          CloseHandle(hThread);
                  if(hProcess!=NULL)
                          CloseHandle(hProcess);
          }
          return result;
  }
  
  
  注意,现在只是加载,并没有运行。不过我们知道,每次dll被加载时,它的DllMain函数都会被调用,
  是的,聪明的你应该已经想到,我们的劫持代码就是在这儿被调用的。
  
  BOOL APIENTRY DllMain( HANDLE hModule,
                                            DWORD  ul_reason_for_call,
                                            LPVOID lpReserved
                                            )
  {
          switch(ul_reason_for_call)
          {
          case DLL_PROCESS_ATTACH:
                  {
                          //MessageBox(NULL,"Attached to Process","Caution",MB_OK);
                          WORD wVersionRequested;
                          WSADATA wsaData;
                          int err;
                           wVersionRequested = MAKEWORD(2,2);
   
                          err = WSAStartup( wVersionRequested, &wsaData );
                          if(err)MessageBox(NULL,"WSAStartup Error","Error",MB_OK);
  
                          s=socket(AF_INET,SOCK_DGRAM,0);
                          MyFunc();     //这里面就是我们的劫持代码
                  }
          case DLL_PROCESS_DETACH:
                  {
                          //MessageBox(NULL,"Detached from Process","Caution",MB_OK);
                  }
          case DLL_THREAD_ATTACH:
                  {
                          //MessageBox(NULL,"Attached to Thread","Caution",MB_OK);
                  }
          case DLL_THREAD_DETACH:
                  {
                          //MessageBox(NULL,"Detached from Thread","Caution",MB_OK);
                  }
          }
      return TRUE;
  }
  
  
  下面来关注劫持的问题。大家知道,Windows的每个进程都会维护一张虚拟函数地址表。
  程序显示链接的函数地址都保存在这张表中。
  所以,只要修改了这张表,就可以使得对API函数的调用编程对我们自己函数的调用(amazing, huh?)。
  于是话不多说,改!
  可是,我们会郁闷的发现,由于加壳的缘故,
  神州数码的客户端并没有使用显示链接到SendTo函数(就是我们要劫持的那个,记得吗),
  而是用LibraryA函数动态加载。所以,修改虚拟函数的方法失效了。
  
  真的没办法了吗?当然不是。不然我还写这篇东西干吗呢?
  凡走过比留下痕迹。改还是要改的,不过不改SendTo了,而是改GetProcAddress。
  因为动态加载通过它来返回函数地址,只要我们把对SendTo的加载返回成自己函数的地址,瞒天过海之计就成了。
  
  extern "C" __declspec(dllexport) void MyFunc()
  {
          HOOKAPI a;
          LPHOOKAPI pHookApi=&a;
          HMODULE hm1=(HMODULE)0x00400000;
  
          pHookApi->szFunc=(LPCSTR)"GetProcAddress";
          pHookApi->pNewProc=(PROC)MyGetProcAddress;
          HookAPIByName(hm1,"kernel32.dll",pHookApi);
                 
          pHookApi->szFunc=(LPCSTR)"LoadLibraryA";
          pHookApi->pNewProc=(PROC)MyLoadLibrary;
          HookAPIByName(hm1,"kernel32.dll",pHookApi);  //这里劫持了LoadLibraryA函数
  }
  
  typedef struct tag_HOOKAPI
  {
          LPCSTR szFunc;//待HOOK的API函数名
          PROC pNewProc;//新的函数指针
          PROC pOldProc;//老的函数指针
  }HOOKAPI, *LPHOOKAPI;
  
  PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportMod)
  {
          //首先是DOS头
          PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
          if(pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
          PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader + (DWORD)(pDOSHeader->e_lfanew));
          if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
          //如果没有Import部分,返回失败
          if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
                  return NULL;
          //取Import部分
          PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
                  ((DWORD)pDOSHeader + (DWORD)(pNTHeader->OptionalHeader.
                  DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
          //寻找与szImportMod相配部分
          while (pImportDesc->Name)
          {
                  PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader + (DWORD)(pImportDesc->Name));
                  if (stricmp(szCurrMod, szImportMod) == 0)
                          break; //找到
                  pImportDesc++;
          }
          if(pImportDesc->Name == NULL) return NULL;
          return pImportDesc;
  }
  
  HookAPIByName(HMODULE hModule/*被HOOK的目标进程MODULE*/, LPCSTR szImportMod/*如GDI32.DLL*/,LPHOOKAPI pHookApi/*指定函数
  名,如"MessageBoxW"*/)
  {
          PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
                  GetNamedImportDescriptor(hModule, szImportMod);
          if (pImportDesc == NULL)
                  return FALSE; //需要改换的API不能取到正确描
          PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->OriginalFirstThunk));
          PIMAGE_THUNK_DATA pRealThunk =
                  (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->FirstThunk));
          while(pOrigThunk->u1.Function)
          {
                  if((pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
                  {
                          PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)hModule + (DWORD)
  (pOrigThunk->u1.AddressOfData));
                          if(pByName->Name[0] == '\0')
                                  return FALSE; //失败
                          if(strcmpi(pHookApi->szFunc, (char*)pByName->Name) == 0)
                          {
                                  //改变thunk保护属性
                                  MEMORY_BASIC_INFORMATION mbi_thunk;
                                  VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
                                  VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, PAGE_READWRITE,
  &mbi_thunk.Protect);
                                  //保存原来的API函数指针
                                  if(pHookApi->pOldProc == NULL)
                                          pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
                                  //改变API函数指针
                                  pRealThunk->u1.Function = (PDWORD)pHookApi->pNewProc;
                                  //将thunk保护属性改回来
                                  DWORD dwOldProtect;
                                  VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
                                          mbi_thunk.Protect, &dwOldProtect);
                          }
                  }
                  pOrigThunk++;
                  pRealThunk++;
          }
          SetLastError(ERROR_SUCCESS);
          return TRUE;
  }
  
  
  五、实战演练
  
  现在万事俱备,只欠一个我们反复提到的----自己的函数就可以完成全部的任务了。
  那这个自己的函数要做些什么呢?其实很简单,就是把通过它发送的数据包一一记录下来备用即可。
  而记录的数据,将要被我们自己的程序所使用,所以存在dll的共享数据区里面。
  这是其一,对于解除代理和BT的封禁,这已经足够了。
  但要解决路由器的问题,还需要再加一点功能。
  我们会把正确的不带路由器的认证包(记为A包)存起来,然后在有路由器环境下,
  客户端会产生指示使用了路由器的认证包(记为B包),用A包代替B包发送出去,就起到了欺骗服务器的作用,
  解除了路由器的封禁。
  
  static int WINAPI Mysendto (
    SOCKET s,                        
    const char FAR * buf,            
    int len,                        
    int flags,                       
    const struct sockaddr FAR * to,  
    int tolen                        
  )
  {
          int ii=0;
          if(len!=26)
          {
                  if(statue_client==0)
                  {
                          if(stat_authorize==0)
                          {
                                  for(int tt=0;tt<len;tt++)
                                  {
                                          *(buf_authorize+tt)=*(buf+tt);
                                  }
                                  len_authorize=len;
                                  tolen_authorize=tolen;
                                  flags_authorize=flags;
                                  strcpy(to_authorize.sa_data,to->sa_data);
                                  to_authorize.sa_family=to->sa_family;
                                  stat_authorize=1;
                                  ii=::sendto(s,buf,len,flags,to,tolen);
                          }
                          else
                          {
                                  ii=::sendto(s,buf_authorize,len_authorize,flags_authorize,&to_authorize,tolen_authorize);
                          }
                          statue_client=1;
                  }
                  else if(statue_client==1)
                  {
                          statue_client=2;
                          for(int tt=0;tt<len;tt++)
                          {
                                  *(buf_keepconn+tt)=*(buf+tt);
                          }
                          len_keepconn=len;
                          tolen_keepconn=tolen;
                          flags_keepconn=flags;
                          strcpy(to_keepconn.sa_data,to->sa_data);
                          to_keepconn.sa_family=to->sa_family;
                          stat_keepconn=1;
                          statue_client=3;
                          ii=::sendto(s,buf,len,flags,to,tolen);
                  }
          }
          else
          {
                  for(int tt=0;tt<len;tt++)
                  {
                          *(buf_getservice+tt)=*(buf+tt);
                  }
                  len_getservice=len;
                  tolen_getservice=tolen;
                  flags_getservice=flags;
                  strcpy(to_getservice.sa_data,to->sa_data);
                  to_getservice.sa_family=to->sa_family;
                  stat_getservice=1;
                  ii=::sendto(s,buf,len,flags,to,tolen);
          }
          return ii;
  }
  
  
  六、屠城三日
  
  至此,神州数码的三大封禁已基本被我们解决,
  剩下来的工作,就是愉快的上网,愉快的用代理,愉快的用路由器,愉快的BT了。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年05月13日 3:00:40


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (23)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
2006-5-21 03:16
0
雪    币: 216
活跃值: (77)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
好文!支持

另外,想问问楼主一个关于dll注入的问题,有时候一些程序是在启动时调用相关API的(如findwindow),我们的dll还没来的及注入,它就已经调用了该api了,本来想hook这些api的,但是迟了,请问有什么办法可以以最快的速度把dll注入其它程序呢?
另:用CreateProcess插入代码应该可以,哪有例子看看啊。
2006-5-21 05:20
0
雪    币: 235
活跃值: (100)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
4
最初由 dezone 发布
好文!支持

另外,想问问楼主一个关于dll注入的问题,有时候一些程序是在启动时调用相关API的(如findwindow),我们的dll还没来的及注入,它就已经调用了该api了,本来想hook这些api的,但是迟了,请问有什么办法可以以最快的速度把dll注入其它程序呢?
另:用CreateProcess插入代码应该可以,哪有例子看看啊。


这个函数配合InjectLib函数可以完成这个功能:

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
LPPROCESS_INFORMATION p=new _PROCESS_INFORMATION;
STARTUPINFO ss=
{
  sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  BOOL b=::CreateProcess(EXEPATH,"",NULL,NULL,TRUE,CREATE_SUSPENDED,NULL,NULL,&ss,p);
  DWORD err;
  if(!b)
  {
   err=GetLastError();
   return -1;
  }
  InjectLib(p->dwProcessId,DLLPATH);
  ::ResumeThread(p->hThread);
  return 0;
}
2006-5-21 08:11
0
雪    币: 236
活跃值: (35)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
好东西 学习中
2006-5-21 08:12
0
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
顶!!!!!1

崇拜中.....
2006-5-21 11:08
0
雪    币: 267
活跃值: (235)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
7
写得不错,学习了
2006-5-21 15:01
0
雪    币: 250
活跃值: (103)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
2006-5-21 17:55
0
雪    币: 222
活跃值: (40)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
这个要好好支持一下
2006-5-21 18:48
0
雪    币: 250
活跃值: (30)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
非常感谢楼主写的这篇文章,不过我在看完后仍然不知道如何用VC调试,自己胡乱摸索了一通,可是我对dll文件注入和VC的使用完全不了解,所以编译总是出现一个error ,错误如下:

--------------------Configuration: mydll - Win32 Debug--------------------
Compiling...
mydll.cpp
d:\program files\microsoft visual studio\myprojects\mydll\mydll.cpp(243) : fatal error C1010: unexpected end of file while looking for precompiled header directive
Error executing cl.exe.

mydll.dll - 1 error(s), 0 warning(s)

楼主能够详细说明如何来使用VC编译,然后如何使用吗?谢谢
2006-5-21 18:56
0
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
学习中。。。。
2006-5-21 19:22
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
超强,顶.....
2006-5-21 22:02
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
破格录取为中级..........呵呵
2006-5-21 23:00
0
雪    币: 95
活跃值: (419)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
14
收藏下来慢慢研究........,谢谢啊
2006-5-21 23:17
0
雪    币: 223
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
五篇皆精华,厉害
2006-5-22 08:32
0
雪    币: 253
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
16
真是一篇好文章,思路清晰,技术更是牛!

网络这方面的文章更是不可错过~
2006-5-22 09:03
0
雪    币: 1316
活跃值: (512)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
17
DLL注入直接用LoadLibrary作线程函数的想法太精妙了,五篇全部收藏学习,期待更多好文章......
2006-5-22 11:14
0
雪    币: 465
活跃值: (667)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
18
uppppppp
2006-5-22 20:14
0
雪    币: 176
活跃值: (100)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
19
请教:802.1不通过用户密码验证无法保持连接吧?是否可以登陆完下线,然后用自己的程序发包连接?
2006-5-23 03:38
0
雪    币: 216
活跃值: (77)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
最初由 austiny 发布
这个函数配合InjectLib函数可以完成这个功能:

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
........

感谢austiny兄的回复。(不好意思,才上看雪,刚看到)
不过这个使用了CreateRemoteThread,只能在2k,xp以上系统下使用,98下不能使用。

另外,《windows核心编程》中有提到用CreateProcess插入代码的方法来实现dll的注入(最后提到的一种方法,这种方法的好处是98,2k,xp均可),但却没有给出代码,只给出大概的思路,我做了一下没成功,网上也找不到相关例子,不知道哪位兄弟做过。
2006-5-24 07:22
0
雪    币: 203
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
21
不知道网络验证的本地验证器可不可以依次建立一个“本地验证生成七?”。
2006-5-24 14:01
0
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
事隔 多日         还是一头雾水!

在崇拜一下 以后在学
2006-9-23 14:20
0
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
23
几天没来,又一个牛人诞生在看雪!
此人的文章条理清晰,引人入胜。比CCdebug有过之而无不及!而且功力饱满,几乎脱壳、网络分析、pe结构分析,汇编、api编程等都精通,乃全才啊。
你怎么练就的这一套,难道石头里蹦 出来的不成?!
2006-9-23 17:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
不知道你可不可以把你改了的神州数码的程序发给我一下,我是用的这个客户端,我本来想自己去改,但是我发现里面涉及的东东太多了,我很多根本不怎么懂, 哎.你功力太深厚了,呵呵   
  不知道可以发给我不,谢谢 我的邮箱是dwzxwjshuang@sina.com

不过这篇文章也让我学到了很多东西
2006-11-11 11:07
0
游客
登录 | 注册 方可回帖
返回
//