├── HeapView.py ├── README.md ├── pintool ├── makefile ├── makefile.rules ├── obj-ia32 │ └── pintool.so ├── obj-intel64 │ └── pintool.so └── pintool.cpp └── tests ├── Makefile ├── test1.c ├── test1.svg ├── test2.c ├── test2.svg ├── test3.c └── test3.svg /HeapView.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from copy import deepcopy 3 | import random 4 | import sys 5 | 6 | class Chunk(): 7 | def __init__(self,start,end,dstart,dend): 8 | self.start = start 9 | self.end = end 10 | self.dstart = dstart 11 | self.dend = dend 12 | self.free = False 13 | self.color = random_color() 14 | def get_color(self): 15 | if self.free == False: 16 | return self.color 17 | else: 18 | return (240,240,240) 19 | def get_start(self): 20 | return self.start 21 | def get_dstart(self): 22 | return self.dstart 23 | def get_dend(self): 24 | return self.dend 25 | def get_end(self): 26 | return self.end 27 | def set_free(self): 28 | self.free = True 29 | def isFree(self): 30 | return self.free 31 | def __repr__(self): 32 | return "Chunk(%#x,%#x,data(%#x,%#x),free=%d)" % (self.start,self.end,self.dstart,self.dend,self.free) 33 | def overflow(self,addr): 34 | if addr > self.start and addr <= self.dstart: 35 | overflow=addr-self.start 36 | s="s" if overflow > 1 else "" 37 | return "next_chunk (%d byte%s)" % (overflow,s) 38 | if addr > self.dend and addr < self.end: 39 | overflow=addr-self.dend 40 | s="s" if overflow > 1 else "" 41 | return "padding (%d byte%s)" % (overflow,s) 42 | return None 43 | 44 | class MemoryWrite(): 45 | def __init__(self,name,start,end): 46 | self.name = name 47 | self.start = start 48 | self.end = end 49 | def get_name(self): 50 | return self.name 51 | def get_start(self): 52 | return self.start 53 | def get_end(self): 54 | return self.end 55 | def __repr__(self): 56 | return "%s(%#x->%#x)" % (self.name,self.start,self.end) 57 | 58 | class State(): 59 | def __init__(self,operation,chunks,memory_writes,min_addr,max_addr): 60 | self.operation=operation 61 | self.chunks = chunks 62 | self.memory_writes = memory_writes 63 | self.max_addr=max_addr 64 | self.min_addr=min_addr 65 | def get_chunks(self): 66 | return self.chunks 67 | def get_memoryWrites(self): 68 | return self.memory_writes 69 | def get_name(self): 70 | return self.operation.replace("<","<").replace(">",">") 71 | def get_min_addr(self): 72 | return self.min_addr 73 | def get_max_addr(self): 74 | return self.max_addr 75 | 76 | def svg_header(height): 77 | return ''' 78 | 79 | ''' % (height) 80 | 81 | def svg_footer(): 82 | return ''' 83 | AB 84 | ''' 85 | 86 | def svg_style(): 87 | return '''''' 106 | 107 | def svg_script(): 108 | return ''' 160 | ''' 161 | 162 | def svg_rec(x,y,width,height,rx,ry,colors,opacity=1,boldtext="",text=""): 163 | if width < 4: 164 | width=4 165 | x-=2 166 | svg='''\n''' % (colors[0],colors[1],colors[2],opacity,rx,ry,height,width,y,x,boldtext,text) 172 | return svg 173 | 174 | def svg_dashed_rec(x,y,width,height,rx,ry,colors,opacity=1,boldtext="",text=""): 175 | svg='''\n''' % (colors[0],colors[1],colors[2],opacity,rx,ry,height,width,y,x,boldtext,text) 182 | return svg 183 | 184 | def svg_text(x,y,text,bold="100",color=(0,0,0)): 185 | svg='''%s\n''' % (bold,color[0],color[1],color[2],x,y,text) 186 | return svg 187 | 188 | def svg_info(x,y,text): 189 | return ''' 190 | 191 | 192 | %s 193 | 194 | ''' % (x,y,text) 195 | 196 | def parse_arg(arg): 197 | if arg is None: 198 | return None 199 | if arg == "": 200 | return 0 201 | try: 202 | arg=int(arg,0) 203 | except: 204 | pass 205 | return arg 206 | 207 | def parse_call(call_line): 208 | args = [] 209 | name = call_line.split("(")[0] 210 | ret = call_line.split(" = ")[-1] 211 | call_args = call_line.split("(")[1].split(")")[0].split(",") 212 | for arg in [ret,]+call_args: 213 | args.append(parse_arg(arg)) 214 | return (name,args) 215 | 216 | def parse_ltrace(data): 217 | chunks = [] 218 | states = [] 219 | max_addr = 0 220 | min_addr = 0xFFFFFFFFFFFFFFFF 221 | 222 | last_write_function="" 223 | last_write_function_info="" 224 | 225 | lines = data.split("\n") 226 | for line in lines: 227 | memory_writes = None 228 | if line is "": 229 | continue 230 | (call_name,call_args) = parse_call(line) 231 | if call_name == "malloc": 232 | chunk_start = call_args[0]-8 233 | data_start = call_args[0] 234 | data_end = call_args[0]+call_args[1] 235 | size = call_args[1] + 8 + (0x10-1) 236 | size = size - (size%0x10) 237 | if size < 0x20: 238 | size=0x20 239 | chunk_end=chunk_start + size 240 | chunks.append(Chunk(chunk_start,chunk_end,data_start,data_end)) 241 | if chunk_end > max_addr: 242 | max_addr=chunk_end 243 | if chunk_start < min_addr: 244 | min_addr=chunk_start 245 | elif call_name == "free": 246 | # find chunk 247 | free_ok=False 248 | for chunk in chunks: 249 | if chunk.get_dstart() == call_args[1] and not chunk.isFree(): 250 | chunk.set_free() 251 | free_ok=True 252 | if not free_ok: 253 | continue 254 | elif call_name == "memset": 255 | start=call_args[1] 256 | end=call_args[1]+call_args[3] 257 | for chunk in chunks: 258 | if start >= chunk.get_dstart() and start <= chunk.get_dend(): 259 | memory_writes=MemoryWrite(call_name,start,end) 260 | break 261 | elif call_name == "memcpy": 262 | start=call_args[1] 263 | end=call_args[1]+call_args[3] 264 | for chunk in chunks: 265 | if start >= chunk.get_dstart() and start <= chunk.get_dend(): 266 | memory_writes=MemoryWrite(call_name,start,end) 267 | break 268 | elif call_name == "memory_write": 269 | start=call_args[2] 270 | end=start+call_args[3] 271 | function = call_args[1].split("+")[0] 272 | if last_write_function == function: 273 | if last_write_size+last_write_addr == start: 274 | last_write_size+=call_args[3] 275 | continue 276 | else: 277 | for chunk in chunks: 278 | if start >= chunk.get_dstart() and start <= chunk.get_dend(): 279 | memory_writes=MemoryWrite(last_write_function,last_write_addr,last_write_addr+last_write_size) 280 | break 281 | if last_write_function not in ("_int_malloc","_int_free","malloc_consolidate"): 282 | states.append(State("<"+last_write_function_info+">",deepcopy(chunks),memory_writes,min_addr,max_addr)) 283 | last_write_addr = start 284 | last_write_size = call_args[3] 285 | last_write_function=function 286 | last_write_function_info=call_args[1] 287 | else: 288 | if last_write_function != "": 289 | for chunk in chunks: 290 | if start >= chunk.get_dstart() and start <= chunk.get_dend(): 291 | memory_writes=MemoryWrite(last_write_function,last_write_addr,last_write_addr+last_write_size) 292 | break 293 | if last_write_function not in ("_int_malloc","_int_free","malloc_consolidate"): 294 | states.append(State("<"+last_write_function_info+">",deepcopy(chunks),memory_writes,min_addr,max_addr)) 295 | last_write_addr = start 296 | last_write_size = call_args[3] 297 | last_write_function=function 298 | last_write_function_info=call_args[1] 299 | continue 300 | else: 301 | continue 302 | states.append(State(line,deepcopy(chunks),memory_writes,min_addr,max_addr)) 303 | 304 | return states 305 | 306 | def state_to_svg(pos,state,min_addr,max_addr): 307 | svg="" 308 | line_pos = 100*pos + 5*pos 309 | svg+=svg_rec(5, line_pos, 1010, 100, 10, 10, (255,255,255)) 310 | svg+=svg_text(10,line_pos+15,state.get_name(),bold="bold") 311 | for chunk_nb,chunk in enumerate(state.get_chunks()): 312 | overlap=0 313 | for nb,check_chunk in enumerate(state.get_chunks()): 314 | if nb >= chunk_nb: 315 | break 316 | if chunk.get_start() >= check_chunk.get_start() and chunk.get_start() < check_chunk.get_end(): 317 | overlap+=1; 318 | chunk_size = chunk.get_end() - chunk.get_start() 319 | size = int(((chunk_size * 1.0) / (max_addr - min_addr * 1.0)) * 1000) 320 | start = int(((chunk.get_start() - min_addr * 1.0) / (max_addr - min_addr * 1.0)) * 1000) 321 | chunk_dsize = chunk.get_dend() - chunk.get_dstart() 322 | bold_text = "%#x" % (chunk.get_start()) 323 | text = " - %#x (%#x) (%#x -> %#x)" % (chunk_size,chunk_dsize,chunk.get_start(),chunk.get_end()) 324 | if chunk.isFree(): 325 | svg+=svg_dashed_rec(start+10, line_pos + 20 + overlap*20, size, 20, 5, 5, chunk.get_color(),text=text,boldtext=bold_text) 326 | else: 327 | svg+=svg_rec(start+10, line_pos + 20 + overlap*20, size, 20, 5, 5, chunk.get_color(),text=text,boldtext=bold_text) 328 | write = state.get_memoryWrites() 329 | if write is not None: 330 | size = int(((write.get_end() - write.get_start() * 1.0) / (max_addr - min_addr * 1.0)) * 1000) 331 | start = int(((write.get_start() - min_addr * 1.0) / (max_addr - min_addr * 1.0)) * 1000) 332 | colors=(128,255,128) 333 | text_colors=(0,0,0) 334 | text="%s (%#x -> %#x)" % (write.get_name(),write.get_start(),write.get_end()) 335 | tooltip_text="" 336 | overflow=None 337 | for chunk in state.get_chunks(): 338 | overflow=chunk.overflow(write.get_end()) 339 | if overflow is not None: 340 | colors=(255,0,0) 341 | text_colors=(255,255,255) 342 | tooltip_text="overflow in "+overflow+" " 343 | break 344 | # check if end addr is in chunk 345 | found=False 346 | for chunk in state.get_chunks(): 347 | if write.get_end() >= chunk.get_start() and write.get_end() <= chunk.get_end(): 348 | found=True 349 | break 350 | if not found: 351 | overflow="write outside chunk" 352 | colors=(255,0,0) 353 | text_colors=(255,255,255) 354 | tooltip_text=overflow 355 | 356 | 357 | overlap=-1 358 | for check_chunk in state.get_chunks(): 359 | if write.get_start() > check_chunk.get_start() and write.get_start() <= check_chunk.get_end(): 360 | overlap+=1; 361 | if overflow: 362 | svg+=svg_info(start+10+size, line_pos + 24 + overlap*20, tooltip_text) 363 | svg+=svg_rec(start+10, line_pos + 24 + overlap*20, size, 12, 2, 2, colors, opacity=1,text=text) 364 | return svg 365 | 366 | 367 | 368 | def random_color(r=200, g=200, b=125): 369 | 370 | red = (random.randrange(0, 256) + r) / 2 371 | green = (random.randrange(0, 256) + g) / 2 372 | blue = (random.randrange(0, 256) + b) / 2 373 | 374 | return (red, green, blue) 375 | 376 | 377 | 378 | if __name__ == '__main__': 379 | random.seed(100) 380 | chunks=[] 381 | data = open(sys.argv[1],"rb").read() 382 | states = parse_ltrace(data) 383 | min_addr = states[-1].get_min_addr() 384 | max_addr = states[-1].get_max_addr() 385 | 386 | nb_state = len(states) 387 | svg_height=100*nb_state + 5*nb_state + 110 388 | svg=svg_header(svg_height) 389 | svg+=svg_style() 390 | svg+=svg_script() 391 | for pos,state in enumerate(states): 392 | svg+=state_to_svg(pos,state,min_addr,max_addr) 393 | 394 | svg+=svg_footer() 395 | out=open(sys.argv[2],"wb") 396 | out.write(svg) 397 | out.close() 398 | 399 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeapView 2 | Tool to view heap chunks and memory writes (using pintool) 3 | , strongly inspired by [Villoc](https://github.com/wapiflapi/villoc). 4 | 5 | ``` 6 | /opt/pin/pin -t pintool/obj-intel64/pintool.so -- ./vuln 7 | HeapView.py trace vuln.svg 8 | ``` 9 | 10 | 11 | -------------------------------------------------------------------------------- /pintool/makefile: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # DO NOT EDIT THIS FILE! 4 | # 5 | ############################################################## 6 | 7 | # If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root. 8 | ifdef PIN_ROOT 9 | CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config 10 | else 11 | CONFIG_ROOT := ../Config 12 | endif 13 | include $(CONFIG_ROOT)/makefile.config 14 | include makefile.rules 15 | include $(TOOLS_ROOT)/Config/makefile.default.rules 16 | 17 | TOOL_INCLUDES += ./src/api/c++/ 18 | 19 | ############################################################## 20 | # 21 | # DO NOT EDIT THIS FILE! 22 | # 23 | ############################################################## 24 | -------------------------------------------------------------------------------- /pintool/makefile.rules: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # This file includes all the test targets as well as all the 4 | # non-default build rules and test recipes. 5 | # 6 | ############################################################## 7 | 8 | 9 | ############################################################## 10 | # 11 | # Test targets 12 | # 13 | ############################################################## 14 | 15 | ###### Place all generic definitions here ###### 16 | 17 | # This defines tests which run tools of the same name. This is simply for convenience to avoid 18 | # defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). 19 | # Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. 20 | TEST_TOOL_ROOTS := pintool 21 | 22 | # This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. 23 | TEST_ROOTS := 24 | 25 | # This defines a list of tests that should run in the "short" sanity. Tests in this list must also 26 | # appear either in the TEST_TOOL_ROOTS or the TEST_ROOTS list. 27 | # If the entire directory should be tested in sanity, assign TEST_TOOL_ROOTS and TEST_ROOTS to the 28 | # SANITY_SUBSET variable in the tests section below (see example in makefile.rules.tmpl). 29 | SANITY_SUBSET := 30 | 31 | # This defines the tools which will be run during the the tests, and were not already defined in 32 | # TEST_TOOL_ROOTS. 33 | TOOL_ROOTS := 34 | 35 | # This defines the static analysis tools which will be run during the the tests. They should not 36 | # be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in 37 | # TEST_ROOTS. 38 | # Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. 39 | # This library provides a subset of the Pin APIs which allows the tool to perform static analysis 40 | # of an application or dll. Pin itself is not used when this tool runs. 41 | SA_TOOL_ROOTS := 42 | 43 | # This defines all the applications that will be run during the tests. 44 | APP_ROOTS := 45 | 46 | # This defines any additional object files that need to be compiled. 47 | OBJECT_ROOTS := 48 | 49 | # This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. 50 | DLL_ROOTS := 51 | 52 | # This defines any static libraries (archives), that need to be built. 53 | LIB_ROOTS := 54 | 55 | 56 | ############################################################## 57 | # 58 | # Test recipes 59 | # 60 | ############################################################## 61 | 62 | # This section contains recipes for tests other than the default. 63 | # See makefile.default.rules for the default test rules. 64 | # All tests in this section should adhere to the naming convention: .test 65 | 66 | 67 | ############################################################## 68 | # 69 | # Build rules 70 | # 71 | ############################################################## 72 | 73 | # This section contains the build rules for all binaries that have special build rules. 74 | # See makefile.default.rules for the default build rules. 75 | -------------------------------------------------------------------------------- /pintool/obj-ia32/pintool.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymorf/HeapView/231e4a614f660edf7c51f49e0a0fc2e31a0396c4/pintool/obj-ia32/pintool.so -------------------------------------------------------------------------------- /pintool/obj-intel64/pintool.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymorf/HeapView/231e4a614f660edf7c51f49e0a0fc2e31a0396c4/pintool/obj-intel64/pintool.so -------------------------------------------------------------------------------- /pintool/pintool.cpp: -------------------------------------------------------------------------------- 1 | #include "pin.H" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* ===================================================================== */ 11 | /* Names of malloc and free */ 12 | /* ===================================================================== */ 13 | #if defined(TARGET_MAC) 14 | #define CALLOC "_calloc" 15 | #define MALLOC "_malloc" 16 | #define FREE "_free" 17 | #define REALLOC "_realloc" 18 | #else 19 | #define CALLOC "calloc" 20 | #define MALLOC "malloc" 21 | #define FREE "free" 22 | #define REALLOC "realloc" 23 | #endif 24 | 25 | 26 | #define CHUNK_SIZE_ALIGN 0x20 27 | using namespace std; 28 | 29 | 30 | /* ===================================================================== */ 31 | /* Global Variables */ 32 | /* ===================================================================== */ 33 | 34 | class Args; 35 | class Bounds; 36 | 37 | ofstream TraceFile; 38 | 39 | Args* args = NULL; 40 | Bounds* bounds = NULL; 41 | 42 | string ADDRINTToHexString(ADDRINT a) 43 | { 44 | ostringstream temp; 45 | temp << "0x" << hex <size = size; 110 | args->retaddr = return_ip; 111 | } 112 | 113 | VOID AfterMalloc(ADDRINT ret) 114 | { 115 | if (is_ld_linux(args->retaddr)) { 116 | return; 117 | } 118 | ADDRINT real_alloc_size = args->size; 119 | if (real_alloc_size % CHUNK_SIZE_ALIGN != 0) { 120 | real_alloc_size += CHUNK_SIZE_ALIGN - (real_alloc_size % CHUNK_SIZE_ALIGN); 121 | } 122 | if(bounds->start > ret) { 123 | bounds->start = ret - 8; 124 | } 125 | if(bounds->end < (ret + real_alloc_size)) { 126 | bounds->end = (ret + real_alloc_size); 127 | } 128 | TraceFile << "malloc(" << args->size << ") = " << ADDRINTToHexString(ret) << endl; 129 | } 130 | 131 | VOID Free(ADDRINT addr, ADDRINT return_ip) 132 | { 133 | if (is_ld_linux(return_ip)) { 134 | return; 135 | } 136 | string formatted_addr = ""; 137 | if(addr == 0){ 138 | formatted_addr = "0"; 139 | } else { 140 | formatted_addr = ADDRINTToHexString(addr); 141 | } 142 | TraceFile << "free(" + formatted_addr +") = " << endl; 143 | } 144 | 145 | VOID BeforeCalloc(ADDRINT num, ADDRINT size, ADDRINT return_ip) 146 | { 147 | args->num = num; 148 | args->size = size; 149 | args->retaddr = return_ip; 150 | } 151 | 152 | VOID AfterCalloc(ADDRINT ret) 153 | { 154 | if (is_ld_linux(args->retaddr)) { 155 | return; 156 | } 157 | ADDRINT real_alloc_size = args->size * args->num; 158 | if (real_alloc_size % CHUNK_SIZE_ALIGN != 0) { 159 | real_alloc_size += CHUNK_SIZE_ALIGN - (real_alloc_size % CHUNK_SIZE_ALIGN); 160 | } 161 | if(bounds->start > ret) { 162 | bounds->start = ret - 8; 163 | } 164 | if(bounds->end < (ret + real_alloc_size)) { 165 | bounds->end = (ret + real_alloc_size); 166 | } 167 | TraceFile << "calloc(" << args->num << "," << ADDRINTToHexString(args->size) +") = " + ADDRINTToHexString(ret) << endl; 168 | } 169 | 170 | VOID BeforeRealloc(ADDRINT addr, ADDRINT size, ADDRINT return_ip) 171 | { 172 | args->addr = addr; 173 | args->size = size; 174 | args->retaddr = return_ip; 175 | } 176 | 177 | VOID AfterRealloc(ADDRINT ret) 178 | { 179 | if (is_ld_linux(args->retaddr)) { 180 | return; 181 | } 182 | ADDRINT real_alloc_size = args->size; 183 | if (real_alloc_size % CHUNK_SIZE_ALIGN != 0) { 184 | real_alloc_size += CHUNK_SIZE_ALIGN - (real_alloc_size % CHUNK_SIZE_ALIGN); 185 | } 186 | if(bounds->start > ret) { 187 | bounds->start = ret - 8; 188 | } 189 | if(bounds->end < (ret + real_alloc_size)) { 190 | bounds->end = (ret + real_alloc_size); 191 | } 192 | cout << "realloc(" << ADDRINTToHexString(args->addr) << "," << args->size << ") = " << ADDRINTToHexString(ret) << endl; 193 | TraceFile << "realloc(" << ADDRINTToHexString(args->addr) << "," << args->size << ") = " << ADDRINTToHexString(ret) << endl; 194 | } 195 | 196 | 197 | 198 | 199 | /* ===================================================================== */ 200 | /* Instrumentation routines */ 201 | /* ===================================================================== */ 202 | 203 | VOID Image(IMG img, VOID *v) 204 | { 205 | // Instrument the malloc() and free() functions. Print the input argument 206 | // of each malloc() or free(), and the return value of malloc(). 207 | // 208 | // Find the malloc() function. 209 | RTN mallocRtn = RTN_FindByName(img, MALLOC); 210 | if (RTN_Valid(mallocRtn)) 211 | { 212 | RTN_Open(mallocRtn); 213 | 214 | // Instrument malloc() to print the input argument value and the return value. 215 | RTN_InsertCall(mallocRtn, IPOINT_BEFORE, (AFUNPTR)BeforeMalloc, 216 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_RETURN_IP, 217 | IARG_END); 218 | RTN_InsertCall(mallocRtn, IPOINT_AFTER, (AFUNPTR)AfterMalloc, 219 | IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); 220 | 221 | RTN_Close(mallocRtn); 222 | } 223 | 224 | // Find the free() function. 225 | RTN freeRtn = RTN_FindByName(img, FREE); 226 | if (RTN_Valid(freeRtn)) 227 | { 228 | RTN_Open(freeRtn); 229 | // Instrument free() to print the input argument value. 230 | RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)Free, 231 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_RETURN_IP, 232 | IARG_END); 233 | 234 | RTN_Close(freeRtn); 235 | } 236 | 237 | //Find the calloc() function 238 | RTN callocRtn = RTN_FindByName(img, CALLOC); 239 | if (RTN_Valid(callocRtn)) 240 | { 241 | RTN_Open(callocRtn); 242 | 243 | // Instrument callocRtn to print the input argument value and the return value. 244 | RTN_InsertCall(callocRtn, IPOINT_BEFORE, (AFUNPTR)BeforeCalloc, 245 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 246 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, 247 | IARG_RETURN_IP, 248 | IARG_END); 249 | RTN_InsertCall(callocRtn, IPOINT_AFTER, (AFUNPTR)AfterCalloc, 250 | IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); 251 | 252 | RTN_Close(callocRtn); 253 | } 254 | //Find the realloc() function 255 | RTN reallocRtn = RTN_FindByName(img, REALLOC); 256 | if (RTN_Valid(reallocRtn)) 257 | { 258 | RTN_Open(reallocRtn); 259 | 260 | // Instrument malloc() to print the input argument value and the return value. 261 | RTN_InsertCall(reallocRtn, IPOINT_BEFORE, (AFUNPTR)BeforeRealloc, 262 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 263 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, 264 | IARG_RETURN_IP, 265 | IARG_END); 266 | RTN_InsertCall(reallocRtn, IPOINT_AFTER, (AFUNPTR)AfterRealloc, 267 | IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); 268 | 269 | RTN_Close(reallocRtn); 270 | } 271 | } 272 | 273 | VOID WriteMem(ADDRINT insAddr, string insDis, ADDRINT memOp, UINT32 size) { 274 | if (memOp >= (bounds->start - CHUNK_SIZE_ALIGN) && memOp <= (bounds->end + CHUNK_SIZE_ALIGN) ) { 275 | PIN_LockClient(); 276 | RTN rtn = RTN_FindByAddress(insAddr); 277 | PIN_UnlockClient(); 278 | if (RTN_Valid(rtn)) { 279 | ADDRINT func_addr = RTN_Address(rtn); 280 | long delta = (long)insAddr - (long)func_addr; 281 | TraceFile << "memory_write(" << RTN_Name(rtn) << "+" << ADDRINTToHexString(delta) << "," << ADDRINTToHexString(memOp) << "," << size << ")" << endl; 282 | }else{ 283 | TraceFile << "memory_write(unknown_" << ADDRINTToHexString(insAddr) << "," << ADDRINTToHexString(memOp) << "," << size << ")" << endl; 284 | } 285 | } 286 | return; 287 | } 288 | 289 | VOID CheckMemoryWrites(INS ins, VOID *v) 290 | { 291 | PIN_LockClient(); 292 | IMG img = IMG_FindByAddress(INS_Address(ins)); 293 | PIN_UnlockClient(); 294 | if (IMG_Valid(img)){ 295 | for (unsigned int i = 0; i < INS_MemoryOperandCount(ins); i++) { 296 | if (INS_MemoryOperandIsWritten(ins, i)){ 297 | INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)WriteMem, 298 | IARG_ADDRINT, INS_Address(ins), 299 | IARG_PTR, new string(INS_Disassemble(ins)), 300 | IARG_MEMORYOP_EA, i, IARG_MEMORYWRITE_SIZE, 301 | IARG_END); 302 | } 303 | } 304 | } 305 | } 306 | 307 | /* ===================================================================== */ 308 | 309 | VOID Fini(INT32 code, VOID *v) 310 | { 311 | TraceFile.close(); 312 | } 313 | 314 | /* ===================================================================== */ 315 | 316 | /* ===================================================================== */ 317 | /* Commandline Switches */ 318 | /* ===================================================================== */ 319 | 320 | KNOB KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", 321 | "o", "trace", "specify trace file name"); 322 | /* ===================================================================== */ 323 | /* Print Help Message */ 324 | /* ===================================================================== */ 325 | 326 | INT32 Usage() 327 | { 328 | cerr << "This tool produces a visualisation is memory allocator activity." << endl; 329 | cerr << endl << KNOB_BASE::StringKnobSummary() << endl; 330 | return -1; 331 | } 332 | 333 | /* ===================================================================== */ 334 | /* Main */ 335 | /* ===================================================================== */ 336 | 337 | int main(int argc, char *argv[]) 338 | { 339 | // Initialize pin & symbol manager 340 | PIN_InitSymbols(); 341 | if( PIN_Init(argc,argv) ) 342 | { 343 | return Usage(); 344 | } 345 | TraceFile.open(KnobOutputFile.Value().c_str()); 346 | // Write to a file since TraceFile and cerr maybe closed by the application 347 | Args* initial = new Args(); 348 | args = initial; 349 | bounds = new Bounds(); 350 | // Register Image to be called to instrument functions. 351 | 352 | IMG_AddInstrumentFunction(Image, 0); 353 | INS_AddInstrumentFunction(CheckMemoryWrites, 0); 354 | PIN_AddFiniFunction(Fini, 0); 355 | 356 | // Never returns 357 | PIN_StartProgram(); 358 | 359 | return 0; 360 | } 361 | 362 | /* ===================================================================== */ 363 | /* eof */ 364 | /* ===================================================================== */ 365 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | PIN=/opt/pin/pin 2 | 3 | test: 4 | for test in 1 2 3; do \ 5 | gcc test$$test.c -o test$$test; \ 6 | ${PIN} -ifeellucky -t ../pintool/obj-intel64/pintool.so -o test$$test.log -- ./test$$test; \ 7 | python ../HeapView.py test$$test.log test$$test.svg; \ 8 | done 9 | 10 | all: test 11 | -------------------------------------------------------------------------------- /tests/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void fn_write_loop(char *a,int max, int val) { 5 | for(int i=0; i<=max; i++) 6 | a[i]=val; 7 | } 8 | void fn_overflow_loop(char *a,int max, int val) { 9 | for(int i=0; i<=max; i++) 10 | a[i]=val; 11 | } 12 | 13 | void fn_overflow_single(char *a, int pos, int val) { 14 | a[pos]=val; 15 | } 16 | 17 | int main() { 18 | char *a = malloc(0x100-8); 19 | char *b = malloc(0x100-8); 20 | char *c = malloc(0x80-8); 21 | free(b); 22 | fn_write_loop(a, 0x60, 0x60); 23 | fn_overflow_loop(a, 0x100-7, 0x81); 24 | fn_overflow_single(a,0x100-8,0x81); 25 | char *e = malloc(0x100 + 0x80-8); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/test1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 73 | 79 | malloc(248) = 0x223a010 80 | 86 | 92 | malloc(248) = 0x223a110 93 | 99 | 105 | 111 | malloc(120) = 0x223a210 112 | 118 | 124 | 130 | 136 | free(0x223a110) = <void> 137 | 143 | 150 | 156 | 162 | <fn_write_loop+0x27> 163 | 169 | 176 | 182 | 188 | 194 | <fn_overflow_loop+0x27> 195 | 201 | 208 | 214 | 215 | 216 | 217 | overflow in next_chunk (2 bytes) 218 | 219 | 225 | 231 | <fn_overflow_single+0x1e> 232 | 238 | 245 | 251 | 252 | 253 | 254 | overflow in next_chunk (1 byte) 255 | 256 | 262 | 268 | malloc(376) = 0x223a110 269 | 275 | 282 | 288 | 294 | 295 | AB 296 | -------------------------------------------------------------------------------- /tests/test2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main() { 4 | char *a = malloc(0x100-8); 5 | char *b = malloc(0x100-8); 6 | char *c = malloc(0x80-8); 7 | char *d = malloc(0x120); 8 | memset(d,255,0x120); 9 | memset(b,22,0x100-8); 10 | memset(c,33,0x80-8); 11 | free(b); 12 | /* overflow in memcpy */ 13 | memcpy(a+0x40,d,0xC0-8+1); 14 | char *e = malloc(0x100 + 0x80-8); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/test2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 73 | 79 | malloc(248) = 0xc02010 80 | 86 | 92 | malloc(248) = 0xc02110 93 | 99 | 105 | 111 | malloc(120) = 0xc02210 112 | 118 | 124 | 130 | 136 | malloc(288) = 0xc02290 137 | 143 | 149 | 155 | 161 | 167 | <__memset_sse2_unaligned_erms+0x58> 168 | 174 | 180 | 186 | 192 | 198 | 204 | <__memset_sse2_unaligned_erms+0x60> 205 | 211 | 217 | 223 | 229 | 235 | 241 | <__memset_sse2_unaligned_erms+0x66> 242 | 248 | 254 | 260 | 266 | 272 | 278 | <__memset_sse2_unaligned_erms+0x6b> 279 | 285 | 291 | 297 | 303 | 309 | 315 | <__memset_sse2_unaligned_erms+0x71> 316 | 322 | 328 | 334 | 340 | 346 | 352 | <__memset_sse2_unaligned_erms+0x76> 353 | 359 | 365 | 371 | 377 | 383 | 389 | <__memset_sse2_unaligned_erms+0x7c> 390 | 396 | 402 | 408 | 414 | 420 | 426 | <__memset_sse2_unaligned_erms+0x81> 427 | 433 | 439 | 445 | 451 | 457 | 463 | <__memset_sse2_unaligned_erms+0x93> 464 | 470 | 476 | 482 | 488 | 494 | 500 | <__memset_sse2_unaligned_erms+0x58> 501 | 507 | 513 | 519 | 525 | 531 | 537 | <__memset_sse2_unaligned_erms+0x60> 538 | 544 | 550 | 556 | 562 | 568 | 574 | <__memset_sse2_unaligned_erms+0x66> 575 | 581 | 587 | 593 | 599 | 605 | 611 | <__memset_sse2_unaligned_erms+0x6b> 612 | 618 | 624 | 630 | 636 | 642 | 648 | <__memset_sse2_unaligned_erms+0x71> 649 | 655 | 661 | 667 | 673 | 679 | 685 | <__memset_sse2_unaligned_erms+0x76> 686 | 692 | 698 | 704 | 710 | 716 | 722 | <__memset_sse2_unaligned_erms+0x7c> 723 | 729 | 735 | 741 | 747 | 753 | 759 | <__memset_sse2_unaligned_erms+0x81> 760 | 766 | 772 | 778 | 784 | 790 | 796 | <__memset_sse2_unaligned_erms+0x93> 797 | 803 | 809 | 815 | 821 | 827 | 833 | <__memset_sse2_unaligned_erms+0x58> 834 | 840 | 846 | 852 | 858 | 864 | 870 | <__memset_sse2_unaligned_erms+0x60> 871 | 877 | 883 | 889 | 895 | 901 | 907 | <__memset_sse2_unaligned_erms+0x66> 908 | 914 | 920 | 926 | 932 | 938 | 944 | <__memset_sse2_unaligned_erms+0x6b> 945 | 951 | 957 | 963 | 969 | 975 | 981 | <__memset_sse2_unaligned_erms+0x71> 982 | 988 | 994 | 1000 | 1006 | 1012 | 1018 | <__memset_sse2_unaligned_erms+0x76> 1019 | 1025 | 1031 | 1037 | 1043 | 1049 | 1055 | <__memset_sse2_unaligned_erms+0x7c> 1056 | 1062 | 1068 | 1074 | 1080 | 1086 | 1092 | <__memset_sse2_unaligned_erms+0x81> 1093 | 1099 | 1105 | 1111 | 1117 | 1123 | 1129 | free(0xc02110) = <void> 1130 | 1136 | 1143 | 1149 | 1155 | 1161 | <__memset_sse2_unaligned_erms+0x93> 1162 | 1168 | 1175 | 1181 | 1187 | 1193 | 1199 | <__memcpy_sse2_unaligned_erms+0x176> 1200 | 1206 | 1213 | 1219 | 1225 | 1231 | 1237 | <__memcpy_sse2_unaligned_erms+0x18f> 1238 | 1244 | 1251 | 1257 | 1263 | 1264 | 1265 | 1266 | overflow in next_chunk (1 byte) 1267 | 1268 | 1274 | 1280 | <__memcpy_sse2_unaligned_erms+0x192> 1281 | 1287 | 1294 | 1300 | 1306 | 1312 | 1318 | <__memcpy_sse2_unaligned_erms+0x196> 1319 | 1325 | 1332 | 1338 | 1344 | 1350 | 1356 | <__memcpy_sse2_unaligned_erms+0x19a> 1357 | 1363 | 1370 | 1376 | 1382 | 1388 | 1394 | <__memcpy_sse2_unaligned_erms+0x19f> 1395 | 1401 | 1408 | 1414 | 1420 | 1426 | 1432 | malloc(376) = 0xc02110 1433 | 1439 | 1446 | 1452 | 1458 | 1464 | 1465 | AB 1466 | -------------------------------------------------------------------------------- /tests/test3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void fn_overflow_loop(char *a,int max, int val) { 5 | for(int i=0; i<=max; i++) 6 | a[i]=val; 7 | } 8 | 9 | int main() { 10 | char *a = malloc(0x100-8); 11 | char *b = malloc(0x100-8); 12 | char *c = malloc(0x80-8); 13 | free(b); 14 | fn_overflow_loop(a, 0x100-7, 0x81); 15 | char *e = malloc(0x100 + 0x80-8); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/test3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 73 | 79 | malloc(248) = 0x20a1010 80 | 86 | 92 | malloc(248) = 0x20a1110 93 | 99 | 105 | 111 | malloc(120) = 0x20a1210 112 | 118 | 124 | 130 | 136 | free(0x20a1110) = <void> 137 | 143 | 150 | 156 | 162 | <fn_overflow_loop+0x27> 163 | 169 | 176 | 182 | 183 | 184 | 185 | overflow in next_chunk (2 bytes) 186 | 187 | 193 | 199 | malloc(376) = 0x20a1110 200 | 206 | 213 | 219 | 225 | 226 | AB 227 | --------------------------------------------------------------------------------