顯示具有 wye - Programmer 標籤的文章。 顯示所有文章
顯示具有 wye - Programmer 標籤的文章。 顯示所有文章

2014年9月8日 星期一

使用phantomjs+casperjs遇到的坑

这两周帮同学搞一个网站上的在线书籍接触到了phantomjs和casperjs。casperjs大法确实不错啊,可以编排模拟网页访问操作步骤,并截取页面中的指定区域或元素的内容。不过这东西坑和bug也不少,现在把一些还有印象的东西记录一下。

1.循环
Casperjs的执行方式类似于感觉就像在写汇报,而且不支持向后跳转到之前的step,不过也不是完全没有办法循环了,网上的办法是通过递归调用来实现的,就像这样(别忘了在外面初始化一下i):

casper.someloop = function() {
    this.then(function () {
        if(this.i == 10){
this.echo(‘loop end’);
this.exit();
        }
this.i++
});
this.run(this.someloop);
}

同时如果处理的是一个数组内的所有元素的话,还可以用each或者eachThen,官网的例子如下:
var links = [
    'http://google.com/',
    'http://yahoo.com/',
    'http://bing.com/'
];

casper.start().each(links, function(self, link) {
    self.thenOpen(link, function() {
        this.echo(this.getTitle());
    });
});

casper.run();

不过这个函数有个限制,第一个参数传入的数组不可以是在之前某个step中动态获取到的数组,它是不会理会动态获取到的结果的。

2.等待
首先,所有等待函数放在各类’then’中执行会出问题,根本就不等直接就过去了,也不知道这算不算是casperjs的bug。然后是waitFor系列函数,waitFor系列函数是自带超时的,并不是你不传入超时时间它就一直等下去了。还有start和open系列函数打开网页好像没法在网页所有资源加载完毕之前就走到后面的等待判断中。

3.Selector
并不是使用document.querySelector能对象就可以使用casperjs的带selector系列函数,具体什么样的元素可以什么样的元素不可以我还么有找到规律,好像是动态创建的元素会有这个问题。

4.PDF
使用casperjs的capture函数截取pdf的时候会出很多问题,同样的问题在截取图片或者使用phantomjs原生的capture函数时并不会发生,同时会引发下面一个问题

5.casper.options.page
想要解决上面的那个问题又不想截取成图片,翻翻文档貌似就这个东西可用了,不过这应该是一个未完成的功能,设置了这个page对象之后,我的then就没触发过,一直在等待。

6.页面大小
打开一个网页,然后使用evaluate函数执行js修改网页大小,这是再使用capture函数截取网页内容会发现之前调整大小一点效果都没有,后来俺从github上搞了份没有tag的最新版问题才得以改善。

7.evaluate
这其实也不算是坑,也就算是个注意事项吧,使用evaluate执行脚本的时候如果想要引用function外面的变量需要传参进去,否则是不能用的。

8.其他
其实casperjs这东西坑肯定不止这点,有些我忘记了有些我还没遇到,被恶心的时候可以尝试从github上搞个新版本下来。

2014年7月22日 星期二

备忘:关于VB程序报错Out of memory

msvbvm60.dll写的很屎,有一个报错函数写死了报Out of memory,今天就遇到的情况明明是hook的伪SetFilePointer写错了,在某些情况成功操作但是函数返回失败,结果msvbvm60.dll搞了个Out of memory,真够坑爹的。
所以,VB6程序报错Out of memory等于报错Error,除了说明发生了错误之外什么都不能说明 ,memory什么的就当它是个屁好了。

不用任何API获取本可执行文件的ImageBase(dll hModule)

一句话:
extern "C" IMAGE_DOS_HEADER __ImageBase;
这两天读msvcrt源码所得,以后dll获取自身的hmodule再也不用VitualQuery了

2014年7月20日 星期日

