更改局部堆栈变量值

发布于 2024-11-01 18:51:51 字数 45 浏览 4 评论 0原文

使用 Windbg/SOS,可以更改堆栈上局部变量的值吗?如果是这样怎么办?

Using Windbg/SOS, it possible to change value of a local varible on stack? If so how?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜中书 2024-11-08 18:51:51

简短的回答是:这取决于。

默认情况下,本地值类型存储在堆栈中,但由于优化,它们通常仅根据需要存储在寄存器中。引用类型存储在堆上,并引用堆栈上(或寄存器中)的实例。

我假设您正在寻求更改本地值类型。让我们看一个简单的例子。

[MethodImpl(MethodImplOptions.NoInlining)] // avoid inlining of short method
public static void Method(int x) {
    Console.WriteLine("The answer is {0}", x + x);
}

假设我们在 Method 上设置断点并运行直到遇到断点,堆栈看起来像这样:

0:000> !clrstack -a
OS Thread Id: 0x1abc (0)
Child SP IP       Call Site
0035f290 003600e0 TestBench2010.Program.Method(Int32)*** WARNING: Unable to verify checksum for C:\workspaces\TestBench2010\TestBench2010\bin\Release\TestBench2010.exe
 [C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 17]
    PARAMETERS:
        x (<CLR reg>) = 0x00000002

0035f294 003600a2 TestBench2010.Program.Main(System.String[])    [C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 24]
    PARAMETERS:
        args = <no data>

0035f4c0 636221bb [GCFrame: 0035f4c0] 

注意,本地 x 被列为 ,但它并没有告诉我们我们注册的。我们可以查看寄存器并找到值为 2 的寄存器,但也可能不止一个。相反,让我们看看该方法的 JIT 编译代码。

0:000> !u 001c37f0     
Normal JIT generated code
TestBench2010.Program.Method(Int32)
Begin 003600e0, size 32

C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 17:
003600e0 55              push    ebp
003600e1 8bec            mov     ebp,esp
003600e3 56              push    esi
003600e4 8bf1            mov     esi,ecx
*** WARNING: Unable to verify checksum for    C:\windows\assembly\NativeImages_v4.0.30319_32\mscorlib\658bbc023e2f4f4e802be9483e988373\mscorlib.ni.dll
003600e6 b9302be004      mov     ecx,offset mscorlib_ni+0x322b30 (04e02b30) (MT: System.Int32)
003600eb e8301fe5ff      call    001b2020 (JitHelp: CORINFO_HELP_NEWSFAST)
003600f0 8bd0            mov     edx,eax
003600f2 03f6            add     esi,esi    <==== This is x + x
003600f4 897204          mov     dword ptr [edx+4],esi
003600f7 8bf2            mov     esi,edx
003600f9 e882709d04      call    mscorlib_ni+0x257180 (04d37180)(System.Console.get_Out(), mdToken: 060008cd)
003600fe 56              push    esi
003600ff 8bc8            mov     ecx,eax
00360101 8b1534204c03    mov     edx,dword ptr ds:[34C2034h] ("The answer is {0}")
00360107 8b01            mov     eax,dword ptr [ecx]
00360109 8b403c          mov     eax,dword ptr [eax+3Ch]
0036010c ff5018          call    dword ptr [eax+18h]

C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 18:
0036010f 5e              pop     esi
00360110 5d              pop     ebp
00360111 c3              ret

查看代码,我们发现唯一的add指令使用了esi寄存器,因此我们的值在计算之前存储在这里。不幸的是,此时esi 没有保存正确的值,但向后看我们发现mov esi,ecx。即该值最初存储在ecx中。

要更改 ecx 的值,请使用 r 命令。例如,要将值设置为 0x15,请执行以下操作:

0:000> r ecx=15

该方法的输出现在为:

答案是42

请记住,上面的示例只是许多可能的情况之一。根据调试/发布版本以及 32/64 位,本地变量的处理方式有所不同。此外,对于复杂的方法,跟踪值的确切位置可能会有点困难。

