[疑难] 请教关于DLL的问题。

hurd 2009-07-09
写一个Hook WSASeed的抓包程序,用工具注入D写的dll到目标进程。注入工具是在pediy上找的,试了别人写的dll和我自己写的vc6 dll都成功注入。但是D写的dll一直提示0x1000d836(dll变下就成其他指令了)指令引用的"0x80bc008"内存,该内存不能为read。

我去掉了D写的dll里的所有模块,全部文件变成这样
module DllMain2;
alias	void* HINSTANCE, LPVOID;
alias uint	ULONG;
alias bool BOOL;
extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved){
	return true;
}


编译出来的dll仍然存在同样错误,但是能被程序通过LoadLibraryA使用。

然后我使用vc写了个dll,启动一个进程来load D写的哪个dll,结果仍然抱同样的错误。
引用
// test3.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"


DWORD WINAPI helloFunc(LPVOID arg){
Sleep(20);
HANDLE handlerDLL   =   LoadLibrary("F:\\ent\\games\\HookPkg\\test2.DLL");
if( handlerDLL ){
OutputDebugStringA("OK");
}else{
OutputDebugStringA("NO.");
}
return 0;
}

DWORD test1(){
HANDLE thread;
int x = 0;
thread = CreateThread(NULL,0, helloFunc,&x,0,NULL);
return 0;
}

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
){
    switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
test1();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
    }
    return TRUE;
}


请教为什么会这样?
tomqyp 2009-07-09
不只是注入DLL,像Dll服务这样正常操作也出这个问题,忘了是什么原因,只记得好像是D中哪一部分的C库引起的,这就意味着就算自已解决了,每次D更新都要再hack一次。

建议换个方法
hurd 2009-07-10
如果知道调试或者hack的方法,也不用每次更新都hack。用一个可用的版本就行了。问题是现在不知道如何hack或者调试这样的运行方式。。。。
tomqyp 2009-07-10
一定要自己要调试的话,先这样

dmd source.d -L/M
这样生成的map文件里会有所有程序运行时每个函数的所处的内存地址(如果生成的是DLL的话,运行时的内存地址可能会重定向,怎么计算Dll中函数运行时的地址一下子说不清楚,可以去google一下),然后把softice,ollydbg之类的调试工具设定为系统默认调试器,出错时选调试,进入调试器界面,然后在堆栈里找到出错时所在的函数地址,再从编译时生成的map文件里根据你找到的出错函数的内存地址(如果是DLL的函数就要再计算一下)就可以找出错函数的函数名了。

我记得这个问题是出在D里面的一个C库里面,没有源码。因为lib本身的结构原因不能修改有直接寻址的代码,所以如果一定要改的话,可以直接改编译出的exe和dll文件,可是这样的话,每编译一次自己就要改一次代价太大。另外我想可不可以这样,搞清楚omf lib文件格式,写个拆解工具把lib文件里的函数和结构都还原成目标文件,这样只要搞清楚出错的函数,只要重写出错函数再编译成目标文件然后打包就可以了,不过这对一个开源工具来说代价还是太大了不划算。

对于你的程序,我以前也写过两个工具也有类似的需要,不过我是用其它办法代替的。
一个是一个函数调用的跟踪打印工具,我用的是debug api,好处是不只可以hook api还可以hook静态链接的函数,也不用为多线程锁担心,而且不需要DLL,缺点是cpu占用比较高。

另一个也是个抓包工具,解决办法是实现一个简单的socks5服务端,然后用第三方工具把要分析程序的连接请求转到自己的socks5服务端上就行了。

还有个更简单的抓包改包办法我还没试过,去reactOS主页下载ws2_32.dll的源码,在连接部份加一段判断,如果是要分析的程序就转到自己的侦听端口,处理后转发出去。编译后替换自己的系统dll.
betty_betty2008 2009-07-10
楼上真强,羡慕ing~
hurd 2009-07-10
感谢tomqyp兄提示,运行在函数 SelectFree@RTLMultiPool中异常。
1000DE20  /$  53            push    ebx                              
1000DE21  |.  8B4424 08     mov     eax, dword ptr ss:[esp+8]
1000DE25  |.  8B10          mov     edx, dword ptr ds:[eax]
1000DE27  |.  56            push    esi
1000DE28  |.  3B51 04       cmp     edx, dword ptr ds:[ecx+4]
1000DE2B  |.  77 13         ja      short te2.1000DE40
1000DE2D  |.  8D5A FF       lea     ebx, dword ptr ds:[edx-1]
1000DE30  |.  C1EB 03       shr     ebx, 3
1000DE33  |.  50            push    eax
1000DE34  |.  8B31          mov     esi, dword ptr ds:[ecx]
1000DE36  |.  8B0C9E        mov     ecx, dword ptr ds:[esi+ebx*4] ; 停在这里
1000DE39  |.  E8 C20B0000   call    te2.1000EA00
1000DE3E  |.  EB 0C         jmp     short te2.1000DE4C
1000DE40  |>  8B0D 448D0110 mov     ecx, dword ptr ds:[10018D44]     ;  te2.1001AB48
1000DE46  |.  50            push    eax
1000DE47  |.  E8 7C1B0000   call    te2.1000F9C8
1000DE4C  |>  5E            pop     esi
1000DE4D  |.  5B            pop     ebx
1000DE4E  \.  C2 0400       ret     4