VS2013写的dll被内存加载失败原因一例

  在公司写的某dll终于完成了,和公司的壳一同使用时,公司的一种壳可以稳定的从内存加载使用我写的dll,而另一种时好时坏(大多数情况都无 法正常加载),在我来这里工作之前,这两个壳在内存加载他们使用低版本VS编译出的dll时都可以正常工作,我因为使用了一些C++11语法特性,实在不 想做降版这蛋疼事,调试分析了这其中的原因。
  经过一番调试,终于把前因后果捋顺了:
· 壳->DLLEntryPoint:走到_DllMainCRTStartup
· _DllMainCRTStartup->_CRT_INIT:初始化CRT环境
· _CRT_INIT->_cinit:这还真不知道是初始化啥
· 其中_cinit中有段代码是这样写的:
if (_FPinit != NULL &&
     _IsNonwritableInCurrentImage((PBYTE)&_FPinit))
{
      //这里代码具体记不清楚了,是调用了一个函数初始化浮点数运算相关环境
}

代码意思比较好懂,如果_FPinit不为零同时这个变量在当前映像中是不可写入的就初始化浮点数运算环境。而问题就出在_IsNonwritableInCurrentImage的判断上了:
BOOL __cdecl _IsNonwritableInCurrentImage(char *pTarget)
{
  _IMAGE_SECTION_HEADER *pTargetSectoinHeader; 
  BOOL bResult = FALSE;
  if ( _ValidateImageBase((char *)0x10000000)
&& (pTargetSectoinHeader = 
  _FindPESection((char *)0x10000000, (unsigned int)(pTarget - 0x10000000))) != 0 )
  {
    bResult = ~(pTargetSectoinHeader->Characteristics >> 31) & 1;
  }
  return bResult;
}


这个函数先调用_ValidateImageBase判断了当前映像基址是否为合法的PE文件头,然后调用_FindPESection找出变量所在的区段的区段头,检查区段头中记录的该区段是否具有写入权限,返回结果。代码中写死的0x10000000地址都会在dll加载时系统通过重定位表将这些地址改写为映像基址。
但是在_ValidateImageBase判断头两个字节是否为MZ时程序就非法内存访问然后崩溃了。
经过了与维护壳的同事沟通确认,产生问题的壳在最初分析完我的dll之后就把PE头抹掉了,使用了一个自定义的结构体代替PE头记录了部分PE头内的信息供内存加载dll初始化时使用,在为我的dll代码申请内存空间时根本就没有留出PE头的空,当然更没有把PE头还原回去。
内存空间就成了这样:
未分配的内存,本应为PE头
为DLL申请的内存
↑ImageBase
于是经过重定位表重定位后传递给_ValidateImageHeader的映像基址就指向了一处非法地址,最终产生了非法内存访问。在极其偶然的情况下该地址可能指向了另一块内存区域的一部分,指向的内容正好不是MZ开头,而这个dll又凑巧不需要初始化浮点数运算环境,才会出现偶尔可以正常启动的情况。
至此这个问题的前因后果已经搞明白,具体该如何解决还等和领导沟通后再定吧。
--8:28 PM 7/21/2014
解决方案:
方法一(最省事相对稳定):由于_IsNonwritableInCurrentImage总共就判断两个变量,所以可以根据自身程序情况写个代替函数写死了,修改工程设置人为规定EntryPoint,调用_CRT_INIT初始化之前修改内存权限把自己写的函数memcpy过去。
方法二(相对省事可能不稳定):修改工程设置人为规定EntryPoint,然后参照IDA F5和VS安装目录下的crt源代码,写自己的_DllMainCRTStartup、_CRT_INIT、_cinit。不稳定因素有二:1.由于msvcrt.lib有两个obj都有某全局变量,所以实现_CRT_INIT时对着两个全局变量是没法初始化了2.可能正是由于不稳定因素1,调用初始化环境的函数会失败。
方法三(最稳定最费时):搞个自己的msvcrt.lib

