七
Two useful IDA Python scripts
Written by KiSSinGGer on 七月 28th, 2010
Useful when you’re confuse about reversing Windows executables using COM.
ClassAndInterfaceToNames.zip
VtablesStructuresFromPSDK2003R2.zip
欢迎你丫来到 KiSSinGGer的私处
在这个充满G点的国度里,我这儿,没有敏感的东西.
Written by KiSSinGGer on 七月 28th, 2010
Useful when you’re confuse about reversing Windows executables using COM.
ClassAndInterfaceToNames.zip
VtablesStructuresFromPSDK2003R2.zip
Written by KiSSinGGer on 四月 12th, 2010
http://www.harmonysecurity.com/
视察一段时间,准备加入链接.
Written by KiSSinGGer on 一月 27th, 2010
转自 Xiaoxiao’s Blog
http://home.xxsyzx.com/2197.html
之前以为linux和windows下的socket都是符合posix标准的,在一个环境下能够正常使用在另外一个环境也没问题。但没想到两者还是有那么多不同。
1.最先发现的是在windows上需要调用那个WSAStartup进行初始化才能使用socket。
2.linux下关闭socket用close,而windows上用closesocket。
3.后来发现shutdown的参数名称不同,windows上用SD_BOTH,linux上是SHUT_RDWR。
4.在linux下如果不正常关闭socket会触发SIGPIPE异常,所以我使用signal( SIGPIPE, SIG_IGN );忽略之。
5.linux下可以使用read,write函数操作socket,也可以使用send,recv。windows上只能使用send,recv?(不考虑udp的sendfrom和recvfrom)
今天又发现一个很大的不同点了,造成了homeserver在短时间内生成了一个20GB的日志文件,用完了vps的硬盘空间。如果一个正在使用中的socket使用close来关闭之而不调用shutdown,则该socket还是处于连接中,只是在进程的文件描述符中移除了(right?)所以导致了使用那个socket的线程一直没有收到关闭信号,阻塞在socket的recv上无法被关闭。而在windows上调用closesocket就彻底地断开了连接了。
下面是网上提供的信息:
close—–关闭本进程的socket id,但链接还是开着的,用这个socket id的其它进程还能用这个链接,能读或写这个socket id
shutdown–则破坏了socket 链接,读的时候可能侦探到EOF结束符,写的时候可能会收到一个SIGPIPE信号,这个信号可能直到socket buffer被填充了才收到,shutdown还有一个关闭方式的参数,0 不能再读,1不能再写,2 读写都不能。
所以,以后关闭socket还是同时使用shutdown和close好。
Written by KiSSinGGer on 一月 5th, 2010
一般来说,Posix的线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。
![]()
不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。
最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。
在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源–从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。API定义如下:
void pthread_cleanup_push(void (*routine) (void *), void *arg) void pthread_cleanup_pop(int execute)
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:
#define pthread_cleanup_push(routine,arg) \
{ struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); }
可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut); pthread_mutex_lock(&mut); /* do some work */ pthread_mutex_unlock(&mut); pthread_cleanup_pop(0);
必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的mutex变量,造成错误。因此,在使用清理函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的Linux实现中还提供了一对不保证可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()扩展函数,功能与以下代码段相当:
{ int oldtype;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
pthread_cleanup_push(routine, arg);
...
pthread_cleanup_pop(execute);
pthread_setcanceltype(oldtype, NULL);
}
![]()
![]()
一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。
void pthread_exit(void *retval) int pthread_join(pthread_t th, void **thread_return) int pthread_detach(pthread_t th)
pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。
如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。
一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。
![]()
![]()
理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。
在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。
其次,在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment fault。
Written by KiSSinGGer on 三月 25th, 2009
http://www.microsoft.com/security/msec/default.mspx
基于WinDBG的一个CrashDump分析工具,作为Plugin使用.相当于微软自产Fuzzer,开源.
Written by KiSSinGGer on 三月 13th, 2009
一个程序在ubuntu上跑得好好的,在苹果下面编译了一下就囧了.发现是我的多线程模块里面用的sem_init出了问题. X给我看了看MacOSX上函数定义&实现,日,直接就是个残废的return.后来在邮件列表里面看到有人问了.囧的不只我. > I have a library that uses posix semaphores. It compiles fine, but the > call to "sem_init" fails. If I use strerror on the errno set, I get > "Function not implemented". Am I missing something? Why would posix > semaphores not be implemented on Mac OS X? Unnamed POSIX semaphores are indeed not implemented. Semaphores are implemented on top of mach semaphores, so that probably ties into the reasoning. You must create a named semaphore via sem_open(). You'll have to pass the semaphore name into sem_unlink() as well. Named POSIX semaphores work wonderfully well on MacOS X, but you do have to do a little extra management with the semaphore names. I recommend filing a feature request for unnamed POSIX semaphore support. Chris -- Chris Scharver Electronic Visualization Laboratory The University of Illinois at Chicago
Written by KiSSinGGer on 二月 20th, 2009
原文刊载
http://advdbg.org/blogs/advdbg_system/articles/1492.aspx
By Raymond
使用内核调试会话也可以执行一些用户态调试任务,比如向位于用户态的模块设置断点。但这样做与使用用户态调试器有什么不同呢?我们就以向NTDLL.dll模块的ZwTerminateProcess函数(Stub)为例谈谈二者的区别。
区别一、在内核调试会话中设置这个断点的“难度”略大些。这是因为NTDLL不属于内核态的模块,所以内核会话通常不会加载这个模块(的符号),因此当执行bp命令时很可能被自动蜕化为bu命令。
0: kd> bp ntdll!ZwTerminateProcess
Bp expression ‘ntdll!ZwTerminateProcess’ could not be resolved, adding deferred bp
恢复执行后,一般的操作也不会触发调试器来加载NTDLL模块和解决这个未决的断点。因此再中断下来,重新加载符号也可能没有用:
2: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
……………………………………………………………………………………………………………..
Loading User Symbols
Loading unloaded module list
……..
2: kd> bl
0 eu 0001 (0001) (ntdll!ZwTerminateProcess)
使用.reload命令强制加载这个模块也不那么容易:
0: kd> .reload /s /f ntdll.dll
"ntdll.dll" was not found in the image list.
Debugger will attempt to load "ntdll.dll" at given base 00000000.
Please provide the full image name, including the extension (i.e. kernel32.dll)
for more reliable results.Base address and size overrides can be given as
.reload <image.ext>=<base>,<size>.
Unable to add module at 00000000
那么该如何设置呢?方法一需要以下几步:
1.A 使用!process命令显示当前进程:
kd> !process
PROCESS 80af22a0 SessionId: none Cid: 0000 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1001e38 HandleCount: 240.
Image: Idle
如果像上面这样是IDLE进程或者是System这些没有用户态的进程,那么就需要执行下面一步,否则跳到1.C。
1.B 使用!process 0 0命令列出所有进程,然后选一个普通的Windows进程,并切换到这个进程:
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
…
PROCESS 82748330 SessionId: 0 Cid: 0110 Peb: 7ffde000 ParentCid: 059c
DirBase: 13076000 ObjectTable: e1a55640 HandleCount: 72.
Image: notepad.exe
kd> .PROCESS 82748330
Implicit process is now 82748330
WARNING: .cache forcedecodeuser is not enabled
1.C 执行.reload或.reload /user重新加载符号:
kd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
……………………………………………………………………………………
Loading User Symbols
………………………….
Loading unloaded module list
…………………………
kd> lm m ntdll
start end module name
7c800000 7c8c3000 ntdll (pdb symbols) d:\symbols\ntdll.pdb\9A2A73EBE8194059A14361915257B0B01\ntdll.pdb
第二种看起来可能更费事的方法就是在系统服务的内核函数设置断点,断点命中后,执行栈回溯这样的命令,再执行.reload(加/user会省些时间,不是必须)。例如:
0: kd> bp nt!NtTerminateProcess
0: kd> g
Breakpoint 2 hit
nt!NtTerminateProcess:
81a1b043 8bff mov edi,edi
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0×12a (FPO: [0,3] TrapFrame @ 9f272d64)
WARNING: Frame IP not in any known module. Following frames may be wrong.
0021f998 7682d873 00000000 77e8f3b0 ffffffff 0×77c20f34
…
0: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
………………………………………………………………………………………………………………
Loading User Symbols
………………
再执行kv:
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0×12a (FPO: [0,3] TrapFrame @ 9f272d64)
0021f978 77c20580 77bfa35f 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0021f97c 77bfa35f 00000000 00000000 00af0e70 ntdll!NtTerminateProcess+0xc (FPO: [2,0,0])
0021f998 7682d872 00000000 77e8f3b0 ffffffff ntdll!RtlExitUserProcess+0×39 (FPO: [Non-Fpo])
此时可以确信内核调试会话已经加载NTDLL的符号了,再显示断点:
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
2 e 81a1b043 0001 (0001) nt!NtTerminateProcess
这个显示表明内核调试会话已经落实了这个用户态的断点。
如果是在加载NTDLL模块后再执行bp命令,恢复执行后,KD会有一个提示告诉我们它成功的向断点位置写入了INT 3。
0: kd> bp ntdll!ZwTerminateProcess
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
0: kd> g
KD: write to 77c20574 ok
相对而言,如果是在用户态调试会话中,因为NTDLL会被映射到所有用户态进程中,而且ZwTerminateProcess是导出的函数,所以bp ntdll!ZwTerminateProcess会非常顺利的执行。
区别二、断点的作用范围不同,在内核调试会话中设置的ntdll!NtTerminateProcess断点会影响所有进程(可能有特例),而在用户态调试中对这个位置设置的断点只对当前进程有效。举例来说,刚才在内核调试会话中设置bp断点时的当前进程是notepad,但是当我们关闭计算器进程时这个断点也会命中。甚至当我们新启动一个WinMine程序,然后关闭它时,断点也会命中。
相对而言,如果是在调试notepad进程的用户态调试会话中对ntdll!NtTerminateProcess设置一个断点,那么这绝不会影响其它进程。
那么为什么有这个差异呢?
首先解释一下,为什么在内核调试会话中设置的断点会影响所有进程。还是通过试验来说明,我们先想办法观察到我们设置的断点所对应的INT 3指令。当KD落实我们的断点后,将目标再中断到调试器,这时无论是直接观察线性地址还是物理地址,都看不到INT 3:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
0: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 000000001C9AC867 contains 000000001DDDD025
pfn 1c9ac —DA–UWEV pfn 1dddd —-A–UREV
0: kd> !dd 1dddd574
#1dddd574 000152b8 0300ba00 12ff7ffe 900008c2
#1dddd584 000153b8 0300ba00 12ff7ffe 900008c2
#1dddd594 000154b8 0300ba00 12ff7ffe 00498dc3
#1dddd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
#1dddd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
#1dddd5c4 000157b8 0300ba00 12ff7ffe 900010c2
#1dddd5d4 000158b8 0300ba00 12ff7ffe 900018c2
#1dddd5e4 000159b8 0300ba00 12ff7ffe 900010c2
这是因为调试器在将目标中断到调试器之前会恢复已经设置的断点,按Ctrl+Alt+D启用WinDBG与KD的通信过程后就可以看到这样的信息:
DbgKdRestoreBreakPoint(1) returns 00000000
当恢复执行时,WinDBG会重新把断点写入:
DbgKdWriteBreakPoint(77c20574) returns 00000000, 1
那么如何观察到写入的INT 3呢?一种很惬意的方法就是使用ITP这样的硬件调试器,用了ITP,对付这样的任务真是手到擒来(图1)。

