在捕获块中使用开关语句的怪异行为

发布于 2025-02-13 04:16:39 字数 6667 浏览 2 评论 0原文

我目前正在研究.NET混淆器,并且在控制流相混淆中,我注意到了一种奇怪的行为。以某种方式,我生成的代码中的堆栈大小不一致。

原始IL:

{
._Statements:

IL_0000: ldstr "MethodD"
IL_0005: call System.Void System.Console::WriteLine(System.String)


Try{
._Statements:

IL_000a: ldc.i4 128736421
IL_000f: stloc.0

IL_0010: ldc.i4 12386123
IL_0015: stloc.1

IL_0016: ldstr "Result D: {0}"
IL_001b: ldloc.0
IL_001c: ldloc.1
IL_001d: add
IL_001e: box System.Int32
IL_0023: call System.String System.String::Format(System.String,System.Object)
IL_0028: call System.Void System.Console::WriteLine(System.String)
IL_002d: leave.s IL_0057
}
Handler{
._Statements:

IL_002f: ldstr "Something horrible happened! MethodD"
IL_0034: call System.Void System.Console::WriteLine(System.String)
IL_0039: call System.Void System.Console::WriteLine(System.Object)

IL_003e: ldc.i4 129387123
IL_0043: ldc.i8 1283769182434
IL_004c: stloc.2
IL_004d: conv.i8
IL_004e: ldloc.2
IL_004f: add
IL_0050: call System.Void System.Console::WriteLine(System.Int64)
IL_0055: leave.s IL_0057
}
._Statements:

IL_0057: call System.Void System.Console::WriteLine()

IL_005c: ret

}

控制流相混淆IL:

/* 0x00000480 727D010070   */ IL_0000: ldstr     "MethodD"
/* 0x00000485 280800000A   */ IL_0005: call      void [mscorlib]System.Console::WriteLine(string)
.try
{
    /* 0x0000048A 16           */ IL_000A: ldc.i4.0
    /* 0x0000048B 0D           */ IL_000B: stloc.3
    /* 0x0000048C 2B00         */ IL_000C: br.s      IL_000E

    /* 0x0000048E 09           */ IL_000E: ldloc.3
    // loop start (head: IL_000F)
    /* 0x0000048F 4503000000000000000B00000016000000 */ IL_000F: switch    (IL_0020, IL_002B, IL_0036)

    /* 0x000004A0 20A55CAC07   */ IL_0020: ldc.i4    128736421
    /* 0x000004A5 0A           */ IL_0025: stloc.0
    /* 0x000004A6 17           */ IL_0026: ldc.i4.1

    /* 0x000004A7 0D           */ IL_0027: stloc.3
    /* 0x000004A8 09           */ IL_0028: ldloc.3
    /* 0x000004A9 2BE4         */ IL_0029: br.s      IL_000F

    /* 0x000004AB 204BFFBC00   */ IL_002B: ldc.i4    12386123
    /* 0x000004B0 0B           */ IL_0030: stloc.1
    /* 0x000004B1 18           */ IL_0031: ldc.i4.2
    /* 0x000004B2 0D           */ IL_0032: stloc.3
    /* 0x000004B3 09           */ IL_0033: ldloc.3
    /* 0x000004B4 2BD9         */ IL_0034: br.s      IL_000F
    // end loop

    /* 0x000004B6 728D010070   */ IL_0036: ldstr     "Result D: {0}"
    /* 0x000004BB 06           */ IL_003B: ldloc.0
    /* 0x000004BC 07           */ IL_003C: ldloc.1
    /* 0x000004BD 58           */ IL_003D: add
    /* 0x000004BE 8C0F000001   */ IL_003E: box       [mscorlib]System.Int32
    /* 0x000004C3 281500000A   */ IL_0043: call      string [mscorlib]System.String::Format(string, object)
    /* 0x000004C8 280800000A   */ IL_0048: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x000004CD DE43         */ IL_004D: leave.s   IL_0092
} // end .try
catch [mscorlib]System.Exception
{
    /* 0x000004CF 16           */ IL_004F: ldc.i4.0
    /* 0x000004D0 1304         */ IL_0050: stloc.s   V_4
    /* 0x000004D2 2B00         */ IL_0052: br.s      IL_0054

    /* 0x000004D4 1104         */ IL_0054: ldloc.s   V_4
    // loop start (head: IL_0056)
    /* 0x000004D6 45020000000000000016000000 */ IL_0056: switch    (IL_0063, IL_0079)

    /* 0x000004E3 72A9010070   */ IL_0063: ldstr     "Something horrible happened! MethodD"
    /* 0x000004E8 280800000A   */ IL_0068: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x000004ED 280D00000A   */ IL_006D: call      void [mscorlib]System.Console::WriteLine(object)
    /* 0x000004F2 17           */ IL_0072: ldc.i4.1
    /* 0x000004F3 1304         */ IL_0073: stloc.s   V_4
    /* 0x000004F5 1104         */ IL_0075: ldloc.s   V_4
    /* 0x000004F7 2BDD         */ IL_0077: br.s      IL_0056
    // end loop

    /* 0x000004F9 20734AB607   */ IL_0079: ldc.i4    129387123
    /* 0x000004FE 21E2289BE62A010000 */ IL_007E: ldc.i8    1283769182434
    /* 0x00000507 0C           */ IL_0087: stloc.2
    /* 0x00000508 6A           */ IL_0088: conv.i8
    /* 0x00000509 08           */ IL_0089: ldloc.2
    /* 0x0000050A 58           */ IL_008A: add
    /* 0x0000050B 281600000A   */ IL_008B: call      void [mscorlib]System.Console::WriteLine(int64)
    /* 0x00000510 DE00         */ IL_0090: leave.s   IL_0092
} // end handler