2014年7月2日 星期三

Hook CloseHandle时遇到程序退出时卡死的问题原因及解决方案

  实际上这个问题我很久之前就遇到过了,在Hook CloseHandle函数的情况下,点击关闭按钮后程序要等一段时间才能够关闭,当初只当是虚拟机性能差造成的延迟,直到前几天我在新公司写Hook代码在实机上测试才发现问题并不是我之前认为的那样。

问题现象:
  • 在Hook CloseHandle函数的情况下,点击关闭按钮后要等一段时间进程才能够退出。
问题原因:
  • 在程序退出的过程中,全局对象的生存期已经结束,系统在进行进一步收尾工作时调用了CloseHanle,这时在伪函数内访问了已经销毁的全局对象,导致异常产生。
解决方案:
  • 动态申请在伪函数内需要访问到的全局对象,将对象保存在堆内。
  此时对象保存在堆内而非栈内,在程序退出全局变量生存期结束时不会调用析构函数。同时,虽然用于保存该对象地址的指针/引用虽然也超过生存期,但是由于没有被复写,也不存在析构函数,所以可以访问。

分析过程:
  首先尝试在进程等待退出时间段内使用Process Explorer查看该进程各个线程的调用堆栈情况,初步判断原因。
  现象:一用Process Explorer查看该进程线程情况,一切换到该进程属性的Threads标签页程序立刻退出。
  现象分析:Process Explorer查询线程堆栈调用了dbghelp.dll内相关函数,可能程序遇到的情况如果不挂调试器就不继续执行了。
  尝试:先用Process Explorer查看该进程属性的Threads项,关闭程序,让异常现象产生
  现象:从调用堆栈可以看出,现场内存在未处理的异常。
  猜测:【猜测内容同问题原因】(运气不错,一下猜中了)
  编写粗糙的测试代码:
  BOOL bDestoryed = FALSE;
  Class fdsa{
  public:
    fdsa(){};
    ~fdsa(){bDestoryed = TRUE};
  }
  fdsa xxx;
    同时在CloseHandle的伪函数内判断bDestoryed是否被赋值为TRUE。

  最终确定猜测成立。

2013年11月15日 星期五

使用Greasemonkey脚本彻底摆脱BT工厂下载弹窗

今天有空,所以更新一下多年没有动过的博客。
事情是这样的……
正是因为今天有空,所以我为了陶冶情操,文化交流网站‘BT工厂’看了看最近更新的新的文化交流内容……
然后呢,我一如既往的点开了很多很多的文化交流种子下载页……
但是把这些下载页的种子全都下载下来是一件很痛苦的事情!
来看一下代码:
<form action="../down.php" method="post">
  <input type="hidden" value="torrent" id="type" name="type" />
  <input type="hidden" value="#######" id="id" name="id" />
  <input type="hidden" value="#######" id="name" name="name" />
  <input type="submit" value="  下 载 /  down  " onclick="openpage()" class="round_box" id="down_btn"  />
</form>
可以看到,这个下载按钮被点击时不但会提交表单开始下载文件,同时还会触发openpage函数。
这个函数定义如下: 
function openpage() {
var rand=Math.floor(Math.random()*2+1);
switch(rand)
{
 case 1:
 window.open('http://??.?????.biz/','','scrollbars=1,resizable=1').blur();
 break;
 case 2:
 window.open('http://??.?????.info/','','scrollbars=1,resizable=1').blur();
 break;
 default:
 break;
 }
window.focus(); 
}
 
这才是最蛋疼的!每下载一个种子文件都要经过如下操作:
1点击一下下载按钮
2关闭弹出来的网站主页
3下载文件
4关闭网页

如果只是一个种子还好,但是我每次可都是要下载大量种子的啊!折腾不起啊啊啊啊!
然后我就去下载了greasemonkey插件然后开始折腾用户js脚本……
话说firefox这一点就不如换芯之前的opera方便,opera原生支持User CSS和User JS! 

