-
-
[原创] Pintools 之 Shadow Stack 编写
-
发表于: 2020-9-6 16:21 6467
-
本文是对加州大学滨州分校cs260实验二的作答.shadow stack是一种防护机制,保护函数返回值地址被覆写.阅读本篇文章前要求对Intel Pin有一定的了解.
思路
根据实验指导,初步的shadow stack 运行原理如下:
代码
测试
思路
版本一代码只适合检测单线程情况下的覆写,多线程情况下会更加复杂的入栈和出栈操作.
如: thread1 : call fun1, thread2 : call fun2. 分别将thread1和thread2的返回值地址ret_Address1和ret_Address2依次压入stack中。此时栈顶为ret_Address2,但若线程切换至thread1, 并执行ret指令,就会出现不匹配的情况,但此时并不是将返回值地址覆写的情况. 为此,我们需要为每个线程设置一个shadow stack, 其余操作和版本一相同,部分代码做了优化.
代码
请使用最新的Intel Pin进行实验,否则会出现各种问题.
// example01.c #include <stdio.h> #include <string.h> int IsPasswordOkay(void) { char Password[12]; gets(Password); if (!strcmp(Password, "goodpass")) return 1; return 0; } int main(void) { int PwStatus; puts("Enter password:"); PwStatus = IsPasswordOkay(); if (PwStatus == 0) { puts("Access denied"); return -1; } puts("Access granted"); return 0; }
// 版本一 #include <iostream> #include <fstream> #include <unistd.h> #include <cstdlib> #include <vector> #include "pin.H" using std::cerr; using std::ofstream; using std::ios; using std::string; using std::endl; using std::hex; using std::cout; std::vector<unsigned int> stack; VOID call_ins_call(ADDRINT ip, UINT32 size){ // 入栈 UINT32 next_eip = ip + size; stack.push_back(next_eip); } VOID ret_ins_call(CONTEXT * ctxt){ VOID * current_esp = (VOID *)PIN_GetContextReg(ctxt, REG_STACK_PTR); UINT32 current_eip = (UINT32)(*(int *)current_esp); if(stack.size() > 0){ // 出栈并比较 UINT32 original_ret_value = (UINT32)stack.back(); stack.pop_back(); if(original_ret_value != current_eip){ cout << "control flow jacking is happending" << endl; exit(1); } } else { cout << "stack is empty" << endl; } } VOID Trace(TRACE trace, VOID *v) { // Visit every basic block in the trace for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { for(INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)){ // 是call 指令 if(INS_IsCall(ins)){ UINT32 size = INS_Size(ins); INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)call_ins_call, IARG_INST_PTR, IARG_UINT32, size, IARG_END); } // 是 ret指令 if(INS_IsRet(ins)){ INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)ret_ins_call, IARG_CONTEXT, IARG_END); } } } } // This function is called when the application exits VOID Fini(INT32 code, VOID *v) { cout << "Over" << endl; } /* ===================================================================== */ /* Print Help Message */ /* ===================================================================== */ INT32 Usage() { cerr << "This tool is used to detect control flow jacking" << endl; return -1; } /* ===================================================================== */ /* Main */ /* ===================================================================== */ int main(int argc, char * argv[]) { // Initialize symbol process PIN_InitSymbols(); // Initialize pin if (PIN_Init(argc, argv)) { return Usage(); } // 注册Trace粒度回调函数 TRACE_AddInstrumentFunction(Trace, 0); // Register Fini to be called when the application exits PIN_AddFiniFunction(Fini, 0); // Start the program, never returns PIN_StartProgram(); return 0; }
# 编译 example01 gcc -g -fno-stack-protector -z execstack -o example01 example01.c # 编译pin tools 之 shadow stack, 将上述代码替换source/tools/ManualExamples/inscount0.cpp中的内容 make # 测试 # 正常情况 path/pin -t ./inscount0.so -- ./example01 Enter password: 11111111 Access denied # 覆写情况 Enter password: 111111111111111111111111111111111111 control flow jacking is happending # core dump情况 Enter password: 11111111111111 Over Segmentation fault (core dumped) # 说明 # 只有覆盖了返回值地址才会被检测出来,如果长度不够,指挥core dump.
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-9-6 16:23
被baolongshou编辑
,原因:
赞赏
他的文章
- [原创]C++ static关键字引发的思考 10857
- [原创]Windows 之 CRT的检测内存泄露 16565
- [讨论] <<程序员的自我修养 -- 链接、装载与库>> 书籍相关问题 32328
- [原创] Kamnira Adware分析 4466
看原图
赞赏
雪币:
留言: