记录
二维数组的指针形式
Array[2][3]:
1> *(*(Array + i) + j)
2> *(Array + i * 行长 + j)
详见《C专家编程》
=========================分割线============================
const的后面如果紧随着一个类型说明符,const作用于类型说明符 其他情况下,const作用于它左边紧邻的指针的*号 《C专家编程》P64
=========================分割线============================
互斥对象内部包含有一个线程ID,调用ReleaseMutex时会检查线程ID是否和当前的线程ID相符,不符会返回失败,如果主线程拥有互斥对象,在别的线程中调用ReleaseMutex会失败的,因为线程ID不符,所以互斥对象需要谁拥有谁释放,参考孙鑫VC视频第15课,时间:63:20 <windows核心编程>P255
=========================分割线============================
new 只要你系统的内存还够用,都是在内存里的,不够的话 依标准会抛异常
操作系统如果发现物理内存不够用,会将部分内存调到硬盘上,使用的时候再调回来(换页) virtualalloc 分配的则统统先保存到硬盘上,使用的时候调入内存
<Windows核心编程>P362
=========================分割线============================
使用通用打开对话框打开文件时,会改变当前目录,变为被打开文件的那个目录
=========================分割线============================
关于<Windows核心编程>第19章的P539的死锁问题,根据http://blog.csdn.net/breaksoftware/article/details/8159088所介绍, 线程调用大概是这样的:LdrpInitialize -->LdrpInitializeThread -->LdrpCallInitRoutine,进而调用DllMain 在LdrpInitialize中进入关键段,也就是书中所说的每个进程的锁,LdrpInitializeThread在关键段中包括着,至于调不调用DisableThreadLibCalls是管不着这个关键段的,也就说调用了它,只不过是不掉用DllMain了,但是任然还是会运行LdrpInitializeThread的,但这个函数被包含在关键段中~当前线程获得这个关键段,然后在DllMain中创建线程,然后根据书中所说,新线程会获得锁,但此时当前线程还在关键段中(因为有WaigForSingleObject),所以新线程还是会被关键段给阻塞等待,死锁依然存在~
=========================分割线============================
休眠、挂起、睡眠、阻塞,似乎是我们一开始学习linux最郁闷的四个概念。
其实简单地解释应该是这样:
阻塞操作是指在执行设备操作的时候若不能获得资源将挂起进程,直到满足该操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等到的条件被满足。
因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程,唤醒进程的地方最大可能发生在中断里面,因为硬件资源的获得的同时往往伴随着一个终端。
所以,阻塞是一种统称,而挂起是它的具体行为,休眠是一种状态。
至于睡眠,在调度制度里,睡眠是指被放在等待队列里的进程的状态。
=========================分割线============================
记一下关于SEH全局展开,只有在EXCEPTION_EXECUTE_HANDLER时才进行全局展开,全局展开时,如果异常发生在__try __finally块中,先找到能够处理这个异常的__except,但不执行异常处理代码,然后如果异常过滤值是EXCEPTION_EXECUTE_HANDLER,进行全局展开,首先执行最内层的__finally,然后执行刚刚找到的能够处理这个异常的__except,具体详情查看<Windows核心编程>第24章的全局展开,<Windows核心编程>第24章,P647,全局展开这节的最后一段,<Windows核心编程>第25章25.4节第一段
ps: 如果在__finally的异常处理程序中再次发生异常,将不会执行产生异常的指令后面的指令,如果在__except的异常处理块中发生异常,同样不会执行产生异常的指令后面的指令,而是寻找上一个匹配的__except。如果是异常过滤程序中发生异常,调试时貌似会出现死循环,貌似是如果异常过滤程序发生异常,这个__except会捕获,捕获再进入异常过滤程序,然后再发异常,__except再捕获……
1 void foo() 2 { 3 __try 4 { 5 int v = 0; 6 int n = 5 / v; 7 } 8 __finally 9 { // 如果__finally里发生一个异常,则异常指令后的指令将不会执行 10 // 先执行foo的finally和func的finally的原因就是发生了异常 11 // 需要执行test中的异常处理代码块,跳出了finally的try的代码块 12 // 所以要执行finally 13 MessageBox(NULL, L"finally2", NULL, 0); 14 } 15 } 16 void func() 17 { 18 __try 19 { 20 foo(); 21 } 22 __finally 23 { 24 MessageBox(NULL, L"finally1", NULL, 0); 25 } 26 } 27 28 void test() 29 { 30 __try 31 { 32 func(); 33 } 34 __except (EXCEPTION_EXECUTE_HANDLER) 35 { // 如果这里产生一个异常,将不会执行产生异常的指令的后面的指令 36 // 继续寻找上一个__except 37 MessageBox(NULL, L"except2", NULL, 0); 38 } 39 } 40 41 int _tmain(int argc, _TCHAR* argv[]) 42 { 43 __try 44 { 45 test(); 46 } 47 __except (EXCEPTION_EXECUTE_HANDLER) 48 { 49 MessageBox(NULL, L"except1", NULL, 0); 50 } 51 return 0; 52 } 53 54 执行结果: 55 finally2 56 finally1 57 except2 58 ============================ 59 void foo() 60 { 61 __try 62 { 63 *(PBYTE)NULL = 0; 64 } 65 __except (EXCEPTION_EXECUTE_HANDLER) 66 // 如果是这样的,应为执行异常处理程序不用跳出finally的try 67 // 所以先执行except后执行finally 68 { 69 70 MessageBox(NULL, TEXT("except"), NULL, MB_OK); 71 } 72 } 73 74 void test() 75 { 76 __try 77 { 78 foo(); 79 } 80 __finally 81 { 82 MessageBox(NULL, TEXT("finally"), NULL, MB_OK); 83 } 84 }
=========================分割线============================
TrackPopupMenu函数是同步的,也就是说弹出菜单后,不选择是不会返回的,必须等到菜单弹出后,然后选择了菜单,如果这个菜单的功能也是同步的,必须等到这个菜单处理完,比如弹出一个对话框,等这个对话框关闭了,这个函数才返回.
=========================分割线============================
关于LoadMenu和GetMenu返回的句柄之所以不同,是因为GetMenu返回的是当前窗口已加载的菜单的句柄,而LoadMenu则是新加载一个菜单资源,然后返回其句柄
ps:SetMenu可以把LoadMenu新加载的菜单资源设置为当前菜单
=========================分割线============================
关于CreateFileMapping:
关于使用CreateFileMapping把PE文件映射到进程地址空间中的问题
用CreateFileMapping为一个PE文件创建内存映射并且第三个参数包含SEC_IMAGE标志时,映射后的内存文件会按内存粒度和节中指定的VA对齐~ 此时内存中的布局和磁盘上的通常不一样,因为一般情况下内存对齐粒度比磁盘对齐粒度大~
=========================分割线============================
MEMORY_BASIC_INFORMATION结构一些成员备忘 BaseAddress >> 块(一些具有相同保护属性连续的页面)的起始地址, 将指定地址向下取整到页面大小.(同<Windows内核情景分析P54的区块>) AllocationBase >> 当前内存区域的起始地址, 一个内存区域可以包含多个块, 该区域包含指定地址, 就一个模块而言, 这个地址是模块的基地址. 指定地址为MEM_FREE状态时,此成员为0.(同<Windows内核情景分析P54的区间>) RegionSize >> 当前块的长度
=========================分割线============================
关于MmCreateHyperspaceMapping的一些理解备忘:
在MmCopyMmInfo中, 本进程页目录的HYPERSPACE目录项被赋值为一个物理页号pfn[1], 可以理解为一个页表4kb, 1024个表项. MmCreateHyperspaceMapping将指定的物理页映射到自己的一个空白表项, 然后返回该表项的地址. 返回时Hyperspace基地址 + i * PAGE_SIZE是为了形成地址, Hyperspace是形成页目录号, i * PAGE_SIZE是形成页表号(页表号和页表号之间间隔4096字节, 因为访问偏移超过1页(4kb)才会换页), 下面用MmGetPageTableForProcess中创建指定进程页目录的临时映射为例, 假如, Hyperspace的基地址为0xc0400000, 空白项的索引为1, 返回0xc0401000, 也就PageDir的值, 设PdeOffset为2, 当执行PageDir[PdeOffset](展开为*(0xc0401000 + 8))时, 将地址0xc0401008分为页目录号, 页表号和偏移3部分, 用页目录号从当前进程的页目录中获取Hyperspace的物理地址, 用页表号在Hyperspace中就可以获得MmCreateHyperspaceMapping中保存的目标进程的页目录的PTE(物理地址)了, 然后用偏移加上目标进程页目录的物理地址, 即可获得指定的PDE:

=========================分割线============================
查找窗口的WndProc(如果本地调试时无法查看win32k的符号, .reload下):
参考, ValidateHwnd反汇编, win32子系统之三:ValidateHwnd函数分析
具体原理: (win32k!gSharedInfo(win32k!tagSHAREDINFO)+0x08) * LOWORD(hwnd) + win32k!gSharedInfo+0x04获得一个win32k!_HANDLEENTRY结构指针,该结构的第一个成员phead是结构win32k!tagWND结构的地址, 该结构0x60处为lpWndProc
=========================分割线============================
编译在别的进程中运行的shellcode时要注意的的编译选项:
增量链接: 启用后调用函数会先到一个jmp, 然后再jmp到真实函数
基本运行时检查: 启用会生成ChkEsp
缓冲区安全检查: 启用会在函数头生成安全 cookie相关代码

浙公网安备 33010602011771号