解决弹窗的话很容易,只需要一句:
document.getElementById("down_btn").onclick = null;
 
好吧,即便如此还是太麻烦了…… 
毕竟有那么多种子等着我去下载学习呢,要是不用点击下载按钮就好了……
这也好说,这么一句就够了!
document.getElementsByTagName("form")[0].submit();
嗯…… 
这下好多了,尤其是我还在FireFox里设置了种子文件默认是保存操作,省得我一下一下点了。

不过好像还是有点不爽啊,既然都自动下载了,
那么下载了之后我又何必非要很傻逼的去手动关掉那些下载页!我想要它自动关掉!
于是就有了如下最终版代码:
// ==UserScript==
// @name        AntiBTGCPop&AutoDownload
// @namespace   wye-ANGER.blogspot.com
// @description AntiBTGCPop & AutoDownload
// @include     http://www3.*domn.com/*/file.php/*.html
// @include     http://www3.*down.info/*/file.php/*.html
// @include     http://www3.*down.com/*/file.php/*.html
// @version     1
// @grant       none
// ==/UserScript==

document.getElementsByTagName("form")[0].submit();
document.getElementsByTagName('html')[0].innerHTML
    = document.getElementsByTagName('form')[0].outerHTML;
var http_request = new XMLHttpRequest();
document.getElementById("down_btn").onclick = null;

function onResponse()
{
    if(http_request.readyState!=4) return;
    if(http_request.status!=200)
    {
        alert("Problem retrieving XML data");
        return;
    }
    //assume browser cached torrent
    setTimeout("window.close()",1000);
}

var nameval = document.getElementById("name").value;
var idval = document.getElementById("id").value;
var arg="type=torrent" +"&name="+nameval+"&id="+idval;

http_request.onreadystatechange=onResponse;
http_request.open('POST',"../down.php", true);
http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http_request.send(arg);

这个脚本需要配合两项FireFox来使用
1.about:config中的dom.allow_scripts_to_close_windows需要设置为true以允许js关闭窗口
2.火狐应设置自动保存种子文件而不询问(没试过,或许不这么设置也可以用)
使用这个脚本之后,只需要按住Ctrl点击下载页连接,下载页在后台打开后会自动开始下载,然后自动关闭下载页!
这下终于可以痛痛快快的进行文化交流了~
 
找脚本的同学看到这里就够了,如果对代码还有点兴趣可以继续看。 
 
稍微解释一下上面的脚本吧。
一开始先对form进行submit()先通过能触发下载的正规渠道发出请求。
然后问题又来了,从浏览器发出请求到服务器完成响应的间隔时间是不可预测的!
如果窗口关早了,服务器还没来得及响应请求,那文件都没来得及开始下载。 
对于BT工厂这样的文化交流网站更是如此!慢起来的时候那真是^$#&*@(&#$*@)&$*#@!
 
那么就需要简单的判断一下服务器是否已经响应了下载请求。
对此,我的解决方案是先调用submit()走正规途径提交表单。
然后通过XMLHttpRequest提交一个同样的表单。 
这时假设这两次提交表单对应的HTTP请求被浏览器缓存优化,
认定多出来的一次请求不会造成太多的网络负担(本来一个种子文件也不会造成多少负担)。 
假定等后提交的请求服务器完成全部响应时,前一个通过submit()发送的请求已经至少完成了初步的响应。
那么此时关闭窗口应该至少浏览器已经开始下载了。
 
至此,大功告成~ 

2011年2月12日 星期六

嘿嘿,原来可以

既然能发帖了,那我可能要考虑是否恢复这blog了……
之前也试图IPV6翻墙发帖,一点New Post就被stick,还以为是hosts文件改动不够全呢。
看来是我这网络访问ipv6太慢的原因……

