#include "stdafx.h"

#include <tchar.h>



#ifdef _UNICODE

#define _ttol _wtol

#else

#define _ttol atol

#endif 



void Usage()

{

#ifdef _UNICODE

    wprintf(L"[Usage]: nativedebug.exe <digital numbers>\n");



#else

    printf("[Usage]: nativedebug.exe <digital numbers>\n");

#endif





int _tmain(int argc, _TCHAR* argv[])

{

         int result = 0;

         if ( argc != 2 )

         {

                   Usage();

                   return -1;

         } 



         result = _ttol(argv[1]);



#ifdef _UNICODE

         wprintf(L"%s * %s = %d\n", argv[1], argv[1], result * result);

         wprintf(L"Press any key to exit ...\n");

         _getwch();

#else

         printf("%s * %s = %d\n", result * result);

         printf("Press any key to exit ...\n");

         _getch();

#endif

         return 0;

}

编译,用Windbg分析。

1. 设置断点,打开源文件,直接在result = _ttol(argv[1]);按F9 

或者设置_wtol和atol的断点:

因为代码中有:

#ifdef _UNICODE

#   define _ttol       _wtol

#else

#   define _ttol       atol

#endif

而宏是在编译期间就被编译器扩展,并不会被加到符号文件中去,因此如果你试图使用bp命令在_ttol入口设置断点的话,是会失败的。因此你可以使用类似下面的通配符来查找正确的函数名:

x MSVCR90D!*tol×

然后用bm *tol*给所有含有tol的函数都设置断点;

然后用bl查看断点列表,用bc 2-6 清除,用bd 2-6禁用 第二个到6个断点

2. lm查看loaded Modules

lm

start    end        module name

01330000 0134b000   MyApp    C (private pdb symbols)  E:\ProLab\WindbgFirst\Debug\MyApp.pdb

59bc0000 59ce4000   MSVCR90D   (deferred)             

75100000 75200000   kernel32   (deferred)             

76750000 76796000   KERNELBASE   (deferred)             

77500000 77680000   ntdll      (pdb symbols)          c:\websymbols\wntdll.pdb\ACE318E6A2F44F23A6CC5628F10A7DDC2\wntdll.pdb

我们发现MSVCR90D的pdb并没有被加载,是因为程序还没运行到,

3. 按F11

没有跳到源代码!

运行:src.path C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src

4. lm

 lm

start    end        module name

01330000 0134b000   MyApp    C (private pdb symbols)  E:\ProLab\WindbgFirst\Debug\MyApp.pdb

598d0000 599f4000   MSVCR90D   (private pdb symbols)  c:\websymbols\msvcr90d.i386.pdb\EBEA784C96244F1E8F8D35E0391C898D1\msvcr90d.i386.pdb

75100000 75200000   kernel32   (deferred)             

76750000 76796000   KERNELBASE   (deferred)             

77500000 77680000   ntdll      (pdb symbols)          c:\websymbols\wntdll.pdb\ACE318E6A2F44F23A6CC5628F10A7DDC2\wntdll.pdb

5. 查看堆栈 k / kp / kP / kn

k比较简单,kp能看到各个函数的输入参数,kP比kp看起来更舒服,kn(callstack with index number)

在windbg中,在堆栈中切换到不同的函数,需要用到.frame命令(注意前面的点号)。

kn

 # ChildEBP RetAddr  

00 0021fa0c 01341464 MSVCR90D!_wtol [f:\dd\vctools\crt_bld\self_x86\crt\src\atox.c @ 55]

01 0021faf0 01341a88 MyApp!wmain+0x44 [e:\prolab\windbgfirst\windbgfirst\windbgfirst.cpp @ 29]

02 0021fb40 013418cf MyApp!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]

03 0021fb48 75113677 MyApp!wmainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]

04 0021fb54 77539d42 kernel32!BaseThreadInitThunk+0xe

05 0021fb94 77539d15 ntdll!__RtlUserThreadStart+0x70

06 0021fbac 00000000 ntdll!_RtlUserThreadStart+0x1b

0:000> .frame 01

01 0021faf0 01341a88 MyApp!wmain+0x44 [e:\prolab\windbgfirst\windbgfirst\windbgfirst.cpp @ 29]

0:000> dv

           argc = 0n2

           argv = 0x000d1b90

         result = 0n0

6. dv(display variables)

切换了函数以后,下一步就是查看变量的值,使用dv命令来查看变量信息,这个命令相当于Visual Studio里面的局部变量(locals)窗口。