图1 使用硬件调试器观察断点指令(0xCC)
因为NTDLL是映射到所有进程中的,所以每个进程执行NtTerminateProcess函数时都会撞见这个0xCC,于是乎这个断点对所有进程都起作用也就在情理之中了。
下面再说说另一种情况,也就是在用户态调试器中对ntdll!NtTerminateProcess设置断点,难道这时就没有把0xCC写在大家都会“撞见”的地方么?的确如此。
我们在内核调试会话中使用bc *命令清除所有断点,并恢复执行一次,而且通过ITP观察确保刚才的0xcc已经不在。然后在目标系统中启动系统中自带的NTSD来调试计算器程序,并使用bp ntdll!NtTerminateProcess设置一个断点。恢复执行一次,以便让调试器写入这个断点。然后退出计算器程序,这时计算器程序会中断到NTSD,NTSD中不做分析,直接用g命令恢复执行,这下,我们前面设置的nt!NtTerminateProcess断点会命中,也就是中断到内核调试器中。
在内核调试器中,观察nt!NtTerminateProcess所对应的线性地址:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
睁大眼睛看那个0xCC,对的,这里的确有0xCC。因为内核断点已经取消了,这一定是用户态调试器写入的。
接下来的问题是,既然这里有0xCC,那么为什么不影响其它进程呢?注意这个线性地址与前面的一模一样。
其中的奥妙在于这个线性地址已经不再是前面那个物理地址了:
1: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 00000000012E0867 contains 00000000049BD025
pfn 12e0 —DA–UWEV pfn 49bd —-A–UREV
虽然还同是一个线性地址,但是它现在对应的物理地址变成了49bd574。观察这个物理地址,其内容与刚才使用线性地址的得到的结果是一样的:
1: kd> !dd 49bd574
# 49bd574 000152cc 0300ba00 12ff7ffe 900008c2
# 49bd584 000153b8 0300ba00 12ff7ffe 900008c2
# 49bd594 000154b8 0300ba00 12ff7ffe 00498dc3
# 49bd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
# 49bd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
# 49bd5c4 000157b8 0300ba00 12ff7ffe 900010c2
# 49bd5d4 000158b8 0300ba00 12ff7ffe 900018c2
# 49bd5e4 000159b8 0300ba00 12ff7ffe 900010c2
而此时,物理地址1dddd574那里根本没有0xCC。
说到这里,谜团基本揭开了。事实上, 对于一个普通的进程,系统会把NTDLL的代码映射给它,如果这个进程始终很普通,那么它便会永远使用这份映射过来的代码。但是当它要修改代码时,系统会执行所谓的Copy on Write动作,为其复制一份,让它来写。结合我们的情况,当在用户态调试会话中向NTDLL中设置断点时,系统为其复制了一份代码,让它去写,因此它写入的断点只有它自己“撞的到”,不会影响其它进程。但是当在内核会话中写入断点时,因为是内核调试引擎执行的写动作,所以没有触发Copy on Write,因此KD写入的断点写在了公共的代码上,会影响到使用这个公共代码的所有进程。