那么,我最近在搞啥?
看这里:
http://hi.baidu.com/resfleam/home
http://0rz.co/blog/447574.htm
http://tora.to/blog/447574.htm 

2009年8月11日 星期二

枚举质数 Enum prime numbers in C++

http://projecteuler.net/index.php?section=problems&id=7
以下代码将枚举10001个质数,耗时16ms
Example for enum 10001 prime numbers in 16ms.

int res[10001],Pos1=2;
res[0]=2;
res[1]=3;
res[2]=5;
for (int Enum=5;Pos1<10001;Enum++)
{
        BOOL wanted=1;
        for (int Pos2=0;res[Pos2]<=sqrt(Enum);Pos2++)
       {
                 if (!(Enum%res[Pos2]))
                {
                          wanted=0;
                          break;
                 }
        }
        if (wanted)
        {
                 res[Pos1]=Enum;
                 Pos1++;
        }
}
printf("%i",res[10000]);

2009年7月29日 星期三

Note:Object type number of process under vista & object type numbers

 ' Windows NT 4          Window 2000            Window XP
 ' -----------------     -------------------    -------------------
 '  1 - Type              1 - Type               1 - (Type)
 '  2 - Directory         2 - Directory          2 - Directory
 '  3 - SymbolicLink      3 - SymbolicLink       3 - SymbolicLink
 '  4 - Token             4 - Token              4 - Token
 '  5 - Process           5 - Process            5 - Process
 '  6 - Thread            6 - Thread             6 - Thread
 '  7 - Event             7 - Job                7 - Job
 '  8 - EventPair         8 - Event              8 - ((DebugObject))
 '  9 - Mutant            9 - EventPair          9 - Event
 ' 10 - Semaphore        10 - Mutant            10 - (EventPair)
 ' 11 - Timer            11 - Callback          11 - Mutant
 ' 12 - Profile          12 - Semaphore         12 - (Callback)
 ' 13 - WindowStation    13 - Timer             13 - Semaphore
 ' 14 - Desktop          14 - Profile           14 - Timer
 ' 15 - Section          15 - WindowsStation    15 - (Profile)
 ' 16 - Key              16 - Desktop           16 - KeyedEvent
 ' 17 - Port             17 - Section           17 - WindowStation
 ' 18 - Adapter          18 - Key               18 - Desktop
 ' 19 - Controller       19 - Port              19 - Section
 ' 20 - Device           20 - WaitablePort      20 - Key
 ' 21 - Driver           21 - Adapter           21 - Port
 ' 22 - IoCompletion     22 - Controller        22 - WaitablePort
 ' 23 - File             23 - Device            23 - (Adapter)
 '                       24 - Driver            24 - (Controller)
 '                       25 - IoCompletion      25 - (Device)
 '                       26 - File              26 - (Driver)
 '                       27 - WmiGuid           27 - IoCompletion
 '                                              28 - File
 '                                              29 - WmiGuid


Object type number of process under vista is 6

2009年6月25日 星期四

Duplicate User Token

-------December 13,2009

由于blog长期没有维护,之前那篇文章广告积累过多
再加上blogger的删除评论功能又很纠结。

故决定删除原文,重新发布此文

此次发布,未对文章内容进行修改
现在看来,之前这篇文章真的是没有什么效用
而且还有很多疏漏与不当之处
许多地方都很繁琐。

不过,作为我成长过程的一个记录,还是保留下来吧。
---------------------------

新本入手,Vista体验中...
体验过程中发现过去一直在使用的一段代码已经不奏效了。
既然如此,那么公开出来也无妨。
看来Agent Starter就要迎来又一次久违的大改了。
这回比较懒,我就不修改、注释了,也没有处理错误,有疑问,可留言。

功能:在当前进程为SYSTEM权限时,绕过密码登陆,获取当前已登录用户的Token。此Token可用于CreateProcessAsUser。

返回值:成功则是HANDLE,失败返回0.

Story:

