鲁迅的幽默故事:全球首创? 再次突破VB极限!VB真正稳定多线程(不用tlb,tls,ax,pcode)

来源:百度文库 编辑:九乡新闻网 时间:2024/05/06 10:13:22
本帖最后由 VBProFan 于 2010-4-29 16:21 编辑

全球首创? 全球再次突破VB极限!

只需在线程中加一行代码实现 VB 真正稳定多线程

不需要...
1.不需要写tlb库(感觉麻烦了,使用tlb主要是防止vb设置err.lastDllerr),
2.不需要处理tls(很多都是暴力copy,不知道有没有后遗症?)
3.不需要编译成activx exe(这东东会在注册表留下垃圾项,从VB的引用中会看到),
4.不需要编译成p-code(P代码的坏处众所周知了)
5.不需要干掉 setSysXXXerr(干掉后 err.lastDllErr肯定无效啦,不爽)
6.不需要远程创建线程...(汗,谁发明的,好厉害,转了好大一个圈)
...(还有没有?)

俗话说得好,有钱能使鬼推磨,果然不假...
此次的灵感来源于这1000块大洋...

http://www.vbgood.com/viewthread.php?tid=92649

(上面的代码是使用P CODE实现的VB线程安全)
等你学会了全球首创大法后,就能把他改为本机代码的线程安全.原代码不需要用TLB再次封装修改.
  1. '无文档的VB API
  2. Public Declare Function CreateIExprSrvObj Lib "msvbvm60.dll" (ByVal p1_0 As Long, ByVal p2_4 As Long, ByVal p3_0 As Long) As Long
  3. '调用方法:在线程函数的第一行加上
  4. CreateIExprSrvObj 0, 4, 0
复制代码下面的多线程来源于网上因为懒得再写,他是用P-CODE实现的VB线程安全)
http://www.freevbcode.com/ShowCode.Asp?ID=4029

