-
QQ验证码老是输不对的原因
Posted on 四月 29th, 2008 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搞去分给别人了,那个被分到的家伙就倒霉了,重复输入好多次验证码才会进得去了。
这简直是一套七伤拳啊,杀敌三分,自损七分。虽然确实可以杀敌。
想要摆脱这种老是输入的局面也有招,参考解决方案:
- 重新播放,换IP上,也许马上就可以上了。
- 停一停,过一个小时再来上,由于这个IP闲了下来,会渐渐被排除恶意,也许就上得去了。
-
做了Perl 5.10.0的chm文档
Posted on 四月 27th, 2008 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 No commentsuse 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 No comments一个很原始的需求是:做了一个activex对象,只希望它在某些站点中使用。不希望在其它站点中使用。
或者说,不希望被别的人偷用。或者不希望被广泛使用而造成安全问题。
比如:在www.foo.com中可以使用,在www.bar.com中不能使用。
由于只有ie能创建并访问activeX,因此本文只适用于IE。
要解决这个问题,关键是两点:
- 如何在activex对象中判定当前域名或当前所在网页url。
- 如何在判定不能使用后,禁止用户使用。
对于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,然后,在GetInterfaceSafetyOptions和SetInterfaceSafetyOptions中,根据当前情况返回E_FAIL就行了。
以上内容是是研究微软的某软件的一个activex控件在不同网页里的表现不同而发现的。
-
不支持抵制法货的人,你们真的是理智吗?
Posted on 四月 18th, 2008 No comments张朝阳站出来支持抵制法货了,很好。给白岩松反戈一击了。道理不想说太多,反对抵制的人无非是说,家乐福里的都是中国人,卖的也差不多都是中国货,抵制了就会给自己人带来损失。其实我们抵制法国货不意味着我们不买货了,不去家乐福还可以去别的超市。中国货不在家乐福卖,一样可以在其它超市卖。因此这个道理站不住脚。还有的人是摆出一幅以德服人的面孔,说只要我们自己做好了就行了,以高洁的行动来反衬他们的罪恶,这就更错误了。子曰:以德报怨,何以报德?以直报怨,以德报德。中国没有犯错的时候,外国编一些来评击,外国犯了错了,我们为什么不能想点办法?抵制一下有何不对?
请问:欧洲人抵制我们中国货,烧了我们的鞋子仓库,你知道吗?制造贸易避一句那是欧洲的FQ就可以解决问题了吗?欧盟一直在说我们倾销,千方百计制造贸易壁垒,这不是抵制是什么?人欺我可以,我欺人就不对?就是FQ,就不理智?这种所畏的“理智”其实只是一昧的逃避罢了,想要通过这种与众不同来表达自己的清高,那就大错特错了。
再说,抵制确实发生了效果,法国有些媒体已经报道了中国的抵制,并提出了一些担忧。找找新闻就能找到。试想一下,如果每个中国人都“理智”,都当没事发生,都以德服人,欧盟就不“反倾销”了?法国就不支持ZD了?人家high着呢,只会以为你们好欺负。
还是孔子说得好,以直报怨,以德报德。今天我们的抵制,不过是以彼之道,还施彼身罢了。抵制家乐福,人人有责。从我做起。
-
谈谈有目的的学习
Posted on 四月 16th, 2008 No comments事情的起因是,某人觉得学了C++的语法之后,似乎没什么用,不知道要怎么去用,这样的话,很难继续的学下去。
其实,成年人学习是一定要有目的的,小孩子还好,可以为了学而学。人一长大,学习一旦没有目的,就学不下去了。
对于C++,我是学了一点点基础之后,就直接按钮某杂志的文章写一个在线发送寻呼信息的程序,结果还把我一个同学搞毛了。
对于一般的情况,也是类同此理。自己找一些好玩的应用,遇到不足之处再补相关知识,这样学习起来就十分有劲了。好玩的应用是很多的,比如写一个在桌面上自由活动的小动物,可以把自己的宠物放上去。或者是用javascript写个很小的游戏,或者是用flash做一个故事发给你的朋友,或者是对自己感兴趣的方面搭建一个小网站。不过做这些事的时候,都要对自己提高要求,精益求精,不要因为这个软件或功能别人已经做过了就不去做。最好也不要直接用别人的组件,从底层一点一点慢慢搭。搭完之后,了解就深入了,知识结构就上去了。
比如写一个相册,你可以不用gdiplus库或其它图像库,自己去研究文件的格式,或者,自己去研究一下图像是如何变灰的,当然如果这些你都已经了解了,那也可以再去研究再深一步的效果比如雨点效点。
又如搭网站的时候,先不要使用别人的ajax库,自己先手动做一个ajax模型,这个模型虽不如成熟模型那么方便,但搭的过程会很有意思,也能让自己理解别人为什么要那样设计。
在理解的基础上,再去使用别人的库,就会更得心应手了,出了问题,也不会茫然无措了。
当然,有目的的学习而不是系统的学习也是有缺点的,缺点是这样学了很久之后,可能还有少量很基础的东西不知道。因为毕竟不是系统的学。有时会出点低级错误被人bs。我感觉这样就像张三丰练的九阳真经,听些短言残章,虽有所悟,但是毕竟不系统。不能给小无忌治寒冰掌之毒,但不妨碍他成为武林泰斗。
-
Javascript 终级“解密”
Posted on 四月 15th, 2008 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 No comments222.122.249.5:8080
非常快非常好的代理,如今挂了。不能连了。
google了一把这个IP,一堆的记录啊天啦。
不过现在终于倒了。
也好,搜这个IP能找到很多不错的代理网站。
用代理这个关键字反而找不到这么好的。呵呵,让SEO见鬼去吧。
-
windows 注册表的初始化
Posted on 四月 15th, 2008 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 No commentsdbghelp有个功能,那就是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一起上报就好了。