/* 0x00000512 17           */ IL_0092: ldc.i4.1
/* 0x00000513 1305         */ IL_0093: stloc.s   V_5
/* 0x00000515 2B00         */ IL_0095: br.s      IL_0097

/* 0x00000517 1105         */ IL_0097: ldloc.s   V_5
// loop start (head: IL_0099)
/* 0x00000519 45020000000000000001000000 */ IL_0099: switch    (IL_00A6, IL_00A7)

/* 0x00000526 2A           */ IL_00A6: ret

/* 0x00000527 280400000A   */ IL_00A7: call      void [mscorlib]System.Console::WriteLine()
/* 0x0000052C 16           */ IL_00AC: ldc.i4.0
/* 0x0000052D 1305         */ IL_00AD: stloc.s   V_5
/* 0x0000052F 1105         */ IL_00AF: ldloc.s   V_5
/* 0x00000531 2BE6         */ IL_00B1: br.s      IL_0099
// end loop
/* 0x00000533 2A           */ IL_00B3: ret

我对堆栈行为的分析(对于混淆IL):

IL_004F     Pushes: 1   Pops: 0     Stack usage: 2 (2 because the CLR pushes the Exception object onto the stack before the first instruction in the catch block)
IL_0050     Pushes: 0   Pops: 1     Stack usage: 1
IL_0052     Pushes: 0   Pops: 0     Stack usage: 1
IL_0054     Pushes: 1   Pops: 0     Stack usage: 2
IL_0056     Pushes: 0   Pops: 1     Stack usage: 1
IL_0063     Pushes: 1   Pops: 0     Stack usage: 2
IL_0068     Pushes: 0   Pops: 1     Stack usage: 1
IL_006D     Pushes: 0   Pops: 1     Stack usage: 0 (Exception object gets consumed)
IL_0072     Pushes: 1   Pops: 0     Stack usage: 1
IL_0073     Pushes: 0   Pops: 1     Stack usage: 0
IL_0075     Pushes: 1   Pops: 0     Stack usage: 1
IL_0077     Pushes: 0   Pops: 0     Stack usage: 1
back to IL_0056
IL_0056     Pushes: 0   Pops: 1     Stack usage: 0 (This consumes the int32 with value 1 from IL_0075, which jumps to IL_0079)
IL_0079     Pushes: 1   Pops: 0     Stack usage: 1
IL_007E     Pushes: 1   Pops: 0     Stack usage: 2
IL_0087     Pushes: 0   Pops: 1     Stack usage: 1
IL_0088     Pushes: 1   Pops: 1     Stack usage: 1
IL_0089     Pushes: 1   Pops: 0     Stack usage: 2
IL_008A     Pushes: 1   Pops: 2     Stack usage: 1
IL_008B     Pushes: 0   Pops: 1     Stack usage: 0
IL_0090     Pushes: 0   Pops: all   Stack usage: 0