不知道还有其他异常不。 另外tomqyp兄能简单说下为什么错! 修改dll的话大概要改多少个命令。(比较少的话就用这个方式了。)
tomqyp 2009-07-10
betty_betty2008 写道
楼上真强,羡慕ing~

其实上这就和孔乙己“茴”字的第四种写法一样,当兴趣可能,工作中一般没什么用。

hurd 写道
不知道还有其他异常不。 另外tomqyp兄能简单说下为什么错! 修改dll的话大概要改多少个命令。(比较少的话就用这个方式了。)

这么说应该就是C lib部分的问题了,其实我当初发现是C lib问题就没有源码就没有再分析下去了,当时应该是换了其它的办法。

异常在这里出现,但问题不一定是这里引起的,如果阅读反汇编功底够用,应该可以找出来不过工作量太大不划算,如果你有兴趣的话,我给你理个头,希望对你有所帮助。

这段汇编代码翻译成C++代码大致应该是这样:
xxx  RTLMultiPool::SelectFree(xxx arg1)
{
    //下面的(char)*this+4就是this偏移4位,
    //假如RTLMultiPool第一个成员是int(或者其它占4个字节)类型的话
    //那么(char)*this+4就是指第二个成员
    //当然也有可能是函数,这就要分析RTLMultiPool是怎么定义的
    //另外不同的编译器可能也有差别,可以用DMC编译个简单程序然后反编译看看它的规律
    if(*arg1>(uint)((char)*this+4)
    //或者像下面这样看着可能舒服一些
    //if(*arg > this->member2) 为了容易看懂下面都用这样的伪代码算了
    {
        return this->member1[4]->te2.1000EA00((*arg1-1)*8);
        // 你出错的就在this->member1[4]这里,看来要么是数组越界,要么就是member1还没分配或者已经释放了
    }
    else
    {
        return 10018D44->te2.1000F9C8(arg1);
    }
}


现在你要找出原理至少应该分析调用SelectFree的函数、RTLMultiPool的大致结构和其它涉及到的类和结构、跟踪this->member1至少前4个数组成员的分配、和释放,把这些做完问题估计就找到了。
hurd 2009-07-21
1000DE36  |.  8B0C9E        mov     ecx, dword ptr ds:[esi+ebx*4] ;


改成下面
1000DE36  |.  8B0C9E        mov     ecx, dword ptr ds:[esi+1*4] ;


程序不再出错, 不知道这样改有没有问题?
lifc 2009-07-21
很久没用windows了,07年在linux下把phobos、tango、还有gtkd等几个项目编译成.so(相当于windows下的dll),遇到过一些类似问题,归结起来大概有三方面原因:
1、没有用-fPIC生成位置无关代码,不过windows下可能默认就是位置无关的。
2、垃圾收集相关部分初始化不完整。
3、符号解析失败,通常是连接器只连接了tango用到的符号,用gcc连接生成.so加入-rdynamic可以解决此类问题。
你遇到的很可能是第一、二点,随便说说仅供参考:)
tomqyp 2011-05-22
刚刚我也又遇到这个问题了,在网上搜了一下没想到搜回到这个贴子了。


这个错误是是在虽然是在removethreadtableentry函数中调用free时出现,但是是在在rt_init();后发生的,即使关闭GC或者不调用rt_init();也会出现这个问题,估计是跟GC无关,想必直接用dmc编译一个dll也会出现这个问题,不过我没试。

苦于snn.lib没有公布源码也很难找出原因,hurd的办法我没敢用,必竟free和SelectFree太多地方调用了,不知道会不会引起内存泄露,或者一些其它未知的问题。

我的办法是去掉了removethreadtableentry中的这次free,虽然还是有一块内存没法释放,但是好在removethreadtableentry在DLL中应该只会调用一次,这块内存就把它当成一块没用的静态变量吧。

具体办法是,用二进制编辑器打开snn.lib
找到
83 C4 04 83 7B 1C 00 74 09 FF 73 1C FF 15 00 00 00 00 53 e8
改成
83 C4 04 83 7B 1C 00 74 09 FF 73 1C FF 15 00 00 00 00 eb 07


不知道GDC有没有这个问题,不过就算有好在GCC下的C库都有源码解决应该也不难。

PS: snn.lib估计十几年都没更新了吧
Global site tag (gtag.js) - Google Analytics