7. kM

.frame的方式比较复杂,因此windbg提供了一个快捷命令,kM(callstack
with markup)。这个命令提供了一个类似html网页超链接的形式,供程序员在堆栈中快速切换函数并且显示变量值,

 kM

 # ChildEBP RetAddr  

00 0021fa0c 01341464 MSVCR90D!_wtol+0x5

01 0021faf0 01341a88 MyApp!wmain+0x44

02 0021fb40 013418cf MyApp!__tmainCRTStartup+0x1a8

03 0021fb48 75113677 MyApp!wmainCRTStartup+0xf

04 0021fb54 77539d42 kernel32!BaseThreadInitThunk+0xe

05 0021fb94 77539d15 ntdll!__RtlUserThreadStart+0x70

06 0021fbac 00000000 ntdll!_RtlUserThreadStart+0x1b

8. dt

对于简单类型,例如整型、浮点型甚至是字符串,windbg可以直接显示出变量的值。但是对于一些复杂类型,例如数组,结构,类呀,那就需要借助另外一个命令dt(display
type)了

dt argv

Local var @ 0x21fafc Type wchar_t**

0x000d1b90 

unsigned short,在C和C++程序中,一般都意味着是wchar_t(宽字符)类型

**表示是一个包含宽字符字符串的数组

9. dd(display by double-word)

下一步就是继续查看argv数组里面的内容,根据前面的dv打印的结果,我们知道argc(也就是说明argv数组元素个数的参数)的值是2。在一台32位机(或者是在64位机器上调试一个32位的程序),使用dd命令参看argv的内存,以四字节的形式显示,如果是64位,使用dq(display
by quad-word)命令以8个字节的形式打印内存。

dd默认是显示32个dword,也就是128个字节的内存内容。

# 将argv传给dd命令的时候, windbg是先将argv转换成保存

# 数组指针的地址(就是0021fafc)—毕竟数组的指针也是需要地方保存的嘛。

# 而高亮显示的00081350才是保存argv数组内容的真实地址

dd argv

0021fafc  000d1b90 000d1c30 164b267e 00000000

0021fb0c  00000000 7efde000 00da7a64 00000000

0021fb1c  00000000 00220000 00000000 0021fb04

0021fb2c  00000069 0021fb84 01341087 175eb66e

0021fb3c  00000000 0021fb48 013418cf 0021fb54

0021fb4c  75113677 7efde000 0021fb94 77539d42

0021fb5c  7efde000 76a0918c 00000000 00000000

0021fb6c  7efde000 00000000 00000000 00000000

继续分析argv数组内容

既然我们已经知道argv数组的大小是2的话,你也可以将这个信息提供给dd命令,告诉它你只需要显示argv指针所指向的内存的两个元素就可以了

10. dd 000d1b90 L2

000d1b90  000d1b9c 000d1be8

11. 执行了这么多命令以后,我们终于可以看到argv[0]和argv[1]的值了,不容易呀!既然已经知道是unicode字符串,使用du(display
unicode)命令就可以显示完整的字符串内容了。

du 000d1b9c 

000d1b9c  "E:\ProLab\WindbgFirst\Debug\MyAp"

000d1bdc  "p.exe"

0:000> du 

000d1be8  "226"

发现连续执行du,windbg会默认去取下一个地址的内容,呈现出来.

但是也有很多情况下,你可能并不知道指定地址里面保存的内容是什么,这个时候,建议你用dc(display
double-word values and ASCII characters)命令查看内存。

dc 000d1b90 

000d1b90  000d1b9c 000d1be8 00000000 003a0045  ............E.:.

000d1ba0  0050005c 006f0072 0061004c 005c0062  \.P.r.o.L.a.b.\.

000d1bb0  00690057 0064006e 00670062 00690046  W.i.n.d.b.g.F.i.

000d1bc0  00730072 005c0074 00650044 00750062  r.s.t.\.D.e.b.u.

000d1bd0  005c0067 0079004d 00700041 002e0070  g.\.M.y.A.p.p...

000d1be0  00780065 00000065 00320032 00000036  e.x.e...2.2.6...

000d1bf0  fdfdfdfd abababab abababab feeefeee  ................

000d1c00  00000000 00000000 31dcc3ca 1800157c  ...........1|...

如果你调试的是一个非unicode程序,即是一个只理解ASCII字符集的程序(也就是所有字符串的类型都是char),那么在查看字符串的时候,使用da(display
ascii)而不是du命令来显示内存

