总结和展望:东方不亮西方亮

一转眼,一年又过去了,又到了总结过去的一年,计划新年的时候了。过去的一年里,最大的感觉是,好想经过了好多事情,但却没有经历什么事情。说起来挺绕的,慢慢来回忆下吧。

首先是工作上的事情,由于在前年整个部门被裁,去了一个价值观上和自己的很不同公司。事实证明,这确实不是什么好的决定,吐槽的事情就不细说了。之所以没换公司,是因为我觉得在北京待不了多久了,跳槽只会坑了其他的公司。所以也就勉强的继续干着。虽说是勉强的干活,但是工作的时间还是将近占用了每天的三分之二。工作的内容也没有什么创造性,基本上就是在可怕的代码里改来改去,当然了,也许这也是他这么可怕的原因吧。

多米乐骨效应,工作占用大量的时间,也导致我2014年的计划大打折扣。脚本编译器,写了一半听了下来,minikernel也没什么进展,唯一比较让人舒心的是,0CCh的山寨小工具,还是在慢慢的变多,其中还有花了大量精力写的everything_study,不过由于算法还没没达到目标效果(没有everything快,数据库也比他的大很多),所以没有放出来,也就是自己在用。确实也是因为工作的原因,没有太多时间和精力去改造文件id间相互索引的算法了。再说说健身,同样的理由,工作时间长影响身体和健身,现在25分钟基本上就跑个4.7km,13年的最后,都是能跑5km多点的。

不过东方不亮西方亮了,有失意的地方,总会在其他地方补回来=v=,生命中终于又多了一个人,能让我更加坚定的回老家了。所以,今年的计划特别难定,因为不知道回老家后到底是什么个情况。只能说回去之前继续坚持着,回去之后去找个心仪的事情。然后,编译器和minikernel希望能继续写,编译器希望能写玩,因为并不难。minikernel就比较复杂了,只能说能写多少写多少。另外我也特别喜欢山寨各种各样的小工具,自己造轮子自己用,赶紧挺开心的。健身方面,回去前照旧,回去后尽量保持。

其实生活上14年真的还算挺开心的,和sysdbg的博主一起还打完了好几个ps3的游戏。比如《神秘海域3》,《第一次世界大战——勇敢的心》等等=v=,还有他13年欠我的金钱豹还没请我吃呢…

最后,我还是希望2015年,我自己和家人、亲戚以及基友们,身体健康,阖家辛福快乐,工作顺利发大财!

Python获得文件版本信息

Python干啥都挺方便的,出了调用win32的api。当然了,可以用pywin32这种库。但我不喜欢为了一两个api又给简单的东西加一堆依赖。比如获取文件版本,本来想在网上找个函数复制过去用得了,却发现还真没啥好用的。无奈自己就写了一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class VS_FIXEDFILEINFO(Structure):
_fields_ = [
("dwSignature", c_int),
("dwStrucVersion", c_int),
("dwFileVersionMS", c_int),
("dwFileVersionLS", c_int),
("dwProductVersionMS", c_int),
("dwProductVersionLS", c_int),
("dwFileFlagsMask", c_int),
("dwFileFlags", c_int),
("dwFileOS", c_int),
("dwFileType", c_int),
("dwFileSubtype", c_int),
("dwFileDateMS", c_int),
("dwFileDateLS", c_int)
]
def LOWORD(dword): return dword & 0x0000ffff
def HIWORD(dword): return dword >> 16
def GetFileVersion(filename):
size = windll.version.GetFileVersionInfoSizeW(filename, None)
if not size:
return ''
res = create_string_buffer(size)
windll.version.GetFileVersionInfoW(filename, None, size, res)
r = VS_FIXEDFILEINFO()
l = c_uint()
p = c_void_p()
windll.version.VerQueryValueW(res, '\\', byref(p), byref(l));
memmove(byref(r), p, sizeof(VS_FIXEDFILEINFO))
if not l.value:
return ''
return ('%d.%d.%d.%d' % (HIWORD(r.dwFileVersionMS), LOWORD(r.dwFileVersionMS),
HIWORD(r.dwProductVersionLS), LOWORD(r.dwProductVersionLS)));

Tips

DNSSwitcher —— 一个方便切换DNS的小工具

最近百度也推出的自己的公共DNS,现在可供我们选择使用的DNS也多了起来。但是每次更改DNS都输入IP,确实挺麻烦的。于是我周末在家就写了个切换DNS的小程序,绿色且易用,能在配置好的DNS直接切换,当然也能切换回自动获取DNS的模式。至于配置文件,可以手动修改,也可以通过程序来修改,都挺方便的。

20141214190237