DNSpy告诉我,在IL_0077处是不一致的堆栈大小,但是我的分析告诉我,一切都被消耗了。 有趣的是,当我在堆栈(IL_006D DUP)或IL_0075上欺骗异常对象时。 .NET运行时还拒绝运行代码并在我手动修复它之前引发“无效程序异常”。 IMO驱逐这些说明是一个错误,会破坏堆栈。有人可以告诉我为什么这种行为究竟会发生吗?

编辑05.07.2022 20:38-修复错别字并添加堆栈分析

I'm currently working on a .NET Obfuscator and in my control flow obfuscation I'm noticing a weird behaviour. Somehow there is an inconsistent stack size in my generated code.

Original IL:

{
._Statements:

IL_0000: ldstr "MethodD"
IL_0005: call System.Void System.Console::WriteLine(System.String)


Try{
._Statements:

IL_000a: ldc.i4 128736421
IL_000f: stloc.0

IL_0010: ldc.i4 12386123
IL_0015: stloc.1

IL_0016: ldstr "Result D: {0}"
IL_001b: ldloc.0
IL_001c: ldloc.1
IL_001d: add
IL_001e: box System.Int32
IL_0023: call System.String System.String::Format(System.String,System.Object)
IL_0028: call System.Void System.Console::WriteLine(System.String)
IL_002d: leave.s IL_0057
}
Handler{
._Statements:

IL_002f: ldstr "Something horrible happened! MethodD"
IL_0034: call System.Void System.Console::WriteLine(System.String)
IL_0039: call System.Void System.Console::WriteLine(System.Object)

IL_003e: ldc.i4 129387123
IL_0043: ldc.i8 1283769182434
IL_004c: stloc.2
IL_004d: conv.i8
IL_004e: ldloc.2
IL_004f: add
IL_0050: call System.Void System.Console::WriteLine(System.Int64)
IL_0055: leave.s IL_0057
}
._Statements:

IL_0057: call System.Void System.Console::WriteLine()

IL_005c: ret

}

Control flow obfuscated IL:

/* 0x00000480 727D010070   */ IL_0000: ldstr     "MethodD"
/* 0x00000485 280800000A   */ IL_0005: call      void [mscorlib]System.Console::WriteLine(string)
.try
{
    /* 0x0000048A 16           */ IL_000A: ldc.i4.0
    /* 0x0000048B 0D           */ IL_000B: stloc.3
    /* 0x0000048C 2B00         */ IL_000C: br.s      IL_000E

    /* 0x0000048E 09           */ IL_000E: ldloc.3
    // loop start (head: IL_000F)
    /* 0x0000048F 4503000000000000000B00000016000000 */ IL_000F: switch    (IL_0020, IL_002B, IL_0036)

    /* 0x000004A0 20A55CAC07   */ IL_0020: ldc.i4    128736421
    /* 0x000004A5 0A           */ IL_0025: stloc.0
    /* 0x000004A6 17           */ IL_0026: ldc.i4.1

    /* 0x000004A7 0D           */ IL_0027: stloc.3
    /* 0x000004A8 09           */ IL_0028: ldloc.3
    /* 0x000004A9 2BE4         */ IL_0029: br.s      IL_000F

    /* 0x000004AB 204BFFBC00   */ IL_002B: ldc.i4    12386123
    /* 0x000004B0 0B           */ IL_0030: stloc.1
    /* 0x000004B1 18           */ IL_0031: ldc.i4.2
    /* 0x000004B2 0D           */ IL_0032: stloc.3
    /* 0x000004B3 09           */ IL_0033: ldloc.3
    /* 0x000004B4 2BD9         */ IL_0034: br.s      IL_000F
    // end loop

    /* 0x000004B6 728D010070   */ IL_0036: ldstr     "Result D: {0}"
    /* 0x000004BB 06           */ IL_003B: ldloc.0
    /* 0x000004BC 07           */ IL_003C: ldloc.1
    /* 0x000004BD 58           */ IL_003D: add
    /* 0x000004BE 8C0F000001   */ IL_003E: box       [mscorlib]System.Int32
    /* 0x000004C3 281500000A   */ IL_0043: call      string [mscorlib]System.String::Format(string, object)
    /* 0x000004C8 280800000A   */ IL_0048: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x000004CD DE43         */ IL_004D: leave.s   IL_0092
} // end .try
catch [mscorlib]System.Exception
{
    /* 0x000004CF 16           */ IL_004F: ldc.i4.0
    /* 0x000004D0 1304         */ IL_0050: stloc.s   V_4
    /* 0x000004D2 2B00         */ IL_0052: br.s      IL_0054

    /* 0x000004D4 1104         */ IL_0054: ldloc.s   V_4
    // loop start (head: IL_0056)
    /* 0x000004D6 45020000000000000016000000 */ IL_0056: switch    (IL_0063, IL_0079)

    /* 0x000004E3 72A9010070   */ IL_0063: ldstr     "Something horrible happened! MethodD"
    /* 0x000004E8 280800000A   */ IL_0068: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x000004ED 280D00000A   */ IL_006D: call      void [mscorlib]System.Console::WriteLine(object)
    /* 0x000004F2 17           */ IL_0072: ldc.i4.1
    /* 0x000004F3 1304         */ IL_0073: stloc.s   V_4
    /* 0x000004F5 1104         */ IL_0075: ldloc.s   V_4
    /* 0x000004F7 2BDD         */ IL_0077: br.s      IL_0056
    // end loop

    /* 0x000004F9 20734AB607   */ IL_0079: ldc.i4    129387123
    /* 0x000004FE 21E2289BE62A010000 */ IL_007E: ldc.i8    1283769182434
    /* 0x00000507 0C           */ IL_0087: stloc.2
    /* 0x00000508 6A           */ IL_0088: conv.i8
    /* 0x00000509 08           */ IL_0089: ldloc.2
    /* 0x0000050A 58           */ IL_008A: add
    /* 0x0000050B 281600000A   */ IL_008B: call      void [mscorlib]System.Console::WriteLine(int64)
    /* 0x00000510 DE00         */ IL_0090: leave.s   IL_0092
} // end handler

/* 0x00000512 17           */ IL_0092: ldc.i4.1
/* 0x00000513 1305         */ IL_0093: stloc.s   V_5
/* 0x00000515 2B00         */ IL_0095: br.s      IL_0097

/* 0x00000517 1105         */ IL_0097: ldloc.s   V_5
// loop start (head: IL_0099)
/* 0x00000519 45020000000000000001000000 */ IL_0099: switch    (IL_00A6, IL_00A7)

/* 0x00000526 2A           */ IL_00A6: ret

/* 0x00000527 280400000A   */ IL_00A7: call      void [mscorlib]System.Console::WriteLine()
/* 0x0000052C 16           */ IL_00AC: ldc.i4.0
/* 0x0000052D 1305         */ IL_00AD: stloc.s   V_5
/* 0x0000052F 1105         */ IL_00AF: ldloc.s   V_5
/* 0x00000531 2BE6         */ IL_00B1: br.s      IL_0099
// end loop
/* 0x00000533 2A           */ IL_00B3: ret