Windbg Step 2 分析程序堆栈实战的更多相关文章

  1. 调试技巧 —— 如何利用windbg + dump + map分析程序异常

    调试技巧 —— 如何利用windbg + dump + map分析程序异常 逗比汪星人2011-09-04上传   调试技巧 —— 如何利用windbg + dump + map分析程序异常 http ...

  2. 微信小程序教学第二章:小程序中级实战教程之预备篇 - 项目结构设计 |基于最新版1.0开发者工具

    iKcamp官网:http://www.ikcamp.com 访问官网更快阅读全部免费分享课程:<iKcamp出品|全网最新|微信小程序|基于最新版1.0开发者工具之初中级培训教程分享>. ...

  3. 通过官方API结合源码,如何分析程序流程

    通过官方API结合源码,如何分析程序流程通过官方API找到我们关注的API的某个方法,然后把整个流程执行起来,然后在idea中,把我们关注的方法打上断点,然后通过Step Out,从内向外一层一层分析 ...

  4. JVM:如何分析线程堆栈

    英文原文:JVM: How to analyze Thread Dump 在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因.在我看来线程堆栈分析技术是Java EE产品 ...

  5. python爬取微信小程序(实战篇)

    python爬取微信小程序(实战篇) 本文链接:https://blog.csdn.net/HeyShHeyou/article/details/90452656 展开 一.背景介绍 近期有需求需要抓 ...

  6. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  7. 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)

    在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...

  8. google perftools分析程序性能

    Google perftools 1.功能简介 它的主要功能就是通过采样的方式,给程序中cpu的使用情况进行“画像”,通过它所输出的结果,我们可以对程序中各个函数(得到函数之间的调用关系)耗时情况一目 ...

  9. [大数据从入门到放弃系列教程]第一个spark分析程序

    [大数据从入门到放弃系列教程]第一个spark分析程序 原文链接:http://www.cnblogs.com/blog5277/p/8580007.html 原文作者:博客园--曲高终和寡 **** ...

随机推荐

  1. vue 学习五 深入了解components(父子组件之间的传值)

    上一章记录了 如何在父组件中向子组件传值,但在实际应用中,往往子组件也要向父组件中传递数据,那么此时我们应该怎么办呢 1.在父组件内使用v-on监听子组件事件,并在子组件中使用$emit传递数据 // ...

  2. CSIC_716_20191206【并发编程理论基础】

    进程:正在执行的一个过程,进程是对正在执行过程的一个抽象.区别于程序, 进程的三种状态:  进程是动态的. 就绪态ready:   进程具备运行状态,等待操作系统分配处理器 运行状态running:进 ...

  3. JavaScript学习笔记之CSS-DOM

    HTML负责结构层,网页的结构层由HTML或者XHTML之类的标记语言负责构建 CSS负责表示层,描述页面内容应该如何呈现. JavaScript负责行为层,负责内容应该如何响应事件这一问题. 能利用 ...

  4. NX二次开发-UFUN遍历函数UF_OBJ_cycle_objs_in_part

    NX11+VS2013 #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include < ...

  5. iOS 获取音频或是视频的时间

    AVURLAsset* audioAsset =[AVURLAssetURLAssetWithURL:audioFileURL options:nil]; CMTime audioDuration = ...

  6. nc命令官方Demo实例

    nc命令可用于发送任务tcp/udp连接和监听. 官方描述的主要功能包括: simple TCP proxies shell-script based HTTP clients and servers ...

  7. (转)HashMap和HashSet的区别

    HashMap和HashSet的区别是Java面试中最常被问到的问题.如果没有涉及到Collection框架以及多线程的面试,可以说是不完整.而Collection框架的问题不涉及到HashSet和H ...

  8. 剑指offer——16二进制中1的个数

    题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 有可能引起死循环解法: 每次判断最右端是不是1[与 & 1即可],是就cnt++,然后右移一位,直到num为0,结束 ...

  9. CSS3教程:Responsive框架常见的Media Queries片段

    CSS3 Media Queries片段在这里主要分成三类:移动端.PC端以及一些常见的响应式框架的Media Queries片段.移动端Media Queries片段iPhone5@media sc ...

  10. HTTPS客户端的java实现

    目录 https客户端 指定ssl算法套 浏览器可以作为客户端向服务器发送Http请求,当需要访问后台或第三方Restful接口时,也可以用java实现客户端直接发get/post请求,获取数据. h ...