要更改实例的状态,您必须在堆栈上找到引用(例如使用 !clrstack!dso)。找到后,您可以使用偏移量来查找保存数据的内存,并使用 e* 命令根据需要更改值。如果您也想要一个例子,请告诉我。

The short answer is: It depends.

Per default local value types are stored on the stack but due to optimization they will often be stored only in registers as needed. Reference types are stored on the heap, with a reference to the instance on the stack (or in a register).

I am going to assume that you're looking to change a local value type. Let's look at a simple example.

[MethodImpl(MethodImplOptions.NoInlining)] // avoid inlining of short method
public static void Method(int x) {
    Console.WriteLine("The answer is {0}", x + x);
}

Assuming we set a breakpoint on Method and run until the breakpoint is hit, the stack looks like this:

0:000> !clrstack -a
OS Thread Id: 0x1abc (0)
Child SP IP       Call Site
0035f290 003600e0 TestBench2010.Program.Method(Int32)*** WARNING: Unable to verify checksum for C:\workspaces\TestBench2010\TestBench2010\bin\Release\TestBench2010.exe
 [C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 17]
    PARAMETERS:
        x (<CLR reg>) = 0x00000002

0035f294 003600a2 TestBench2010.Program.Main(System.String[])    [C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 24]
    PARAMETERS:
        args = <no data>

0035f4c0 636221bb [GCFrame: 0035f4c0] 

Notice that the local x is listed as , but it doesn't tell us which register. We could look at the registers and find the one with the value 2, but there could be more than one. Instead let's look at the JIT compiled code for the method.

0:000> !u 001c37f0     
Normal JIT generated code
TestBench2010.Program.Method(Int32)
Begin 003600e0, size 32

C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 17:
003600e0 55              push    ebp
003600e1 8bec            mov     ebp,esp
003600e3 56              push    esi
003600e4 8bf1            mov     esi,ecx
*** WARNING: Unable to verify checksum for    C:\windows\assembly\NativeImages_v4.0.30319_32\mscorlib\658bbc023e2f4f4e802be9483e988373\mscorlib.ni.dll
003600e6 b9302be004      mov     ecx,offset mscorlib_ni+0x322b30 (04e02b30) (MT: System.Int32)
003600eb e8301fe5ff      call    001b2020 (JitHelp: CORINFO_HELP_NEWSFAST)
003600f0 8bd0            mov     edx,eax
003600f2 03f6            add     esi,esi    <==== This is x + x
003600f4 897204          mov     dword ptr [edx+4],esi
003600f7 8bf2            mov     esi,edx
003600f9 e882709d04      call    mscorlib_ni+0x257180 (04d37180)(System.Console.get_Out(), mdToken: 060008cd)
003600fe 56              push    esi
003600ff 8bc8            mov     ecx,eax
00360101 8b1534204c03    mov     edx,dword ptr ds:[34C2034h] ("The answer is {0}")
00360107 8b01            mov     eax,dword ptr [ecx]
00360109 8b403c          mov     eax,dword ptr [eax+3Ch]
0036010c ff5018          call    dword ptr [eax+18h]

C:\workspaces\TestBench2010\TestBench2010\Program.cs @ 18:
0036010f 5e              pop     esi
00360110 5d              pop     ebp
00360111 c3              ret

Looking at the code, we see that the only add instruction uses the esi register, so our value is stored here prior to the calculation. Unfortunately, esi doesn't hold the correct value at this point, but looking backwards we find mov esi,ecx. I.e. the value is initially stored in ecx.

To change the value of ecx use the r command. E.g. to set the value to 0x15 do the following:

0:000> r ecx=15

The output of the method is now:

The answer is 42

Please keep in mind that the example above is only one of many possible scenarios. Locals are handled differently depending on debug/release build as well as 32/64 bit. Also, for complex methods it may be a bit harder tracking the exact location of the value.

To change the state of an instance, you have to locate the reference on the stack (e.g. using !clrstack or !dso). Once located you can use the offsets to find the memory, that holds the data and use the e* commands to change the values as needed. Let me know if you want an example for that as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文