My analysis of stack behaviour (for obfuscated IL):

IL_004F     Pushes: 1   Pops: 0     Stack usage: 2 (2 because the CLR pushes the Exception object onto the stack before the first instruction in the catch block)
IL_0050     Pushes: 0   Pops: 1     Stack usage: 1
IL_0052     Pushes: 0   Pops: 0     Stack usage: 1
IL_0054     Pushes: 1   Pops: 0     Stack usage: 2
IL_0056     Pushes: 0   Pops: 1     Stack usage: 1
IL_0063     Pushes: 1   Pops: 0     Stack usage: 2
IL_0068     Pushes: 0   Pops: 1     Stack usage: 1
IL_006D     Pushes: 0   Pops: 1     Stack usage: 0 (Exception object gets consumed)
IL_0072     Pushes: 1   Pops: 0     Stack usage: 1
IL_0073     Pushes: 0   Pops: 1     Stack usage: 0
IL_0075     Pushes: 1   Pops: 0     Stack usage: 1
IL_0077     Pushes: 0   Pops: 0     Stack usage: 1
back to IL_0056
IL_0056     Pushes: 0   Pops: 1     Stack usage: 0 (This consumes the int32 with value 1 from IL_0075, which jumps to IL_0079)
IL_0079     Pushes: 1   Pops: 0     Stack usage: 1
IL_007E     Pushes: 1   Pops: 0     Stack usage: 2
IL_0087     Pushes: 0   Pops: 1     Stack usage: 1
IL_0088     Pushes: 1   Pops: 1     Stack usage: 1
IL_0089     Pushes: 1   Pops: 0     Stack usage: 2
IL_008A     Pushes: 1   Pops: 2     Stack usage: 1
IL_008B     Pushes: 0   Pops: 1     Stack usage: 0
IL_0090     Pushes: 0   Pops: all   Stack usage: 0

dnSpy is telling me that at IL_0077 is the inconsistent stack size, yet my analysis tells me that everything gets consumed.
Funnily enough when I either dupe the exception object on the stack (IL_006D dup) or IL_0075 it works. The .NET Runtime also refuses to run the code and throws an "Invalid program exception" before I manually fix it. IMO duping those instructions is an error and will corrupt the stack. Can anybody tell me why exactly this behaviour occurs?

Edit 05.07.2022 20:38 - Fix typos and add stack analysis

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

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

发布评论

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

评论(1

爱的十字路口 2025-02-20 04:16:40

这根本不是堆栈验证的工作原理。没有任何变量可能是或不会的,没有动态分析。分析查看堆栈上的对象是什么类型,无论您如何达到这一点,都需要堆栈是相同的。

switch简单地分析为一堆条件br跳。因此,在IL_0075堆栈是空的,您将加载int,然后跳回IL_0056。但是分析已经传递iL_0056,堆栈包含exception int,但现在只有int,因此不一致的堆栈错误。

仔细阅读

该验证算法在模拟过程中不利用任何数据值(例如,它不执​​行恒定传播),而仅使用类型分配

如果在下一个指令地址上存在现有的堆栈状态(对于有条件的分支机构...可能有多个这样的地址),则该算法也将失败。 >

This is simply not how stack verification works. There is no dynamic analysis of what any variables might or might not be. The analysis only looks at what types the objects on the stack are, and it requires the stack to be the same no matter how you reached that point.

A switch is simply analyzed as a bunch of conditional br jumps. So at IL_0075 the stack is empty, you load an int then jump back to IL_0056. But analysis has already passed IL_0056, at which the stack contained Exception int but now it's only int, hence the inconsistent stack error.

Take a careful read of ECMA-335 Section III 1.8.1.1:

The verification algorithm does not take advantage of any data values during its simulation (e.g., it does not perform constant propagation), but uses only type assignments

The algorithm shall also fail if there is an existing stack state at the next instruction address (for conditional branches ... there might be more than one such address) that cannot be merged with the stack state just computed"

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