醉卧沙场君莫笑 古来征战几人回
RSS icon Email icon Home icon
  • QQ验证码老是输不对的原因

    Posted on 四月 29th, 2008 admin 4 comments

    这些天,很多朋友碰到了一个问题,就是在QQ登录时,碰到需要输入验证码,可是输入正确后,却要继续输入,可能要好多次才好,据说还有人连续输入30次都不对。由此,由不断的通不过,陷入不断输入的循环,简直让人崩溃。

    今天终于知道了原因。原来,现在所有恶意行为,包括发送垃圾消息,查QB、消费QB、修改密码等,都是纯手工行为。 雇佣1 个人,每天50 元(含吃住),可以输入2W 次验证码,假设平均输入3 次验证码成功登录1 次,可以发出10 条诈骗消息,上当的比例是1/10000 ,如果上当,则获利300 元。那么每雇佣一人平均获利2W*10*300/3/1W=2K 元,假设平均输入3 次验证码可以查询一个QB ,QB 3 折,那么雇佣1 人平均获利2W*0.1*5*0.3/3=1K 元 。

     因此,TX对这些人也进行打击,当怀疑一个IP是恶意IP时,会对其进行限制,使n次输入只有一次真正算对,这样,就会有人连续很多次不能正确输入验证码了。

    那为什么一个用户的IP会被认为是恶意IP呢?除了程序出错引起的BUG,则通常是另一种情况:

    原来,现在雇人手动输入验证码的家伙也是很聪明的,你限IP我就变IP。ADSL网络有个特点,断开重新播号,IP就变了。那这个旧IP哪去了呢?被ISP搞去分给别人了,那个被分到的家伙就倒霉了,重复输入好多次验证码才会进得去了。

    这简直是一套七伤拳啊,杀敌三分,自损七分。虽然确实可以杀敌。

    想要摆脱这种老是输入的局面也有招,参考解决方案:

    1. 重新播放,换IP上,也许马上就可以上了。
    2. 停一停,过一个小时再来上,由于这个IP闲了下来,会渐渐被排除恶意,也许就上得去了。

     

     

  • 做了Perl 5.10.0的chm文档

    Posted on 四月 27th, 2008 admin 5 comments

    http://perldoc.perl.org/ 上可以看perl的文档。这自然是一件很好的事。

    不过我还是比较习惯看chm的文档,比如我要找函数stat的说明,用网页方式的话我要打开浏览器,上到perldoc网站,定位到perlfunc那一页,再找到stat,这个时候可能是需要ctrl+f一下才找得到的,然后再转过去。有了chm文档,就方便了,打开后在index里输入stat回车即可看了。

    5.8.8的就先不做了,一般也用不到这些差别。

    在这里下载

     

  • perl utf8值得注意的一点

    Posted on 四月 26th, 2008 admin No comments

    use utf8; my $var="测试啊"; print "OK1\n" if ($var=~/试/); printf "str: %s length: %d\n", $var, length($var); $var = `echo $var`; chomp($var); print "OK2\n" if ($var=~/试/); printf "str: %s length: %d\n", $var, length($var);

    结果:

    debian:~# ./test.pl  OK1 Wide character in print at ./test.pl line 11. str: 测试啊 length: 3 str: 测试啊 length: 9

    可以看到输出内容虽然没有变,但是第一次$var是内在utf8的,而第二次内在格式则不是了。

    求长度的结果是第一个三个字符,正确,第二次九个字符,说明内存格式占了9个字符(或者叫octet?搞不懂perl的名词了),编码为utf-8。

    这就会带来问题,导致regexp匹配不了。

    事实上,我遇到的问题并不是通过IPC读来的字符串,而是直接opendir后由readdir得到的中文文件名。这样得到的也像第二次的$var一样,输出正确,但内在格式不对,导致脚本异常。

    既然内存格式已经是utf-8编码了,那解决方案自然就是把9个字符变回三个,这是一个解码的过程。对perl不是很熟,但找了一下,还是找到了答案。只消加入一句即可:

    $var = decode('utf8', $var);

     

  • 在activeX对象中判定所在网页

    Posted on 四月 22nd, 2008 admin No comments

    一个很原始的需求是:做了一个activex对象,只希望它在某些站点中使用。不希望在其它站点中使用。

    或者说,不希望被别的人偷用。或者不希望被广泛使用而造成安全问题。

    比如:在www.foo.com中可以使用,在www.bar.com中不能使用。

    由于只有ie能创建并访问activeX,因此本文只适用于IE。

    要解决这个问题,关键是两点:

    1. 如何在activex对象中判定当前域名或当前所在网页url。
    2. 如何在判定不能使用后,禁止用户使用。

    对于2,应该是简单的,不能使用后,所有的接口都返回E_FAIL即可。当然,这里有更好的策略,还可以通知到用户。

    对于1,我以前一直没有找到办法,虽然办法很简单。无非是实现一下IObjectWithSite,这样IE在创建activex的时候会把site传给创建的activex,这样就容易取得当前url了:

    HRESULT CMyActiveX::SetSite(IN IUnknown *pUnkSite) { CComQIPtr<IServiceProvider> spsp = pUnkSite; pUnkSite->QueryInterface(&m_WebBrowser2); m_WebBrowser2->get_LocationURL(&m_bsUrl); //more.. }

    对于2,通知到用户的办法是,实现IObjectSafety,然后,在GetInterfaceSafetyOptionsSetInterfaceSafetyOptions中,根据当前情况返回E_FAIL就行了。

    以上内容是是研究微软的某软件的一个activex控件在不同网页里的表现不同而发现的。

  • 不支持抵制法货的人,你们真的是理智吗?

    Posted on 四月 18th, 2008 admin No comments

    张朝阳站出来支持抵制法货了,很好。给白岩松反戈一击了。道理不想说太多,反对抵制的人无非是说,家乐福里的都是中国人,卖的也差不多都是中国货,抵制了就会给自己人带来损失。其实我们抵制法国货不意味着我们不买货了,不去家乐福还可以去别的超市。中国货不在家乐福卖,一样可以在其它超市卖。因此这个道理站不住脚。还有的人是摆出一幅以德服人的面孔,说只要我们自己做好了就行了,以高洁的行动来反衬他们的罪恶,这就更错误了。子曰:以德报怨,何以报德?以直报怨,以德报德。中国没有犯错的时候,外国编一些来评击,外国犯了错了,我们为什么不能想点办法?抵制一下有何不对?

    请问:欧洲人抵制我们中国货,烧了我们的鞋子仓库,你知道吗?制造贸易避一句那是欧洲的FQ就可以解决问题了吗?欧盟一直在说我们倾销,千方百计制造贸易壁垒,这不是抵制是什么?人欺我可以,我欺人就不对?就是FQ,就不理智?这种所畏的“理智”其实只是一昧的逃避罢了,想要通过这种与众不同来表达自己的清高,那就大错特错了。

    再说,抵制确实发生了效果,法国有些媒体已经报道了中国的抵制,并提出了一些担忧。找找新闻就能找到。试想一下,如果每个中国人都“理智”,都当没事发生,都以德服人,欧盟就不“反倾销”了?法国就不支持ZD了?人家high着呢,只会以为你们好欺负。

    还是孔子说得好,以直报怨,以德报德。今天我们的抵制,不过是以彼之道,还施彼身罢了。抵制家乐福,人人有责。从我做起。

     

  • 谈谈有目的的学习

    Posted on 四月 16th, 2008 admin No comments

    事情的起因是,某人觉得学了C++的语法之后,似乎没什么用,不知道要怎么去用,这样的话,很难继续的学下去。

    其实,成年人学习是一定要有目的的,小孩子还好,可以为了学而学。人一长大,学习一旦没有目的,就学不下去了。

    对于C++,我是学了一点点基础之后,就直接按钮某杂志的文章写一个在线发送寻呼信息的程序,结果还把我一个同学搞毛了。
    对于一般的情况,也是类同此理。自己找一些好玩的应用,遇到不足之处再补相关知识,这样学习起来就十分有劲了。好玩的应用是很多的,比如写一个在桌面上自由活动的小动物,可以把自己的宠物放上去。或者是用javascript写个很小的游戏,或者是用flash做一个故事发给你的朋友,或者是对自己感兴趣的方面搭建一个小网站。

    不过做这些事的时候,都要对自己提高要求,精益求精,不要因为这个软件或功能别人已经做过了就不去做。最好也不要直接用别人的组件,从底层一点一点慢慢搭。搭完之后,了解就深入了,知识结构就上去了。

    比如写一个相册,你可以不用gdiplus库或其它图像库,自己去研究文件的格式,或者,自己去研究一下图像是如何变灰的,当然如果这些你都已经了解了,那也可以再去研究再深一步的效果比如雨点效点。

    又如搭网站的时候,先不要使用别人的ajax库,自己先手动做一个ajax模型,这个模型虽不如成熟模型那么方便,但搭的过程会很有意思,也能让自己理解别人为什么要那样设计。

    在理解的基础上,再去使用别人的库,就会更得心应手了,出了问题,也不会茫然无措了。

    当然,有目的的学习而不是系统的学习也是有缺点的,缺点是这样学了很久之后,可能还有少量很基础的东西不知道。因为毕竟不是系统的学。有时会出点低级错误被人bs。我感觉这样就像张三丰练的九阳真经,听些短言残章,虽有所悟,但是毕竟不系统。不能给小无忌治寒冰掌之毒,但不妨碍他成为武林泰斗。

  • Javascript 终级“解密”

    Posted on 四月 15th, 2008 admin No comments

    解密加上引号,因为并不是真正的解密。

    现在很多网站不让你看js源码,就想办法变形,最后再document.write出来。

    显然不管它怎么变形,最后总要调用document.write,我们直接在document.write的实现处,

    用native调试来下个断点,不就可以读出来了?

    找到document.write函数:

    C:\>syminfo /S E:\symbol C:\WINDOWS\ie7\mshtml.dll | grep ::write 7DDA72FB[+   0] ?writeln@CDocument@@QAGJPAUtagSAFEARRAY@@@Z             public:long __stdcall CDocument::writeln(struct tagSAFEARRAY *) 7DCE0741[+   0] ?write@CDocument@@QAGJPAUtagSAFEARRAY@@@Z               public:long __stdcall CDocument::write(struct tagSAFEARRAY *)

    显然,直接在这两个函数下断点就可以了。

    下完需要读一个VARIANT结构,里面是一个VT_I8的safearray,需要了解safearray的结构,就不赘述了。

     因为还有更好的办法!
    用firebug直接看就是了。原来也不清楚,原来firebug看时,一个javascript段里调用了document.write之后,就会在dom树里紧跟着看到另一个<script></script>的段。所以,直接用firebug翻看就行了。真是简单啊。

    翻看后可以找个工具把代码格式化一下。

    有个软件叫sourceformatx,好像已经有四年没有更新了。找了一下crack的文章,说这个软件破解不好会直接破坏系统,因此没有人破解。由于四年过了,现在已经可以搜到完美破解版,但是不是真的完美呢?要是有一点不完美,系统就要挂了,怕怕。所以先不用了,要用也在vmware里用。

    thank creese@newsmth.

     

  • 晕,我的韩国代理不能用了。

    Posted on 四月 15th, 2008 admin No comments

    222.122.249.5:8080

    非常快非常好的代理,如今挂了。不能连了。

    google了一把这个IP,一堆的记录啊天啦。

    不过现在终于倒了。

    也好,搜这个IP能找到很多不错的代理网站。

    用代理这个关键字反而找不到这么好的。呵呵,让SEO见鬼去吧。

  • windows 注册表的初始化

    Posted on 四月 15th, 2008 admin No comments

    看了看wrk的代码,发现一个函数会调用ZwCreateFile打开注册表的文件,这个函数就是CmpOpenHiveFiles

    这个函数会打开两个注册表文件,master为不带后辍的,secondary为带后辍的。

    system32\config\system和software就是这样被加载的。

     

    查找过程,先找SAM,这个词不常见,grep一下找到一个配置表:

    HIVE_LIST_ENTRY CmpMachineHiveList[] = {     { L"HARDWARE", L"MACHINE\\", NULL, HIVE_VOLATILE    , 0                         ,   NULL,   FALSE,  FALSE,  FALSE},     { L"SECURITY", L"MACHINE\\", NULL, 0                , 0                         ,   NULL,   FALSE,  FALSE,  FALSE},     { L"SOFTWARE", L"MACHINE\\", NULL, 0                , 0                         ,   NULL,   FALSE,  FALSE,  FALSE},     { L"SYSTEM",   L"MACHINE\\", NULL, 0                , 0                         ,   NULL,   FALSE,  FALSE,  FALSE},     { L"DEFAULT",  L"USER\\.DEFAULT", NULL, 0           , CM_CMHIVE_FLAG_UNTRUSTED  ,   NULL,   FALSE,  FALSE,  FALSE},     { L"SAM",      L"MACHINE\\", NULL, HIVE_NOLAZYFLUSH , 0                         ,   NULL,   FALSE,  FALSE,  FALSE},     { NULL,        NULL,         0, 0                   , 0                         ,   NULL,   FALSE,  FALSE,  FALSE}     };

    注册表应该就是从这里初始化的了。

    再找引用:

        for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++) {         ASSERT( CmpMachineHiveList[i].Name != NULL );         //           // just spawn the Threads to load the hives in parallel         //           Status = PsCreateSystemThread(             &Thread,             THREAD_ALL_ACCESS,             NULL,             0,               NULL,             CmpLoadHiveThread,             (PVOID)(ULONG_PTR)(ULONG)i             );           if (NT_SUCCESS(Status)) {             ZwClose(Thread);         } else {             //               // cannot spawn thread; Fatal error             //               CM_BUGCHECK(BAD_SYSTEM_CONFIG_INFO,BAD_HIVE_LIST,3,i,Status);         }        }   

    这里为每个hive创建一个线程,进入CmpLoadHiveThread函数:

      

      // <sysroot>\config\hive     if (CmpMachineHiveList[i].CmHive == NULL) {         //         // Hive has not been initialized in any way.         //         CmpMachineHiveList[i].Allocate = TRUE;         Status = CmpInitHiveFromFile(&FileName,                                      CmpMachineHiveList[i].HHiveFlags,                                      &CmHive,                                      &(CmpMachineHiveList[i].Allocate),                                      CM_CHECK_REGISTRY_CHECK_CLEAN                                      );         if ( (!NT_SUCCESS(Status)) ||              (!CmpShareSystemHives && (CmHive->FileHandles[HFILE_TYPE_LOG] == NULL)) )         {             ErrorParameters = &FileName;             ExRaiseHardError(                 STATUS_CANNOT_LOAD_REGISTRY_FILE,                 1,                 1,                 (PULONG_PTR)&ErrorParameters,                 OptionOk,                 &ErrorResponse                 );         }  

    CmpInitHiveFromFile会调用CmpOpenHiveFiles,后者会调用zwcreatefile打开相应的注册表文件。

    如果要保护注册表,在创建线程前处理一下就好了。还只是猜想,哪天闲出毛病了再试试。

     

  • 绕过dbghelp手动获取unloaded module(dll)信息

    Posted on 四月 15th, 2008 admin No comments

    dbghelp有个功能,那就是MiniDumpWithUnloadedModules,
    当os维护了已经unload的dll的列表时,它会在minidump文件中存入unloaded modules的信息。
    只可惜的是,msdn中明说了,dbghelp 5.1并不支持这个标志。
    看了一下winxpsp2自带的dbghelp.dll,恰好是这个版本的。

    这一个标志是很重要的,因为某些BUG,经查恰好运行到一个非法的地址时Crash,
    而这个非法的地址怎么看都像是原来有一个dll恰好加载在这里,但如今
    这个dll已经被FreeLibrary了。如果有了这个unload modules的信息,
    那么这个BUG就很容易找出来。如果没有的话,就麻烦了,因为程序代码
    中都是com指针,虚表调来调处的根本不知道调用处正常情况应当调用的是哪个dll。
    既然这个标志重要,自然应当把它启用,但winxp sp2恰恰不支持,如果为了这一个功能
    就打包一个体积不小的dbghelp.dll的话,也划不来。

    因此,想到一点:
    何不自己把unloaded modules的信息,记载下来跟.dmp文件一起上报呢?

    如是开始研究,先写了一个很小的程序来实现产生minidump的功能,然后用ollydbg来调试,
    看dbghelp.dll是如何拿到unloaded modules的信息的。
    程序虽小,五脏俱全,代码如下:

    int Filter(_EXCEPTION_POINTERS * source, _EXCEPTION_POINTERS * dest) {  *dest->ContextRecord = *source->ContextRecord;  *dest->ExceptionRecord = *source->ExceptionRecord;  return EXCEPTION_EXECUTE_HANDLER; } void Doxx(EXCEPTION_POINTERS * p) {  HANDLE ho = CreateFile(L"oo.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, 0);  _MINIDUMP_EXCEPTION_INFORMATION ExInfo;  ExInfo.ThreadId          = GetCurrentThreadId();  ExInfo.ExceptionPointers = p;  ExInfo.ClientPointers    = TRUE;  MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), ho, MiniDumpWithUnloadedModules, &ExInfo, 0, 0); } int main() {  EXCEPTION_RECORD ExceptionRecord;  CONTEXT ContextRecord;  EXCEPTION_POINTERS ptr;  ptr.ExceptionRecord = &ExceptionRecord;  ptr.ContextRecord = &ContextRecord;  __try  {   *(int*)0 = 0;  }  __except (Filter(GetExceptionInformation(), &ptr))  {   Doxx(&ptr);  } }

    然后反复查找应用的断点,找字符串找到:
    GenGetProcessInfo.EnumUnloadedModules(0x%x) failed, 0x%08x
    就从它入手了,找到相应的代码引用,然后看上下文,不多久找到了
    NtWin32LiveSystemProvider::EnumUnloadedModules 函数,
    一看实现,是从已经有的内存里copy一个字符串出来而已。
    那还要找到这段内存如何来的,在指定改写处下硬点断点重跑,
    发现这个内存是用ReadProcessMemory读来的,读的地址是7C99D8C0,
    跑到那里一看,果然有unloaded 的 shimengine.dll的信息。
    看了下od的log确认这个dll在exe的入口点之前被卸载。

    接下来就查这个7C99D8C0是哪里来的了,往上翻代码,发现是调了一个函数,
    在那里下断点重跑,找到了:

    030656DE  |. FFD0    CALL EAX   ;  ntdll.RtlGetUnloadEventTrace

    google一把RtlGetUnloadEventTrace,发现msdn2上居然有详细说明。
    到是出乎意料。

    OK,大功告成,总结如下:

    windows xp sp2和2003 sp1以后,ntdll维护了unloaded module的信息。
    由ntdll写入一个叫RtlpUnloadEventTrace的数组,并导出函数
    RtlGetUnloadEventTrace返回该数组。代码实现:

    #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 typedef struct _RTL_UNLOAD_EVENT_TRACE {     PVOID BaseAddress;   // Base address of dll     SIZE_T SizeOfImage;  // Size of image     ULONG Sequence;      // Sequence number for this event     ULONG TimeDateStamp; // Time and date of image     ULONG CheckSum;      // Image checksum     WCHAR ImageName[32]; // Image name } RTL_UNLOAD_EVENT_TRACE, *PRTL_UNLOAD_EVENT_TRACE; RTL_UNLOAD_EVENT_TRACE RtlpUnloadEventTrace[RTL_UNLOAD_EVENT_TRACE_NUMBER]; PRTL_UNLOAD_EVENT_TRACE RtlGetUnloadEventTrace() {     return RtlpUnloadEventTrace; }

    我们只需要调用RtlGetUnloadEventTrace即可拿到指向数组首元素的指针。
    但是这个数组有多少项呢?msdn2上说是有64项,但dbghelp只读了16个,为什么呢?
    如果64个,占用内存大小应当是:
    sizeof(RTL_UNLOAD_EVENT_TRACE) * 64 = 54h*40h = 1500h
    打开ntdll.dll的data segment一看,其实大小并没有这么多,只有540h个字节,
    也就是说,至少在windows xp sp2,这个结构的大小只有10h即16个元素。
    也许在其它版本如vista更大了吧,不管它。

    到这里,要做的,无非就是GetProcAddress拿到RtlGetUnloadEventTrace,
    拿到返回的指针,再读出16个结构出来,然后整理成我需要的报告形式,与
    .dmp一起上报就好了。