配置文件格式如
[Google DNS]
dns1=8.8.8.8
dns2=8.8.4.4
[Open DNS]
dns1=208.67.222.222
dns2=208.67.220.220

下载:DNSSwitcher

Tips

获得使用打开保存对话框操作文件的记录

今天无意中看到了HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU这个键值的用途,虽然感觉没啥实际用途,但是也挺有趣的,于是写了个小程序读取它。这个键值的意义非常明确,就是记录打开保存对话框的最近操作的文件的PIDL。所以我们可以通过PIDL来获得文件路径,就这么简单,确实没啥特别的实际用途吧,就当娱乐了。枚举的效果如下:

20141117231005

代码也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "stdafx.h"
#include <atlbase.h>
#include <atlstr.h>
#include <Shlobj.h>
#include <locale.h>
#include <vector>
#pragma comment(lib, "shell32.lib")
const TCHAR mru_path[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU");
void EnumMRUValue(HKEY subkey, std::vector &mru;_files)
{
ULONG i = 0, j = 0;
TCHAR value_name[MAX_PATH];
ULONG value_name_length = MAX_PATH;
UCHAR data_buffer[1024];
ULONG data_length = sizeof(data_buffer);
while (RegEnumValue(subkey, i++, value_name, &value;_name_length, 0, NULL, data_buffer, &data;_length) == ERROR_SUCCESS)
{
if (_tcscmp(value_name, TEXT("MRUListEx")) != 0) {
CComPtr malloc_ptr;
HRESULT hr = SHGetMalloc(&malloc;_ptr);
LPITEMIDLIST file_pidl = (LPITEMIDLIST)malloc_ptr->Alloc(sizeof(UCHAR) + data_length);
if (file_pidl) {
memcpy(file_pidl, data_buffer, data_length);
WCHAR file_path[MAX_PATH] = { 0 };
if (SHGetPathFromIDList(file_pidl, file_path)) {
mru_files.push_back(file_path);
}
malloc_ptr->Free(file_pidl);
}
}
value_name_length = MAX_PATH;
data_length = sizeof(data_buffer);
}
}
BOOL PrintMRUFiles()
{
HKEY subkey;
LSTATUS l = RegOpenKeyEx(HKEY_CURRENT_USER, mru_path, 0, KEY_READ, &subkey;);
if (l != ERROR_SUCCESS) {
return FALSE;
}
ULONG i = 0;
TCHAR key_name[MAX_PATH];
ULONG key_name_length = MAX_PATH;
while (RegEnumKeyEx(subkey, i++, key_name, &key;_name_length, 0, NULL, NULL, NULL) == ERROR_SUCCESS)
{
HKEY ext_key;
LSTATUS l = RegOpenKeyEx(subkey, key_name, 0, KEY_READ, &ext;_key);
if (l == ERROR_SUCCESS) {
std::vector mru_files;
EnumMRUValue(ext_key, mru_files);
if (mru_files.size() > 0) {
_tprintf(TEXT("Extension Name : %s\n"), key_name);
ULONG j = 0;
for (std::vector::iterator it = mru_files.begin(); it != mru_files.end(); ++it) {
WIN32_FILE_ATTRIBUTE_DATA attribute_data = { 0 };
if (GetFileAttributesEx(it->GetString(), GetFileExInfoStandard, &attribute;_data)) {
ULONGLONG file_size = ((ULONGLONG)attribute_data.nFileSizeHigh) << 32 | attribute_data.nFileSizeLow;
_tprintf(TEXT("\t %u %s % 11I64u KB] %s\n"), j++,
(attribute_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ? TEXT("[FILE") : TEXT("[DIR "),
(file_size + 1023) / 1024,
it->GetString());
}
else {
_tprintf(TEXT("\t %u [ERROR_FILE_NOT_FOUND] %s\n"), j++,
it->GetString());
}
}
}
RegCloseKey(ext_key);
}
key_name_length = MAX_PATH;
}
RegCloseKey(subkey);
return TRUE;
}
int _tmain(int argc, _TCHAR* argv[])
{
setlocale(LC_ALL, "chs");
PrintMRUFiles();
return 0;
}

Tips

主线程退出前请先退出子线程

我们知道理论上如果一个进程的主线程退出,整个进程就会销毁,子线程自然也是要退出的。但是这并不意味着,程序退出的时候我们就能不问不管自己创建的子线程,因为不管他还真有可能出问题。下面是一个典型的主线程退出过程中,而子线程未退出,造成死锁整个进程的分析图,死锁在c runtime里,死锁原因看图便知,也不用太多解释了。

20141106094954

Tips

ClipMonitor 简单的剪切板监控工具

周末花了点时间写了个剪切板监控工具,他会记录剪切板里面的数据,并且支持用HEX和TEXT对数据进行分析。
涉及到的技术很简单,网上已经有一堆了,没什么可说的,我这里主要是加入了HEX的模块,方便对未知的剪切板数据进行分析而已。

20141102204618

20141102204638

20141102204651

下载:ClipMonitor

Tips

利用Windbg脚本监控DLL加载状态

开发过大型程序的人都遇到过这样的问题,一个版本Release后,发现有的功能怎么也不对。但是每个模块在开发人员单独的环境下又是正常运行。那么遇到这样的问题,其中一个能想到的就是DLL模块没有加载正确。一般情况下,这种时候我们可以使用depends这样的工具查看模块的依赖情况,以判断出哪个模块是出问题的那个。但是如果如果真的遇上大型的程序,DLL模块很多,而且动态静态加载不一,这样光靠depends这样的工具是不能满足需求的。所以我这里写了个Windbg脚本来监控DLL加载状态,因为是动态调试,所以很轻松的就能找到加载问题,无论DLL是静态加载还是动态加载。

以下脚本是Windows 7 32Bit,对于64bit,稍微修改下就行。不过如果是Windows 8,DLL的加载细节已经发生变化,所以这个脚本就不适用了。希望能抛砖引玉一下,期待更多系统的脚本分享出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bp ntdll!LdrpFindOrMapDll "
.push /r /q
r @$t0 = poi(@esp+4)
r @$t1 = poi(@esp+8)
g @$ra
.printf \"name = %msu\\n\", @$t0
.printf \"path = %msu\\n\", @$t1
.printf \"Load status = %08X\", @eax
.if((@eax&0xffffffff) != 0) {.printf /D \" WARNNING\\n\"} .else {.printf \"\\n\"}
.pop /r /q
g
"

最后看看脚本的效果吧,如果加载失败了,那么返回的状态值就不是0。
20141027164220
20141027164424

Tips

用Windows Event监控前台窗口变化

网络上有这样一种小工具,号称可以提高计算机响应速度。简单看了下其中的一款工具,实现原理很简单,就是监控前台窗口的变化,设置进程线程的优先级。设置进程线程的优先级无非就是SetPriorityClass和SetThreadPriority这两个函数,主要问题就是要获得前台窗口的情况。
Windows提供了一种叫做Windows Event Hook的机制,来让我们获得多种不同的事件,其中一种就是前台窗口变化事件。所以要完成这个功能很简单,只需要注册一个针对EVENT_SYSTEM_FOREGROUND的HOOK就行了。注册和反注册Hook的函数分别是SetWinEventHook和UnhookWinEvent。
比如注册EVENT_SYSTEM_FOREGROUND事件的Hook,我们只需要这样:

1
2
3
4
5
HWINEVENTHOOK hWinEventHook = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,
NULL, WinEventProc, 0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);

前两个参数设置我们感兴趣的事件范围,由于我们最后一个参数设置的WINEVENT_OUTOFCONTEXT,表示Hook函数并不在产生Event的进程内部,所以hmodWinEventProc设置为NULL。WinEventProc则是我们的Hook函数idProcess和idThread为0,表示我们关心所有桌面上的进程线程,最后的flags还有一个WINEVENT_SKIPOWNPROCESS表示我们对自己的进程不感兴趣。
要反注册Hook只需要

1
2
UnhookWinEvent(hWinEventHook);

WinEventProc的很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void CALLBACK WinEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime
)
{
if (hwnd &&
idObject == OBJID_WINDOW &&
idChild == CHILDID_SELF &&
event == EVENT_SYSTEM_FOREGROUND) {
...
}
}

其中只需要知道hwnd就是当前的前台窗口就行了。然后通过hwnd和函数GetWindowThreadProcessId,获得进程线程id,自然就能获得其句柄,最后调用调整进程线程优先级的函数即可。

Tips

更新Windbg扩展0cchext

更新了pe_export和pe_import两个命令,功能如图

20140919095914

20140919100003

20140919100101

Commands for 0cchext.dll:
!autocmd - Execute the debugger commands.(The config file is
autocmd.ini)
!dpx - Display the contents of memory in the given range.
!dtx - Displays information about structures. (The config file is
struct.ini)
!favcmd - Display the favorite debugger commands.(The config file is
favcmd.ini)
!grep - Search plain-text data sets for lines matching a regular
expression.
!help - Displays information on available extension commands
!hwnd - Show window information by handle.
!init_script_env - Initialize script environment.
!pe_export - Dump PE export functions
!pe_import - Dump PE import modules and functions
!setvprot - Set the protection on a region of committed pages in the
virtual address space of the debuggee process.
!url - Open a URL in a default browser.
!version - Displays the version information for 0cchext.dll
!help will give more information for a particular command

下载:0cchext

DebuggingTips