接下来.用上面"全球首创"的方法改进一下...
  1. Public Sub test_function()
  2. CreateIExprSrvObj 0, 4, 0'加到线程函数的第一句

  3. Dim i As Long
  4. Dim ret As Long
  5. ...

  6. Public Sub test_function2()
  7. CreateIExprSrvObj 0, 4, 0'此例中共有2个线程,所以加2句,这是第2句
  8. msgbox "ok?" '测试 VB的提示框...(效果自己下载代码看)
  9. ''''
复制代码此法使用后VB的字符串操作都可以在线程中进行...
当然了,仍有不足之处,暂时不说(其实在别的贴子说过了),留给大家去发现...

未完待续...(根据反应的良好后再加以重点说明.反应不好的就不说明了 )

PS:有钱的记得加钱,没钱的记得加分啊~~~ 这样才会继续公开VB内幕...
PPS:如果是mei nv也可以向老汉使mei ren计,偶可是会中计滴...

-----------------------下面是揭密----------------------------
只需在线程中加一行代码实现 VB 真正稳定多线程揭密

1.为什么要声明成tlb库的原因.
众所周知,VB的函数包装了API,举例:
当调用GetProcAddr时,VB是用的DllCallFunction(好像是这个名吧?记不太清了)先检查有没有加载,有的话直接JMP过去,没有的话又要执行几个API进行加载. 同样的,当调用GetLastWin32Err(估计这是个名吧?记不太清)同样的,又是经过了几个API.这时的错误号己经不是最初的那个API的错误了.(可能己被覆盖) 那么API出错怎么知道呢,调用源码中声明的API的时候,VB就自作聪明的在后面加上一句SetXXXErr(全名记不太清).这样一来.当在多线程或回调中调用API时.变成了下面这样
DllCall MessageBox
SetXXXErr err.LastDllError (这个err是个对象,保存winapi.GetLastError的值,免得被VB函数中调用的API覆盖了错误信息)
在一般情况下是正常的,在线程中由于这个err没有初始化,所以就会保存错误号到未初始化的内存中.就出现线程错误了.
用TLB时VB编译后这个函数放到了PE头部的导入表中,调用时就不需要LoadLib,GetProcAddr,直接jmp 到导入表的地址即可(由PE的加载器填入)
这样就不会有VB中多余的API操作影响winapi.GetLastError的值了.所以VB又自作主张的去掉了SetXXXerr...这样线程中就正常了,当然(还是不能调用其它的VB函数(特别是和对象有关的)
这样一来,每个需要用的函数要用TLB声明一下,相当的麻烦.结果本来只需要写一行声明在源码中,变成了写到TLB的源文件I/ODL中,再用midl编译,还要再引用,最后才能使用...(这里己经讲到关键了,就不再废话了,不然pjz说我充字数骗稿费就不好了-_-!)
总之用TLB写VB函数不是给VB的程序员用的,是给VC+SDK这类程序员用...
例子:顶楼的声明去掉,改为TLB即成.

2.不需要处理tls(很多都是暴力copy,不知道有没有后遗症?)

不知道是谁发明的...好像是CSDN的超级绿豆?这种我看着晕,所以没测试...
例子:http://www.vbgood.com/viewthread.php?tid=88445
(VBProFan 注:iceboy 的 copy tls

3.不需要编译成activx exe(这东东会在注册表留下垃圾项,从VB的引用中会看到),

这个我最早在国外发现...
由于和揭密无关,就不说了,简单说下,如果想取消注册表的项,可以 shell "filename.exe /unregserver"
例子: http://www.vbgood.com/viewthread.php?tid=65375

4.不需要编译成p-code(P代码的坏处众所周知了)

和揭密无关,为节省版面略之...(猜想由于PCODE在虚拟机中运行,所以VB的对象都是OK的,不会有什么大问题)
例子: 顶楼的那个国外的链接

5.不需要干掉 setSysXXXerr

头痛医头,脚头医脚的办法.
nop掉后也只能用VB内声明的API,还是无法用VB字符串之类的函数

6.不需要远程创建线程...

和揭密无关,略过
例子自己找吧: 好像是chenhui530的RtlCreateUserThread
最最关键来源于这里:
http://www.vbgood.com/viewthread.php?tid=88445
这段文字...
需要说明的是在线程函数里面,只有一次调用API的机会~
因为VB编译器会在每个API后面添加__vbaSetSystemError,
如果未经处理__vbaSetSystemError是肯定会出错的...(后面还有一些,和主题无关略之)

也就是说我们只有一次机会... -_-!
于是开始老汉猜想(不是哥德巴赫猜想)
1.那么最好的办法是什么?
2.为什么在VB的IDE中可以运行?
原因只有一点:就是VB中己经初始化了他需要的对象.
那么有没有初始化对象的这个未公开的函数呢?
于是开始追踪:
(过程可到看雪参观: http://bbs.pediy.com/showthread.php?threadid=36400)
仅贴关键思路点:
  1. OD自动反汇编了这个有问题的KEYGEN。我们逐步跟踪,到下面的代码时,出现异常,问题就出在__vbaStrCat中。
  2. 004012BC   .  E8 2B020000   CALL
  3.     我们跟入__vbaStrCat:
  4. ...
  5. 660E5F47    FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; DS:[6610EE18]=00000000,问题出在这里
  6. ...
  7. 跟到660E5F47时出现异常,我们看到PTR DS:[6610EE18]为0,我们拿原来的CRACKME或者直接拿msvbvm60.dll反汇编,跟入__vbaStrCat:
  8. 660E5F47    FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; OLEAUT32.VarBstrCat
  9.    可以看出问题所在:PTR DS:[6610EE18]在CRACKEME中有被初始化成VarBstrCat而在KEYGEN中没有。
复制代码也就是说,我们需要找一个初始化的函数...继续追踪...
  1. text:66004D81                                         ; CreateIExprSrvObj+3E p
  2.     CreateIExprSrvObj觉得可疑,跟进看看:
  3. .text:660EA734                 public CreateIExprSrvObj
  4. .text:660EA734 CreateIExprSrvObj proc near
  5. ...
  6. .text:660EA772                 call    sub_66004D81  <---- 就是这个CALL
  7.     我们需要这个CALL,deroko对CALL进行了修改并使之运行正常:
  8.     push    0
  9.     push    4
  10.     push    0
  11.     call    CreateIExprSrvObj(记得改call为cinvoke)
  12. ...
复制代码到此,我们己经找到了未公开的初始化函数.那就是CreateIExprSrvObj,根据上面的PUSH共有三个参数声明成VB的API.
  1. '无文档的VB API
  2. Public Declare Function CreateIExprSrvObj Lib "msvbvm60.dll" (ByVal p1_0 As Long, ByVal p2_4 As Long, ByVal p3_0 As Long) As Long
  3. '调用方法:在线程函数的第一行加上
  4. CreateIExprSrvObj 0, 4, 0
复制代码刚才说了,我们只有一次机会可以调用API,那就是调用CreateIExprSrvObj 0,4,0 完成初始化过程.
此时线程中的第二条API应该就是__vbaSetSystemError,也就是非TLB多线程的瓶颈,
这个时候己经没关系了,因为上面己经完成了初始化.至此揭密己经告一段落.为什么参数是0,4,0?
这个就不太清楚了,因为是无文档的东东...留给大家继续发现...

------------------------揭密完成-----------------------