在当前进程为SYSTEM权限时,由于CreateProcess等函数创建进程时父进程的权限会被子进程所继承,并且子进程也会登录为系统用户,容易造成子进程在获取用户设置时出错。目前已知CreateProcessAsUser和CreateProcessWithLogonW可以创建不同于父进程用户的子进程。但是由于CreateProcessAsUser所用的Token需要通过LogonUser 获得,而CreateProcessWithLogonW与LogonUser均需要用户名与密码才可进行登录,这使得获取Token失去了随意性。

这时,曾经在网上盛传的DuplicateHandle获取进程HANDLE法便可以用来解决问题。已知Token的ObjectNumber为4。所以我们便可以Duplicate其他进程里面的Token用于CreateProcessAsUser,从而免除登陆用户的麻烦。但是我们的Token要从哪里来呢?想想创建taskmgr.exe的是winlogon.exe,而winlogon.exe显示为SYSTEM用户,创建后的taskmgr则为当前用户。想必那里会隐藏着用户Token。打开Process Explorer看看,果真如此。至此,事情就变得简单了。(另外,winlogon里存在多个用户Token,XP SP3里面只有一个可用于我们的CreateProcessAsUser。Vista下没有可以直接用的。打算研究一下DuplicateTokenEx或许能有帮助)

代码:


HANDLE YGetUserToken()
{
      HANDLE ph,h_dup;
      ULONG bytesIO,i,NumOfHandle,WinLogon_id;
      PVOID buf;
      PSYSTEM_HANDLE_INFORMATION h_info;

      CHAR InfoBuffer[1000],szAccountName[200], szDomainName[200];

      DWORD dwInfoBufferSize,dwAccountSize = 200, dwDomainSize = 200;

      bytesIO=0x40000;
      buf = 0;

      WinLogon_id = NametoPid(L"winlogon.exe");
      ph = OpenProcess(PROCESS_DUP_HANDLE,0,WinLogon_id); //YDupHandle(WinLogon_id);

      NtAllocateVirtualMemory((HANDLE)-1, &buf, 0, &bytesIO, MEM_COMMIT, PAGE_READWRITE);

      NtQuerySystemInformation(SystemHandleInformation, buf, 0x40000, &bytesIO);
      NumOfHandle = *(PULONG)buf;
      h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);
      int ai=0;

      for (i= 0 ; i<NumOfHandle; i++, h_info++)
      {
            if (h_info->ProcessId==WinLogon_id&&h_info->ObjectTypeNumber == 4)
            {
                  NtDuplicateObject(ph, (PHANDLE)h_info->Handle, (HANDLE)-1, &h_dup,TOKEN_ALL_ACCESS,0,0);
                  PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
                  SID_NAME_USE snu;

                  GetTokenInformation(h_dup,TokenUser,InfoBuffer,1000, &dwInfoBufferSize);

                  LookupAccountSid(NULL, pTokenUser->User.Sid, szAccountName,&dwAccountSize,szDomainName, &dwDomainSize, &snu);

                  if(strcmp(szAccountName,"NT ATHORITY\\\)) //Vista:SYSTEM
                  {
                        printf(szAccountName);
                        if(CreateProcessAsUserW(h_dup,NULL,L"winlogon.exe",NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&startupInfo,&ProcessInfo))
                        {
                              TerminateProcess(ProcessInfo.hProcess,0);
                              NtClose(ProcessInfo.hProcess);
                              NtClose(ProcessInfo.hThread);
                              break;
                        }
                  }
                  NtClose(ProcessInfo.hProcess);
                  NtClose(ProcessInfo.hThread);
                  NtClose(h_dup);

                  h_dup=0;
            }
      }
      CreateProcess()
      NtClose(ph);
      bytesIO = 0;
      NtFreeVirtualMemory((HANDLE)-1, &buf, &bytesIO, MEM_RELEASE);
      return h_dup;
}