星期三, 五月 13, 2009

SUDOKU破解与生成程序1.0

不多说了,增加了第三逻辑和对可能情况的遍历(也就是当不能用逻辑区分的时候的暴力破解)。
http://lostabaddon.spaces.live.com/cns!EB06676D0B60BFBD!1986.entry
FrmSudoku.FRM:
Option Explicit
Dim SUDOKU(9, 9, 9) As Integer
Dim SudokuSolution As New ClsSudoku
Private Sub CmdClear_Click()
Dim I As Integer, J As Integer, K As Integer
For I = 1 To 9
For J = 1 To 9
For K = 0 To 9
SUDOKU(I, J, K) = 0
Next K
Next J
Next I
ShowSUDOKU
End Sub
Private Sub Form_Load()
Dim I As Integer, J As Integer, X As Integer, Y As Integer
Dim T As Integer
Dim P As Single
P = (Me.ScaleWidth - 3700) / 2
TxtInput(0).Visible = False
For I = 1 To 3
For J = 1 To 3
For X = 1 To 3
For Y = 1 To 3
T = ((I - 1) * 3 + (J - 1)) * 9 + ((X - 1) * 3 + Y)
Load TxtInput(T)
TxtInput(T).Left = ((I - 1) * 3 + X - 1) * 400 + (I - 1) * 50 + P
TxtInput(T).Top = ((J - 1) * 3 + Y - 1) * 400 + (J - 1) * 50
TxtInput(T).Visible = True
TxtInput(T) = ""
Next Y
Next X
Next J
Next I
CmdStart.Top = Me.ScaleHeight - CmdStart.Height
CmdGenerate.Top = Me.ScaleHeight - CmdGenerate.Height
CmdStart.Left = (Me.ScaleWidth - CmdStart.Width - CmdGenerate.Width) / 2
CmdGenerate.Left = (Me.ScaleWidth - CmdStart.Width - CmdGenerate.Width) / 2 + CmdStart.Width
End Sub
Private Sub CmdStart_Click()
ReadSUDOKU
If SudokuSolution.Solution(SUDOKU) Then
ShowSUDOKU
End If
ShowSUDOKU
End Sub
Private Sub CmdGenerate_Click()
SudokuSolution.Build SUDOKU
ShowSUDOKU
Me.Caption = "Sudoku Solution"
End Sub
Private Sub TxtInput_DblClick(Index As Integer)
Dim A As Integer
A = Int((Index - 1) / 9) + 1
Dim X As Integer, Y As Integer
X = A - Int(A / 3) * 3
If X = 0 Then X = 3
Y = Int((A - 1) / 3 + 1)
A = Index - ((Y - 1) * 3 + X - 1) * 9
Dim I As Integer, J As Integer
I = A - Int(A / 3) * 3
If I = 0 Then I = 3
J = Int((A - 1) / 3 + 1)
MsgBox Str((X - 1) * 3 + I) & "," & Str((Y - 1) * 3 + J)
End Sub
'辅助区
Private Sub ReadSUDOKU()
Dim I As Integer, J As Integer, X As Integer, Y As Integer
Dim T As Integer, Q As Integer, K As Integer
For I = 1 To 3
For J = 1 To 3
For X = 1 To 3
For Y = 1 To 3
T = ((I - 1) * 3 + (J - 1)) * 9 + ((X - 1) * 3 + Y)
Q = Val(TxtInput(T))
If Q >= 0 Then
SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, 0) = Q
End If
For K = 1 To 9
SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, K) = 0
Next K
Next Y
Next X
Next J
Next I
End Sub
Private Sub ShowSUDOKU()
Dim I As Integer, J As Integer, X As Integer, Y As Integer
Dim T As Integer, Q As Integer
For I = 1 To 3
For J = 1 To 3
For X = 1 To 3
For Y = 1 To 3
T = ((I - 1) * 3 + (J - 1)) * 9 + ((X - 1) * 3 + Y)
Q = SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, 0)
If Q > 0 Then
TxtInput(T).Text = Q
Else
TxtInput(T).Text = ""
End If
Next Y
Next X
Next J
Next I
End Sub
'辅助区结束
ClsSudoku.CLS:
Option Explicit
Dim SUDOKU(9, 9, 9) As Integer
'功能区
Public Function Solution(ByRef S() As Integer) As Boolean
Dim I As Integer, J As Integer, T() As Integer, K As Integer
Dim IsOK As Boolean
ReDim T(0)
GetSUDOKU S
Solution = False
If Check Then
If Not CheckEmpty Then
Exit Function
End If
Else
Exit Function
End If
IsOK = True
Do While IsOK
IsOK = Deal Or ThirdLogic
Loop
Solution = IsFinished
If Solution Then
BackSUDOKU S
Else
Find I, J, T
If UBound(T) = 0 Then Solution = False: Exit Function
For K = 1 To UBound(T)
If Try(I, J, T(K)) Then BackSUDOKU S: Solution = True: Exit For
Next K
End If
End Function
Public Sub Build(ByRef S() As Integer)
'初始化最原始SUDOKU
Dim I As Integer
For I = 1 To 9
SUDOKU(I, 1, 0) = I
Next I
For I = 1 To 3
SUDOKU(6 + I, 2, 0) = I
SUDOKU(I, 3, 0) = 6 + I
Next I
For I = 1 To 6
SUDOKU(I, 2, 0) = 3 + I
SUDOKU(3 + I, 3, 0) = I
Next I
For I = 1 To 8
SUDOKU(I, 4, 0) = I + 1
SUDOKU(I + 1, 7, 0) = I
Next I
SUDOKU(3, 4, 0) = 1
SUDOKU(6, 4, 0) = 4
SUDOKU(9, 4, 0) = 7
SUDOKU(1, 7, 0) = 3
SUDOKU(4, 7, 0) = 6
SUDOKU(7, 7, 0) = 9
For I = 1 To 5
SUDOKU(I, 5, 0) = I + 4
SUDOKU(I + 1, 8, 0) = I + 3
Next I
SUDOKU(6, 5, 0) = 7
SUDOKU(7, 5, 0) = 2
SUDOKU(8, 5, 0) = 3
SUDOKU(9, 5, 0) = 1
SUDOKU(3, 5, 0) = 4
SUDOKU(1, 8, 0) = 6
SUDOKU(4, 8, 0) = 9
SUDOKU(7, 8, 0) = 3
SUDOKU(8, 8, 0) = 1
SUDOKU(9, 8, 0) = 2
For I = 1 To 5
SUDOKU(I + 3, 6, 0) = I + 1
SUDOKU(I + 4, 9, 0) = I
Next I
SUDOKU(1, 6, 0) = 8
SUDOKU(2, 6, 0) = 9
SUDOKU(3, 6, 0) = 7
SUDOKU(6, 6, 0) = 1
SUDOKU(9, 6, 0) = 4
SUDOKU(1, 9, 0) = 9
SUDOKU(2, 9, 0) = 7
SUDOKU(3, 9, 0) = 8
SUDOKU(4, 9, 0) = 3
SUDOKU(7, 9, 0) = 6
'SUDOKU混排
I = 5 + Int(Rnd() * 10)
Do While I > 0
If Rnd() > 0.5 Then
RowTrans 1 + Int(Rnd() * 3), 1 + Int(Rnd() * 3)
Else
ColTrans 1 + Int(Rnd() * 3), 1 + Int(Rnd() * 3)
End If
I = I - 1
Loop
For I = 1 To 6
RowTrans 1 + Int(Rnd() * 3), 1 + Int(Rnd() * 3)
ColTrans 1 + Int(Rnd() * 3), 1 + Int(Rnd() * 3)
Next I
Cycle
Cover
BackSUDOKU S
End Sub
'功能区结束
'辅助区
Private Function Try(ByVal I As Integer, ByVal J As Integer, ByVal T As Integer) As Boolean
Dim SS(9, 9, 9) As Integer
BackSUDOKU SS
SS(I, J, 0) = T
Dim SSS As New ClsSudoku
Try = SSS.Solution(SS)
If Try Then
GetSUDOKU SS
End If
End Function
Private Sub Find(ByRef I As Integer, ByRef J As Integer, ByRef T() As Integer)
Dim A As Integer, B As Integer, C As Integer
Dim D As Integer, E As Integer
E = 10
For A = 1 To 9
For B = 1 To 9
If SUDOKU(A, B, 0) = 0 Then
D = 0
For C = 1 To 9
If SUDOKU(A, B, C) = 1 Then D = D + 1
Next C
If D < E And D > 0 Then
ReDim T(0)
For C = 1 To 9
If SUDOKU(A, B, C) = 1 Then
ReDim Preserve T(UBound(T) + 1)
T(UBound(T)) = C
End If
Next C
I = A
J = B
End If
End If
Next B
Next A
End Sub
Private Function IsFinished() As Boolean
IsFinished = False
Dim I As Integer
Dim J As Integer
If Not Check Then Exit Function
For I = 1 To 9
For J = 1 To 9
If SUDOKU(I, J, 0) = 0 Then Exit Function
Next J
Next I
IsFinished = True
End Function
Private Function Check() As Boolean
Dim I As Integer, J As Integer, X As Integer, Y As Integer
Dim T As Integer, Q As Integer
Dim P1(9) As Integer, P2(9) As Integer, P3(9) As Integer
Check = True
For I = 1 To 3
For X = 1 To 3
For T = 1 To 9
P1(T) = T
P2(T) = T
P3(T) = T
Next
For J = 1 To 3
For Y = 1 To 3
Q = SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, 0)
If P1(Q) > 0 Or Q = 0 Then
P1(Q) = 0
Else
Check = False
Exit Function
End If
Q = SUDOKU((J - 1) * 3 + Y, (I - 1) * 3 + X, 0)
If P2(Q) > 0 Or Q = 0 Then
P2(Q) = 0
Else
Check = False
Exit Function
End If
Q = SUDOKU((I - 1) * 3 + J, (X - 1) * 3 + Y, 0)
If P3(Q) > 0 Or Q = 0 Then
P3(Q) = 0
Else
Check = False
Exit Function
End If
Next
Next
Next
Next
End Function
Private Function CheckEmpty() As Boolean
Dim I As Integer, J As Integer
Dim P As Integer
For I = 1 To 9
For J = 1 To 9
P = P + SUDOKU(I, J, 0)
Next J
Next I
If P = 0 Then
CheckEmpty = False
Else
CheckEmpty = True
End If
End Function
Private Sub GetSUDOKU(ByRef S() As Integer)
Dim I As Integer, J As Integer, K As Integer
For I = 1 To 9
For J = 1 To 9
SUDOKU(I, J, 0) = S(I, J, 0)
For K = 1 To 9
SUDOKU(I, J, K) = 0
Next K
Next J
Next I
End Sub
Private Sub BackSUDOKU(ByRef S() As Integer)
Dim I As Integer, J As Integer, K As Integer
For I = 1 To 9
For J = 1 To 9
S(I, J, 0) = SUDOKU(I, J, 0)
For K = 1 To 9
S(I, J, K) = SUDOKU(I, J, K)
Next K
Next J
Next I
End Sub
'辅助区结束
'SUDOKU生成区
Private Sub RowTrans(ByVal SupN As Integer, ByVal SubN As Integer)
Dim I As Integer
Dim J As Integer
Dim K As Integer
Dim R As Integer
Dim S As Integer
Dim A(9) As Integer, B(9) As Integer
I = (SupN - 1) * 3
J = I
Select Case SubN
Case 1
I = I + 2
J = J + 3
Case 2
I = I + 1
J = J + 3
Case 3
I = I + 1
J = J + 2
End Select
For K = 1 To 9
A(K) = SUDOKU(I, K, 0)
B(K) = SUDOKU(J, K, 0)
Next K
K = Int(Rnd() * 5) + 1
S = SUDOKU(J, K, 0)
R = SUDOKU(I, K, 0)
A(K) = S
B(K) = R
Do While S <> R
For K = 1 To 9
If SUDOKU(I, K, 0) = S Then Exit For
Next K
S = SUDOKU(J, K, 0)
A(K) = SUDOKU(J, K, 0)
B(K) = SUDOKU(I, K, 0)
Loop
For K = 1 To 9
SUDOKU(I, K, 0) = A(K)
SUDOKU(J, K, 0) = B(K)
Next K
End Sub
Private Sub ColTrans(ByVal SupN As Integer, ByVal SubN As Integer)
Dim I As Integer
Dim J As Integer
Dim K As Integer
Dim R As Integer
Dim S As Integer
Dim A(9) As Integer, B(9) As Integer
I = (SupN - 1) * 3
J = I
Select Case SubN
Case 1
I = I + 2
J = J + 3
Case 2
I = I + 1
J = J + 3
Case 3
I = I + 1
J = J + 2
End Select
For K = 1 To 9
A(K) = SUDOKU(K, I, 0)
B(K) = SUDOKU(K, J, 0)
Next K
K = Int(Rnd() * 5) + 1
S = SUDOKU(K, J, 0)
R = SUDOKU(K, I, 0)
A(K) = S
B(K) = R
Do While S <> R
For K = 1 To 9
If SUDOKU(K, I, 0) = S Then Exit For
Next K
S = SUDOKU(K, J, 0)
A(K) = SUDOKU(K, J, 0)
B(K) = SUDOKU(K, I, 0)
Loop
For K = 1 To 9
SUDOKU(K, I, 0) = A(K)
SUDOKU(K, J, 0) = B(K)
Next K
End Sub
Private Sub Cycle()
Dim T(9) As Integer
Dim I As Integer
Dim J As Integer
Dim K As Integer
Randomize
Do While I < 9
J = Int(Rnd() * 9) + 1
If J = 10 Then J = 1
K = K + J
If K > 9 Then K = K - 9
Do While T(K) > 0
K = K + 1
If K = 10 Then K = 1
Loop
I = I + 1
T(K) = I
Loop
For I = 1 To 9
For J = 1 To 9
SUDOKU(I, J, 0) = T(SUDOKU(I, J, 0))
Next J
Next I
End Sub
Private Sub Cover()
Dim Total As Integer
Total = 25 + Int((Rnd() + Rnd()) / 2 * 9)
Total = 81 - Total
Dim I As Integer
Dim J As Integer
Do While Total > 0
I = Int(Rnd() * 9) + 1
If I = 10 Then I = 1
J = Int(Rnd() * 9) + 1
If J = 10 Then J = 9
If SUDOKU(I, J, 0) > 0 Then
SUDOKU(I, J, 0) = 0
Total = Total - 1
End If
Loop
End Sub
'SUDOKU生成区结束
'排除法分析区
Private Function AnalyzeRestH(ByVal Index As Integer) As Integer()
Dim I As Integer
Dim J As Integer
Dim P() As Integer
ReDim P(0)
J = 1
For I = 1 To 9
If SUDOKU(Index, I, 0) > 0 Then
ReDim Preserve P(J)
P(J) = SUDOKU(Index, I, 0)
J = J + 1
End If
Next I
AnalyzeRestH = P
End Function
Private Function AnalyzeRestV(ByVal Index As Integer) As Integer()
Dim I As Integer
Dim J As Integer
Dim P() As Integer
ReDim P(0)
J = 1
For I = 1 To 9
If SUDOKU(I, Index, 0) > 0 Then
ReDim Preserve P(J)
P(J) = SUDOKU(I, Index, 0)
J = J + 1
End If
Next I
AnalyzeRestV = P
End Function
Private Function AnalyzeRestR(ByVal Index As Integer) As Integer()
Dim I As Integer
Dim J As Integer
Dim P() As Integer
ReDim P(0)
I = Int(Index / 3)
J = Index - I * 3
If J = 0 Then J = 3: I = I - 1
I = I + 1
Dim T As Integer
T = 1
Dim X As Integer, Y As Integer
For X = 1 To 3
For Y = 1 To 3
If SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, 0) > 0 Then
ReDim Preserve P(T)
P(T) = SUDOKU((I - 1) * 3 + X, (J - 1) * 3 + Y, 0)
T = T + 1
End If
Next
Next
AnalyzeRestR = P
End Function
Private Function Union(ByRef A() As Integer, B() As Integer, C() As Integer) As Integer()
Dim I As Integer
Dim P(9) As Integer
Dim Q() As Integer
ReDim Q(0)
For I = 1 To 9
P(I) = 1
Next
For I = 1 To UBound(A)
P(A(I)) = 0
Next
For I = 1 To UBound(B)
P(B(I)) = 0
Next
For I = 1 To UBound(C)
P(C(I)) = 0
Next
Dim J As Integer
J = 1
ReDim Union(0)
For I = 1 To 9
If P(I) = 1 Then
ReDim Preserve Q(J)
Q(J) = I
J = J + 1
End If
Next
Union = Q
End Function
'排除法分析区结束
'外推法分析区
Private Function Surround(ByVal Number As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal I As Integer, ByVal J As Integer) As Boolean
Dim A As Integer, B As Integer, C As Integer, D As Integer
Dim M As Integer, N As Integer, P As Integer, Q As Integer
GetBoundary X, M, N
GetBoundary Y, P, Q
GetBoundary I, A, B
GetBoundary J, C, D
Dim Ans As Integer, Res As Integer
Dim T As Integer, R As Integer
Dim Fin As Boolean
Dim TmpA As Integer, TmpB As Integer
Dim Tmp(4) As Integer
T = X * 3 - 3 + A
Fin = False
If SUDOKU(T, Y * 3 - 2, 0) > 0 And SUDOKU(T, Y * 3 - 1, 0) > 0 And SUDOKU(T, Y * 3, 0) > 0 Then
Ans = 1
Else
For R = P * 3 - 2 To P * 3
If SUDOKU(T, R, 0) = Number Then Ans = 1: Fin = True: Exit For
Next R
If Not Fin Then
For R = Q * 3 - 2 To Q * 3
If SUDOKU(T, R, 0) = Number Then Ans = 1: Fin = True: Exit For
Next R
End If
If Fin Then Tmp(1) = 1
End If
T = X * 3 - 3 + B
Fin = False
If SUDOKU(T, Y * 3 - 2, 0) > 0 And SUDOKU(T, Y * 3 - 1, 0) > 0 And SUDOKU(T, Y * 3, 0) > 0 Then
Ans = Ans + 1
Else
For R = P * 3 - 2 To P * 3
If SUDOKU(T, R, 0) = Number Then Ans = Ans + 1: Fin = True: Exit For
Next R
If Not Fin Then
For R = Q * 3 - 2 To Q * 3
If SUDOKU(T, R, 0) = Number Then Ans = Ans + 1: Fin = True: Exit For
Next R
End If
If Fin Then Tmp(2) = 1
End If
T = Y * 3 - 3 + C
Fin = False
If SUDOKU(X * 3 - 2, T, 0) > 0 And SUDOKU(X * 3 - 1, T, 0) > 0 And SUDOKU(X * 3, T, 0) > 0 Then
Res = 1
Else
For R = M * 3 - 2 To M * 3
If SUDOKU(R, T, 0) = Number Then Res = 1: Fin = True: Exit For
Next R
If Not Fin Then
For R = N * 3 - 2 To N * 3
If SUDOKU(R, T, 0) = Number Then Res = 1: Fin = True: Exit For
Next R
End If
If Fin Then Tmp(3) = 1
End If
T = Y * 3 - 3 + D
Fin = False
If SUDOKU(X * 3 - 2, T, 0) > 0 And SUDOKU(X * 3 - 1, T, 0) > 0 And SUDOKU(X * 3, T, 0) > 0 Then
Res = Res + 1
Else
For R = M * 3 - 2 To M * 3
If SUDOKU(R, T, 0) = Number Then Res = Res + 1: Fin = True: Exit For
Next R
If Not Fin Then
For R = N * 3 - 2 To N * 3
If SUDOKU(R, T, 0) = Number Then Res = Res + 1: Fin = True: Exit For
Next R
End If
If Fin Then Tmp(4) = 1
End If
If Ans = 2 And Res = 2 Then Surround = True: Exit Function
TmpA = SUDOKU(X * 3 - 3 + I, Y * 3 - 3 + C, 0)
TmpB = SUDOKU(X * 3 - 3 + I, Y * 3 - 3 + D, 0)
If Ans = 2 And ((TmpA > 0 And TmpB > 0) Or (Tmp(4) = 1 And TmpA > 0) Or (Tmp(3) = 1 And TmpB > 0)) Then Surround = True: Exit Function
TmpA = SUDOKU(X * 3 - 3 + A, Y * 3 - 3 + J, 0)
TmpB = SUDOKU(X * 3 - 3 + B, Y * 3 - 3 + J, 0)
If Res = 2 And ((TmpA > 0 And TmpB > 0) Or (Tmp(2) = 1 And TmpA > 0) Or (Tmp(1) = 1 And TmpB > 0)) Then Surround = True: Exit Function
End Function
Private Function Environment(ByVal Number As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal I As Integer, ByVal J As Integer) As Boolean
Dim A As Integer, B As Integer, C As Integer, D As Integer
Dim M As Integer, N As Integer, P As Integer, Q As Integer
GetBoundary X, M, N
GetBoundary Y, P, Q
GetBoundary I, A, B
GetBoundary J, C, D
Dim Tmp(1) As Boolean
Dim Found As Boolean
Dim T As Integer
Dim K As Integer
Dim Tmps As Integer
Environment = False
Found = True
K = X * 3 - 3 + I
For T = 1 To 3 '当所在列出现该数的存在概率时
If SUDOKU(K, P * 3 - 3 + T, Number) = 1 Or SUDOKU(K, Q * 3 - 3 + T, Number) = 1 Then Found = False: Exit For
Next T
If Found Then
K = X * 3 - 3 + A
If SUDOKU(K, Y * 3 - 3 + J, 0) > 0 Then
Found = True
Else
For T = 1 To 3
If SUDOKU(K, P * 3 - 3 + T, Number) = 1 Then Exit For
If SUDOKU(K, Q * 3 - 3 + T, Number) = 1 Then Exit For
If SUDOKU(K, P * 3 - 3 + T, 0) = Number Then Exit For
If SUDOKU(K, Q * 3 - 3 + T, 0) = Number Then Exit For
Next T
If T < 4 Then Found = True
End If
If Found Then
Found = False
K = X * 3 - 3 + B
If SUDOKU(K, Y * 3 - 3 + J, 0) > 0 Then
Found = True
Else
For T = 1 To 3
If SUDOKU(K, P * 3 - 3 + T, Number) = 1 Then Exit For
If SUDOKU(K, Q * 3 - 3 + T, Number) = 1 Then Exit For
If SUDOKU(K, P * 3 - 3 + T, 0) = Number Then Exit For
If SUDOKU(K, Q * 3 - 3 + T, 0) = Number Then Exit For
Next T
If T < 4 Then Found = True
End If
End If
End If
If Found Then Tmp(0) = True
Found = True
K = Y * 3 - 3 + J
For T = 1 To 3 '当所在列出现该数的存在概率时
If SUDOKU(M * 3 - 3 + T, K, Number) = 1 Or SUDOKU(N * 3 - 3 + T, K, Number) = 1 Then Found = False: Exit For
Next T
If Found Then
K = Y * 3 - 3 + C
If SUDOKU(X * 3 - 3 + I, K, 0) > 0 Then
Found = True
Else
For T = 1 To 3
If SUDOKU(M * 3 - 3 + T, K, Number) = 1 Then Exit For
If SUDOKU(N * 3 - 3 + T, K, Number) = 1 Then Exit For
If SUDOKU(M * 3 - 3 + T, K, 0) = Number Then Exit For
If SUDOKU(N * 3 - 3 + T, K, 0) = Number Then Exit For
Next T
If T < 4 Then Found = True
End If
If Found Then
Found = False
K = Y * 3 - 3 + D
If SUDOKU(X * 3 - 3 + I, K, 0) > 0 Then
Found = True
Else
For T = 1 To 3
If SUDOKU(M * 3 - 3 + T, K, Number) = 1 Then Exit For
If SUDOKU(N * 3 - 3 + T, K, Number) = 1 Then Exit For
If SUDOKU(M * 3 - 3 + T, K, 0) = Number Then Exit For
If SUDOKU(N * 3 - 3 + T, K, 0) = Number Then Exit For
Next T
If T < 4 Then Found = True
End If
End If
End If
If Found Then Tmp(1) = True
If Tmp(0) And Tmp(1) Then Environment = True: Exit Function
If Tmp(0) And SUDOKU(X * 3 - 3 + I, Y * 3 - 3 + C, 0) > 0 And SUDOKU(X * 3 - 3 + I, Y * 3 - 3 + D, 0) > 0 Then Environment = True: Exit Function
If Tmp(1) And SUDOKU(X * 3 - 3 + A, Y * 3 - 3 + J, 0) > 0 And SUDOKU(X * 3 - 3 + B, Y * 3 - 3 + J, 0) > 0 Then Environment = True
End Function
Private Sub GetBoundary(ByVal Index As Integer, ByRef Down As Integer, ByRef Up As Integer)
Select Case Index
Case 1
Down = 2: Up = 3
Case 2
Down = 1: Up = 3
Case 3
Down = 1: Up = 2
End Select
End Sub
'外推法分析区结束
Private Function Deal() As Boolean
Deal = False
Dim HasNew As Boolean
HasNew = True
Dim I As Integer, J As Integer, K As Integer
Dim P1() As Integer, P2() As Integer, P3() As Integer, P4() As Integer
Dim T As Integer
Do While HasNew = True
HasNew = False
For I = 1 To 9
P1 = AnalyzeRestH(I)
For J = 1 To 9
If SUDOKU(I, J, 0) = 0 Then
P2 = AnalyzeRestV(J)
P3 = AnalyzeRestR(Int((I - 1) / 3) * 3 + Int((J - 1) / 3) + 1)
P4 = Union(P1, P2, P3)
If UBound(P4) = 1 Then
SUDOKU(I, J, 0) = P4(1)
HasNew = True
Else
For T = 1 To UBound(P4)
If Surround(P4(T), Int((I - 1) / 3 + 1), Int((J - 1) / 3 + 1), I - Int((I - 1) / 3) * 3, J - Int((J - 1) / 3) * 3) Then
SUDOKU(I, J, 0) = P4(T)
HasNew = True
Exit For
End If
Next T
End If
End If
Next
Next
If HasNew Then Deal = True
Loop
End Function
Private Function ThirdLogic() As Boolean
ThirdLogic = False
Dim HasNew As Boolean
HasNew = True
Dim I As Integer, J As Integer
Dim T As Integer, K As Integer
Dim X As Integer, Y As Integer
Dim P1() As Integer, P2() As Integer, P3() As Integer, P4() As Integer
For I = 1 To 9
P1 = AnalyzeRestH(I)
For J = 1 To 9
For T = 1 To 9
SUDOKU(I, J, T) = 0
Next T
If SUDOKU(I, J, 0) = 0 Then
P2 = AnalyzeRestV(J)
P3 = AnalyzeRestR(Int((I - 1) / 3) * 3 + Int((J - 1) / 3) + 1)
P4 = Union(P1, P2, P3)
If UBound(P4) > 1 Then
For T = 1 To UBound(P4)
SUDOKU(I, J, P4(T)) = 1
Next
End If
End If
Next J
Next I
Do While HasNew = True
HasNew = False
For I = 1 To 9
For J = 1 To 9
For T = 1 To 9
If SUDOKU(I, J, T) = 1 Then
X = Int((I - 1) / 3 + 1)
Y = Int((J - 1) / 3 + 1)
If Environment(T, X, Y, I - Int((I - 1) / 3) * 3, J - Int((J - 1) / 3) * 3) Then
SUDOKU(I, J, 0) = T
For K = 1 To 9
SUDOKU(I, K, T) = 0
SUDOKU(K, J, T) = 0
Next K
For K = 1 To 3
SUDOKU(X * 3 - 2, Y * 3 - 3 + K, T) = 0
SUDOKU(X * 3 - 1, Y * 3 - 3 + K, T) = 0
SUDOKU(X * 3, Y * 3 - 3 + K, T) = 0
Next K
HasNew = True
Exit For
End If
End If
Next T
Next J
Next I
If HasNew Then ThirdLogic = True
Loop
End Function

华为 OptiX OSN 2500

OptiX OSN 2500 智能光传输系统是继承了MSTP技术的全部特点,与传统SDH、MSTP网络保持兼容,融SDH、PDH、Ethernet、WDM、ATM、ESCON、FC/FICON、DVB-ASI(Digital Video Broadcast-Asynchronous Serial Interface)、RPR等技术为一体的新一代2.5G/622M多业务光传输平台(MSTP),主要应用在城域网络中的汇聚与接入层,为现有SDH设备向智能光网络设备过渡提供了完善的解决方案。
功能描述: 高性价比的平台化 OptiX OSN 7500/3500/2500/1500业务板件、软件完全兼容,构成统一平台。这样可以大大降低维护成本以及备板备件的成本。OSN智能平台在业务能力方面高效组合,给运营商最高性价比的解决方案;同时与华为已有的设备可以混合组网,可以纳入华为光网络网管解决方案进行统一管理。 灵活的设备配置 STM-16/4兼容设备; 支持网络设备从622M到2.5G的在线升级。 大容量调度 支持20G高低阶全交叉:高阶128×128 VC-4,低阶8064×8064 VC-12或等效的VC-3交叉能力。 多业务提供 业务接口 支持STM-1(O/E); 支持STM-4/16标准或级联业务; 支持E1/T1/E3/T3/E4;支持FE、GE业务的透传和交换; 支持ATM业务;   支持IMA业务;  支持SAN业务和视频业务; 提供基于GMPLS的快速端到端业务
特征与优势: 高度集成 子架集成度:子架尺寸为460mm(高)×440mm(宽)×295mm(深),单子架具备9个业务板位,8个接口板位。 REG功能 支持基于 OSN 2500 设计的硬件REG设备; 强组网能力 支持Mesh网络节点即插即用 支持Mesh网络在线任意速率升级扩容; 支持Mesh网络中多达40个光方向的组网; 支持链、环、相交环、相切环等多种SDH组网拓扑;   支持RPR、VP-RING环网; 单子架可实现1×STM-64四纤环或2×STM-16四纤环或4×STM-16二纤环。 内置波分技术 提供双路光分叉复用单板; 提供任意速率光波长转换单板。 完善的网络生存机制 Mesh恢复 支持分布式可恢复重路由保护; 提供5类更细化的业务保护方案,根据不同SLA提供:钻石级、金级、银级、铜级、铁级业务。 SDH保护 支持2F/4F MSP、SNCP、DNI、共享光线虚拟路径保护等。 数据业务保护 支持以太业务RPR环网保护,STP生成树保护; 支持ATM业务VP-RING环网保护。 完备的设备保护机制 智能控制单元保护:1+1热备份; 支持关键单元如交叉、时钟1+1热备份保护; 电源保护; TPS保护。
技术规格: OptiX OSN 2500 电源接入 -48V DC、通过外置UPM实现交流220V接入 安装方式 ETSI 300mm深机柜、ETSI 600mm深机柜、19英寸机柜中安装 尺寸 460mm(高)×440mm(宽)×295mm(深) 典型重量 24kg 典型功耗 206W

读代码学ahk 系列之10

1、MsgBox命令有几种格式?
MsgBox, 文本 或 MsgBox [, 类型,标题, 文本, 显示时间 ]
2、MsgBox命令按钮有几种类型?
OK—0
OK/Cancel—1
Abort/Retry/Ignore—2
Yes/No/Cancel—3
Yes/No—4
Retry/Cancel—5
Icon Hand (stop/error)—16
Icon Question—32
Icon Exclamation—48
Icon Asterisk (info)—64
Make 2nd button the default—256
Make 3rd button the default—512
System Modal (always on top) —4096
Task Modal—8192 这些参数允许相加而得到复合效果。
3、显示含有yes/no按钮并总在顶端的对话框?
msgbox,4100,标题,内容
4、MsgBox如何换行?
MsgBox,你好,欢迎关注静海安澜阁 `n 请继续关注

5、如何对MsgBox命令的选择结果进行处理?
IfMsgBox, ButtonName
ButtonName可选项: Yes No OK Cancel Abort Ignore Retry Timeout
6、对话框显示时间,并以秒为单位?
msgbox , , 标题 , 内容 , 5
7、运行外部可执行程序或者直接打开文件?
Run, 目标文件[ , 工作目录 , Max|Min|Hide|UseErrorLevel , 输出PID 变量]
8、如何运行记事本并以最大化状态显示?
Run, Notepad.exe , , Max
9、如何直接打开文件?
Run, MyFile.txt
10、命令行解释器cmd.exe 用那个宏来表示?
%ComSpec% /k在执行完命令后保留命令提示窗口,而/c则在执行完命令之后关闭提示窗口
11、命令“dir c:\Windows\system32”如何实现?
Run, %ComSpec% /k dir c:\Windows\system32
12、如何打开网页?
Run, http://hi.baidu.com/xuejinglan
13、如何打开系统属性窗口?
Run control sysdm.cpl 需要的话可以搜索所有的*.cpl文件
14、打开控制面板?
run,control
15、如何打开搜索窗口且指定搜索位置?
Run, find C:\
16、如何显示指定文件的属性窗口?
Run, properties MyFile.txt 注意:AHK 在退出前将自动关闭打开的属性窗口!
17、如何打印指定文件?
Run, print MyFile.txt
18、等待窗口出现、被激活、被关闭用那些命令?
WinWait/WinWaitActive/WinWaitClose
19、如何使目标窗口出现之前不执行后面语句?
WinWait [, 窗口标题, 窗口文本, 超时时间, 排除标题, 排除文本]
20、如何使目标窗口成为活动窗口?
WinActivate [,窗口标题, 窗口文本, 排除标题, 排除文本]
21、关闭窗口有几种方式?
正常关闭窗口(WinClose ),强行关闭窗口(WinKill):
WinClose/WinKill [ , 窗口标题, 窗口文本, 超时时间, 排除标题, 排除文本]
22、同名窗口使我们无法用窗口标题准确标识窗口,如何使用窗口ID来准确定位窗口?
使用获得窗口句柄的命令WinGet
WinGet[, 输出变量, CMD, 窗口标题, 窗口文本, 排除标题, 排除文本]
23、如何用窗口ID来替代窗口标题?
ahk_pid %ThisPID%
24、那个命令把鼠标点击事件发送到目标窗口的目标控件上?
ControlClick [, 目标控件或坐标位置, 窗口标题, 窗口文本, 鼠标按钮, 点击次数, 选项, 排除标题,排除文本]
25、ControlClick命令中,目标控件参数主要是什么?
目标控件参数是指要点击的控件的类别名(ClassNN)或控件文本,还可以使用控件句柄(若用的是控件句柄则第一个参数需留空,
并在第二个参数中使用ahk_id %控件句柄%)。
26、如何获得目标控件信息?
先打开你要进行操作的目标窗口,然后运行AutoIt3 Window Spy,把鼠标移到目标控件上
27、鼠标单击系统属性确定按钮代码是?
ControlClick, 确定, 系统属性
ControlClick, Button2, 系统属性
28、目标窗口或控件是隐藏状态,ControlClick还可以点击目标控件吗?
可以点击目标控件,但不能保证成功率。
29、如何设置控件文本?
ControlSetText [, 目标控件, 新文本, 窗口标题, 窗口文本, 排除标题, 排除文本]
例如:ControlSetText, Edit1, D:\foobar2000, Setup foobar
30、如何选中和取消单选框和复选框项目中的选择项目?
Control [, 命令, 值, 目标控件, 窗口标题, 窗口文本, 排除标题, 排除文本]
其中,“命令”就是让我们指定要进行何种设置的参数。对这些单选框/复选框按钮来说,适用的命令是“Check”和“UnCheck ”。
例如:Control, Check, , Button5, foobar
Control, UnCheck, , Button7, foobar
31、如何选择下拉列表的项目?
仍是使用Control命令,适用的命令是“Choose, N”和“ChooseString, String”,分别表示选中第N个项目和选中与字符串String
匹配的项目;例如:
Control, Choose, 5, ComboBox1, Installer
Control, ChooseString, 简体中文, ComboBox1, Installer
32、键盘如何重定义?
a::b
33、字符串如何进行自动替换?
::is::island of resistance或者
::china::
Clipboard=中华人民共和国
send,^V
Return
34、屏蔽或开启键盘、鼠标?
BlockInput 命令后,紧跟on,则屏蔽键盘鼠标操作,紧跟off,则重新启用用户输入。
如何仅仅屏蔽鼠标?如何恢复? BlockInput,MouseMove 恢复为:BlockInput, MouseMoveOff
35、如何建立一个弹出式窗口例示文本?如何关闭?
SplashTextOn [, 宽度, 高度, 标题, 文本]
`n 可以在MsgBox和SplashTextOn等命令中对显示的文本进行换行。
SplashTextOff 为关闭SplashTextOn命令建立的弹出式窗口。
36、如何将一系列指令重复执行指定次数? Loop [, Count]
{
循环体……
}
37、Loop中的count允许是表达式或者变量吗?
Count不允许是表达式,但允许是一个变量。
如何知道loop现在循环到第几次? loop 指令隐含有一个变量 A_Index ,用来记录当前状态是第几次循环
38、如何中断循环?
if a_index > 25
break ; 中断循环
39、如何跳过本次循环?
if a_index < 20
continue ; 跳过本次循环,开始一次新的循环
40、如何使使脚本等待指定时间?
Sleep,时间 单位为毫秒
41、如进行数学运算,使用什么符来把表达式运算的结果赋予某变量?
使用冒号等号符。如:NetPrice := Price * (1 - Discount/100)

42、i := i+1 的简化写法是什么?
i + =1
43、如何获取指定区间的随机数?
Random, 输出变量 [, 最小值, 最大值]
44、如何从指定文本中读取指定行?
FileReadLine, 输出变量, 文件名, 行数
45、如何判断文本超出边界?
ErrorLevel 是命令内置的一个变量,当读取文件超出边界时将被赋值为1.
46、ErrorLevel 为0和1时一般代表什么?
一般情况下,为0代表操作成功,为1,代表操作失败。
47、一般情况下,编写弹出式菜单代码的顺序是什么?
1、先定义子菜单项;2、编写菜单项;3、编写令菜单显示的代码;4、编写删除代码;5、编写响应代码。
48、如何添加子菜单?
Menu,add,子菜单项,:子菜单变量如:menu,mymenu,add,常用程序,:amenu
49、用来在弹出式菜单中显示一条分割线的代码是?
Menu, MenuName, add
50、正常菜单项的代码是?
Menu, MenuName, add,菜单项,响应标志
51、响应代码的格式是?
响应标志:
响应代码……
return
52、形如:Menu,amenu,add,txt 的代码是什么意思?
它其实是Menu,amenu,add,txt,txt 的一种简写,代表菜单项和响应标志
53、在指定坐标处显示菜单项的代码是?
Menu,MenuName,show,X坐标,Y坐标 如省略坐标,则在鼠标当前位置显示菜单项。
54、如何指定显示菜单的背景色?
Menu,MenuName,color,颜色代码
55、删除所有菜单的代码是什么?有什么作用?
Menu,mymenu,deleteall
如果没有这句代码,代码中分割线会在菜单重显时翻倍。如无分割线,可免
56、重启、关闭、注销系统的指令是什么?
Shutdown 后跟0为注销1为关机,2为重启,4为强制关闭 8为关闭关闭电源
57、隐藏脚本图标有几种方法?
Menu , tray , noicon 或者#NoTrayIcon
58、显示脚本图标的代码?
Menu , tray , icon
59、脚本图标的气泡提示如何显示?
Menu , tray , tip , 我自己的脚本
60、如何设置脚本的图标?
Menu , tray , icon , %systemroot%\system32\shell32.dll ,22或者Menu , tray , icon , e:\ico\1.ico
61、如何利用快捷键退出脚本?
#x::ExitApp
62、如何移除托盘图标的所有标准菜单项?
Menu , tray , NoStandard

63、如何插入托盘图标菜单?
Menu , tray , add , myitem
64、如何显示托盘图标的标准菜单项?
Menu , tray , standard
65、在托盘图标显示一个二级菜单代码如何表示?
menu,mymenu,add,boke
menu,mymenu,add,luntan
menu,tray,add,相关网站,:mymenu
Return 注意:响应标志后是 :不是::
66、如何使非热键类的脚本持久运行?
#Persistent
67、显示一个标准的文件选择对话框的代码为?
FileSelectFile, OutputVar [, Options, RootDir, Prompt, Filter]
Option 参数如被省略,则为单选。M 多选,M后允许随意跟随一个数字。S: 显示保存对话框。数字为一般为3,即文件及路径必须存在。Filter 参数格式如:Example: Audio (*.wav; *.mp2; *.mp3)
68、FileSelectFile命令如用户选择了多个文件,输出变量如何分割?
输出变量用(`n)字符分割。第一个参数为路径,随后紧跟选定的各个文件名。
if files =
{
MsgBox, 用户放弃选择。
}
Loop, parse, files, `n
{
if a_index = 1
mypath = %A_LoopField%
else
{
runwait, %mypath%\%A_LoopField%
}
}
return
69、运行并等待程序完成的指令是?
runwait
70、从某字符串中逐次分解出一个个的子串。
Loop, Parse , 某字符串 , 用于分割的特征变量
71、用对话框显示音量递增10%?
MsgBox, 音量递增10`%
72、使静音或者取消静音状态?
SoundSet, +1, , mute 或者Send,{Volume_Mute}
73、使主音量提升(降低)10%?
SoundSet ,+(-)10, MASTER
74、使音量中波形音量提升(降低)10%?
SoundSet , +(-)10, WAVE
75、#NoEnv 作用是什么?
主要是为了与以后兼容,也可以改善性能
76、如何对某些多媒体功能键重定义?
建个脚本内容为#InstallKeybdHook并执行,双击图标,窗口中选择菜单 view 第四项 key 开头的然后按多媒体键,f5刷新,看第二列内容比如a,显示的值是01e 。使用时就是sc01e::msgbox
77、显示器的宽度和高度用那两个常数表示?
A_ScreenWidth 和 A_ScreenHeight
78、如何移动鼠标光标?
MouseMove, X, Y [, Speed, R] Speed 参数的范围在 0 (最快) to 100 (最慢)之间取值.
R 参数 如果有此参数,则鼠标移动将以当前位置为基准,进行 x 和 y 像素的位移.
79、如何获取当前鼠标坐标?
MouseGetPos, X, Y
80、MouseGetPos还有什么作用?
MouseGetPos 指令用来返回鼠标的当前位置,以及鼠标当前悬停的窗口和控件。格式为:
MouseGetPos, [OutputVarX, OutputVarY, OutputVarWin, OutputVarControl
OutputVarWin 这个变量存储了当前鼠标悬停的窗口的ID ,即使窗口未处于激活状态。OutputVarControl 这个变量存储了当前鼠标悬停的控件的名称(ClassNN)。
81、MouseGetPos 命令在最后有1、2、3参数是什么意思?
1:在多文档界面程序(例如 SysEdit 或 TextPadThis )中可以准确的获取激活或置顶的子窗口信息。对其它情况就不那么准确了。2 :在 OutputVarControl 中存储 control's HWND/窗口句柄。3 :同时包含 1 和 2 的功能。
82、如何在屏幕上建立一个紧随鼠标的弹出式窗口?
ToolTip [, Text, X, Y]
83、如何获得当前位置像素的颜色RGB的值?
PixelGetColor, OutputVar, X, Y [, RGB]
84、如何在系统托盘建立一个弹出式窗口?
TrayTip [, Title, Text, Seconds, Options] 注意senconds的取值一般在10到30之间
85、如何用指定时间间隔自动或重复激发某子程序?
SetTimer , Label , 时间间隔
86、关闭某进程的代码是?
Process,close,autohotkey.exe
87、如何检索鼠标所在位置的信息?
MouseGetPos , , , id , control ; 得到鼠标所在位置窗口的id及控件名称
WinGetTitle, title, ahk_id %id% ;检索到相应id对应窗口的标题
WinGetClass, class, ahk_id %id% ;检索到相应id对应窗口的类名
88、如何得到当天的日期?
dd = %A_YYYY%年%A_MM%月%A_DD%日
89、如何给剪贴板赋值?
clipboard = %var%
90、当前桌面那个常数表示?
%A_Desktop%
91、如何设置鼠标速度?
SetDefaultMouseSpeed 30; 默认的是2 ,可以从0调到100,越小越快
92、如何设置鼠标动作结束之后的延迟?
SetMouseDelay 30; 默认是10毫秒
93、如何实现ctrl+鼠标双击?
send ^{click 100,200 2}
94、使滚轮向上滚2次?
click wheelup 2 ; wheelup 可以写成WU
95、滚轮向下滚一次?
click wheeldown ;wheeldown 可以简化为WD
96、使鼠标滚轮向下滚5次有几种方法?
Click , WD,5或
loop 5
click WD
97、当前位置鼠标单击左键
click ;
right 右键,left左键(默认可省略),middle 中键
98、当前位置鼠标双击左键?
click 2
99、移动鼠标到30,40?
click 30 , 40 , 0
100、点击鼠标右键的代码如何编写?
Click , 100 , 200 , right
101、如果鼠标点击的坐标是动态的如何表达?
Click %x% %y%
102、拖动某图标?
click 30,40 down
click 50,60 up
103、如何设置一个进度条?
Progress , %i% , 进度条下文本 , 进度条上文本 , 标题
104、如何创建快捷方式?
FileCreateShortcut, 目标文件, 快捷方式名 [, 工作目录, 用空格分开的参数, 描述, 图标, 快捷键, 图标标号, 运行状态]
注意:只需指定单个按键,所有的快捷键被指定为 CTRL+ALT + 快捷键,如果给该参数指定 B,将会产生一个 CTRL-ALT-B 的快捷键。运行状态1 - 正常(默认)3 - 最大化7 - 最小化
105、空格键
{space}
106、向左的箭头
{Left}
107、ctrl键?
^
108、alt键?
!
109、回车?
{enter}
110、小键盘的1?
Numpad1
111、Windows键?
#
112、Shift键?
+
113、逻辑与,把多个按键或鼠标组合成一个自定义键?
&
114、一对按键中的左边的按键?
< 如115、一对按键中的右边的按键?
>
116、通配符,表示热键被按下时,即使同时还有其他的按键按下,依然有效?
*
117、热键被触发后,动作不会被阻塞?
~
118、禁用某键?
在::后紧跟return即可。如:RWin::return
119、按键重映射?
a::b 如:RAlt::Rbutton 右alt映射为鼠标右键Lbutton::return 鼠标左键被屏蔽
120、仅在notepad中交换a、b键?
#IfWinActive ahk_class Notepad
a::b ;
#IfWinActive ;
121、鼠标定义?
LButton RButton MButton WheelDown WheelUp
122、键盘定义?
Space Tab Enter (or Return) Escape (or Esc) Backspace (or BS) Delete (or Del) Insert (or Ins) Home End PgUp PgDn Up Down Left Right ScrollLock CapsLock NumLock Numlock ON Numlock OFF Numpad0 NumpadPgUp NumpadDot (.) NumpadDel NumpadDiv (/) NumpadDiv (/) NumpadMult (*) NumpadMult (*) NumpadAdd (+) NumpadAdd (+) NumpadSub (-) NumpadSub (-) NumpadEnter NumpadEnter 1 a A LWin Control (or Ctrl) Alt Shift F1 PrintScreen CtrlBreak Pause Break
123、注释
;跟在需注释的语句之后,注意语句和分号之间至少要有一个空格。如果要注释多个语句可以使用/* */
124、函数?
Add(x, y)
{
return x + y ; "Return" expects an expression.
}
函数调用时,变量的后面使用 := 例如:Var := Add(2, 3) ;
由于一个函数调用就是一个表达式,所以函数的参数列表里面的变量前面不能加百分号“%”,相反的,字符串要使用引号。当一个命令的参数调用函数的时候,如果命令的参数不支持表达式,就必须使用百分号“%”,如:
MsgBox % "The answer is: " Add(3, 2)
125、如果要想在热字符串替换后加上空格?
在语句的后边加上 `,:*:btw::By the way `
126、弹出光驱
Drive, Eject,, 0
127、收回光驱
Drive, Eject,, 1
128、使窗口透明,值在1-255之间?
WinSet, Transparent, 100,我的电脑
129、创建或更新启动界面?
SplashImage,d:\1.jpg,b fs18
Sleep,5000
b fs 18 可以去除界面边框。原因不明
130、修改脚本之后,按住左键后按R重新载入脚本
~LButton & r::
reload
return
131、打开/激活记事本
IfWinExist 无标题 - 记事本
{
WinActivate
}
Else
{
Run Notepad
WinWait 无标题 - 记事本
WinActivate
}
132、隐藏任务栏 WinHide ahk_class Shell_TrayWnd
显示任务栏 WinShow ahk_class Shell_TrayWnd
133、如何强制单线程?
#SingleInstance force
134、屏蔽Ctrl+Alt+Del?
Regwrite,REG_SZ,HKEY_LOCAL_MACHINE,SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe, Debugger, Hotkey Disabled
解除屏蔽 RegDelete,HKLM,SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\taskmgr.exe

135、双击启动A程序.再双击,自动关闭A程序.
ifexist, 程序窗口标题
winkill, 程序窗口标题
else
run, 程序文件路径
return
136、当前窗口始终置于顶层?
WinSet AlwaysOnTop, on, A
137、关闭当前窗口置于顶层状态?
WinSet AlwaysOnTop, off, A
138、窗口还可以用表示?
ahk_class class、ahk_pid %PID%、ahk_id %ID%
139、音乐播放或暂停?
Send , {Media_Play_Pause}
140、音乐播放停止?
Send , {Media_Stop}
141、播放上一首?
Send , {Media_Prev}
142、播放下一首?
Send , {Media_Next}
143、按下浏览器回退键?
Send , {BROWSER_BACK}
144、按下浏览器 向前键?
Send , {BROWSER_FORWARD}
145、按下浏览器刷新键?
Send , {BROWSER_REFRESH}
146、按下浏览器停止键?
Send , {BROWSER_STOP}
147、按下浏览器搜索键?
Send , {BROWSER_SEARCH}
148、按下浏览器收藏键?
Send , {BROWSER_FAVORITES}
149、按下浏览器主页键?
Send , {BROWSER_HOME}
150、使静音?
Send , {VOLUME_MUTE}

推荐一款极其强大的软件 - AutoHotkey

AutoHotkey 这是一个很神奇的工具。是的,只能用神奇来形容。他的功能类似于按键精灵,可以帮助你完成一系列的动作。或许你已经用过按键精灵,但是在此之前你不会想到 AHK是多么的强大。你可以让他帮助你迅速打开常去的几个网站并且登陆邮箱,或者迅速的运行那些藏的很深的程序,你可以用他来做网游的挂机程序,也可以来做格斗游戏中连续技的脚本。只有你有足够的想象力,没有什么是AHK做不到的。与之类似的还有一个AtuoIt3,不过他的代码看起来要郁闷一些,相比之下我还是更喜欢AHK。
如你所见,AHK的所有工作都是通过你所写的脚本来完成的。而这个脚本实际上就是一个普通的文本文件,AHK按照你所写的顺序一行一行的执行下去。写代码确实令人有些头痛,所以很多人对这个软件都望而却步。但是假如你愿意深入的研究一下,就会发现他的代码实际上非常简单易学。与之相对的是,软件自带了非常强大的帮助文件,可惜是英文的。所以我就写了这样一篇文章,希望可以让大家对AHK产生兴趣^_^

首先是转自__ Scripting Life __的官方文档翻译
介绍
AutoHotkey 是一款免费的、Windows平台下开放源代码的热键脚本语言。有了它,您就可以:

通过发送键盘或鼠标的键击动作命令来实现几乎所有操作的自动化。您可以自己编写 宏 或者使用宏录制器来生成;
为键盘,游戏操纵杆和鼠标创建 热键。 事实上任何的按键、按钮或组合键都可以被设置为热键;
当键入您自定义的缩写时可以 扩展缩写。例如, 键入“btw”就可以自动扩展为“by the way”;
创建自定义的数据输入表格、用户界面、菜单等。详情请看 图形界面 部分;
映射 键盘、游戏操纵杆和鼠标上的按键或按钮;
运行现有的AutoIt v2脚本 并用 新功能 来增强它们;
将脚本文件 编译 成EXE可执行文件,使得程序在没有安装AutoHotkey的机器上得以运行;
借助 相关工具 以实现更强大的功能。
更多关于热键的说明
AutoHotkey 可令键盘、游戏操纵杆和鼠标的所有潜能都发挥出来。例如,除了典型的Control、Alt和Shift键之外,你还可以定义Win键(徽标键)和 Capslock键作为调节键。 而实际上你还可以设置任意的键盘按键或鼠标按键作为调节键。详细内容请看 高级热键 部分。
其他功能

调节音量、设置静音,以及其他 关于声卡的设置;
令指定窗口变为 透明,顶层窗口,或改变 外形;
用 游戏操纵杆 或 键盘 代替鼠标;
监控 您的系统。例如,当您不希望见到的窗口出现时自动关闭它们;
获取并改变 剪贴板的内容, 包括从资源管理器中复制的文件的名称;
关闭或屏蔽 Windows自有的快捷键,例如Win+E和Win+R;
取替Alt-Tab (通过按键、鼠标滚轮或按钮)从而减轻RSI(肢体重复性劳损 );
使用您喜欢的图标、工具提示(ToolTip)、菜单项目和子菜单来自定义 托盘图标菜单 ;
显示 对话框、工具提示(Tooltips)、气球提示 以及 弹出菜单,与用户交互;
执行脚本动作来 响应 系统关闭或注销;
检测系统 空闲时间。例如, 在系统空闲的时候执行高CPU负荷的任务;
通过检测 图像 和 象素色彩来自动执行游戏中的动作;
读取、写入 以及 解释 文本文件比起其他语言更容易;
针对一组与 通配符 匹配的文件进行操作;
可对 注册表 和 INI文件 进行操作。


搞定以后就可以开始写脚本了。任何地方点击右键-新建,选择AutoHotkeyScript(AHK脚本),当然你也可以直接在文本编辑器里写好以后另存为.ahk文件。AutoHotkey会在关联后执行这些.ahk的文件,当然你也可以转换成exe的可执行文件(附带有此工具)。


这里开始,是我的一个示例脚本。平时我需要经常运行很多软件,很麻烦,所以就写了这样一个程序。
首先,我喜欢在上网的同时听歌,平时比较喜欢用foobar2000,所以打算在按下Win+1键时开始用它听歌;
其次,玩游戏的时候经常配合gapa这个颜色优化工具,他的效果非常好,不过每次都要按F4更改配色方案,现在我希望按一下Win+2就可以自动帮我完成;
然后,上网时候经常有需要记住的东西,所以我希望在按Win+3之后他可以把我当前复制的东西保存下来,也就是粘贴到记事本里去;
最后,这个程序可以方便的退出。
知道了要求之后,我们就来看看写出的脚本吧:

Copy code
;这里是我的一个示例脚本
#InstallKeybdHook ;安装键盘钩子,不过其实这里可以不装
msgbox, 这是我的常用软件运行脚本 `n按Win+1运行Foobar2000,并且开始播放 `n按Win+2运行gapa色彩优化工具,并且设置配色方案 `n按Win+3运行将当前复制的内容粘贴入记事本 `n按Win+X退出 ;只是一个弹出的对话框,`n是换行

#1:: ;按下Win+1键
Run, D:foobar2000foobar2000.exe, D:foobar2000 ;运行我的Foobar2000,为了防止出现意外把他设置为所在目录启动,以下同
sleep, 500 ;还是稍微等一下吧,等程序运行
send, !p ;按Alt+P,开始播放的快捷键
return ;返回

#2:: ;这是Win+2
Run, %userprofile%Desktop色彩优化工具gapa.exe, %userprofile%Desktop色彩优化工具 ;执行gapa,在桌面上
sleep, 500 ;再等待一下
send {F4} ;按下F4修改配色方案
return ;返回

#3:: ;这是Win+3
IfWinExist, Untitled - Notepad ;检测窗口是否存在,注意我的英文版xp记事本标题为Untitled - Notepad,如果你是中文版的话,应该为 未命名 - 记事本,下同
{
WinActivate ;如果存在就激活他
}
else ;否则
{
Run, Notepad ;运行notepad
WinWait, Untitled - Notepad ;等一下
WinActivate ;然后激活窗口
}
sleep, 500 ;继续等一下
send, {enter}{enter}^v ;加入两个回车(空行),然后按ctrl+v粘贴
return ;返回

#x:: ;这是Win+X
MsgBox, 4,, 你确定要退出吗 ;弹对话框,4,,是加入了Yes/No的选择
IfMsgBox Yes ;如果点了是
ExitApp ;退出当前脚本
else ;否则
MsgBox 程序还在运行中 ;弹出对话框
return ;返回


;号后面都是我的注释,每一行都做了注释,应该还能看懂吧^_^。他整体代码的感觉比较类似于JS,不是很难。更详细的说明都可以在帮助文件中找到。
另外,gapa这个程序我在每次退出时,都要按F6键返回默认配色,否则桌面时比较难受。所以可以继续做出如下修改:


#4:: ;按Win+4
send {F6} ;按下F6恢复默认配色
process, close, gapa.exe ;结束进程gapa.exe

星期二, 五月 12, 2009

姚明、李开复:30幅画 读懂灾区孩子的内心

孩子们总在用各种方式让我们惊讶与感动,尤其是在一年后,当我们重新面对5•12这个沉重的日子时,更是如此。
大地震后,上海的美术老师张贵和奔赴灾区支教。期间,他和其他老师组织孩子们用画笔来表达内心,用色彩和线条记录情感。于是,就有了现在大家看到的这 30 幅稚嫩却充满真情的画作

姚基金与谷歌中国的很多同事们,都看到了这些作品,并无一例外地被深深触动。是的,只有灾区的孩子们,才会画出如此“雷同”的题材——所有的画作都与地震、与灾难相关。而且,这些画作中经常出现的冷峻色彩、沉甸甸的笔触、呼之欲出的思念,恐怕也绝少在普通孩子的绘画作品中出现。

在其中的一幅画作里,辛勤的鸟妈妈为巢里的鸟儿带回食物,大树下,想念妈妈的孩子淌着泪水仰望树梢,画面里写满了“妈妈”、“妈妈”的字样……



真的只有沉重与悲伤吗?我们被这群童真的孩子引领着,还看到了感恩、希望、笑容,甚至与年龄不符的坚强。

在5•12一周年祭的时候,在全国各界纷纷用各自的方式缅怀逝者的时刻,我们很想通过自己的微薄努力,让更多的人看到这些画,去传递这份感动,去体会灾区孩子的内心。那些幼小的心灵,有阴霾需要去驱散,有伤口需要去抚平,却总是美丽真诚。

从今天起,在最为便捷的网络平台上,谷歌会把孩子们的画作直接展现在网友面前,并同时展开“让音乐传递你我祝福”的5•12专题纪念活动,请网友们为这些画作配上声音,用歌曲为孩子们提供祝福与安慰。姚基金则会把网友们挑选出的歌曲收录在音乐播放器中,转交给那些可爱的小作者们。

我们相信,当孩子们的画作和无数网友的祝福在网络上传播的时候,网络真正联结的应该是每一个人的心灵。作为大人,我们会记住我们的责任,我们会记住5•12的举国同悲,我们不会忘记那些逝去的同胞,我们不会忘记帮助灾区的孩子们坚强长大。5•12一周年祭的时候,我们愈发确认:泱泱中华,总能共渡时艰。

走进孩子们的心灵 了解画作详情:g.cn/music/512

姚明送给孩子们的画:

星期一, 五月 11, 2009

TheWorld中如何使用Tor做代理

  在TheWorld 2.0中,工具菜单-代理服务器-设置代理服务器
  然后在代理列表中输入如下内容
  127.0.0.1:9050@Socks#Tor
  然后点击应用
  
  然后在工具菜单-代理服务器菜单,会看到刚才输入的这条内容,勾选它
  
  运行Tor,当Tor初始化完毕后,TheWorld中的浏览就都用Tor做代理了
  

星期日, 五月 10, 2009

Mobile2.0 手持设备未来的未来:Push everything to the same destination

[kkk]

Mobile2.0 手持设备未来的未来:Push everything to the same destination

手机的未来,或者称为Mobile1.0,已无疑问:人手中的机器,围绕网络。

所以要随时在线,要更好的浏览器,要整合网络服务。

Apple,RIM,Palm,甚至Google,都在这条道上一路狂奔。

有悬念的是,未来的未来呢(Mobile2.0)?

我觉得至少应该做到下面两件事:
1 push everything
2 push to the same destination

1 Push everything

EVERYTHING!不仅仅是Email。

RSS有更新了?Push!
论坛参与的帖子有回复了?Push!
发的blog有评论了?Push!
Flickr上好友上新片子了?Push!

简而言之,所有你关注的地方,有更新,有Push。

重点在于EVERYTHING。全面的监视能带来令人享受的快感。类似用BB全面掌控你的对外联系(Pushmail+mms+sms+电话。不过IM仍然是是漏网之鱼)。所以,不用记住常去的论坛,不用隔一段时间去看看你发的帖子,你关注的一切,尽在掌握。

互联网的地图上,不再有迷雾。视力所及当然清晰,除此之外,是真正的漆黑。两可的“不知道”变成了确定的“没有”。因为你知道,如果发生了什么,一定有Push。

你将有更多的时间和经历去打打球,逗逗小孩,同时,更高效率的利用网络交流。

2 push to the same destination
其实,Push Everything现在就可以。RSS Reader算半个Push。(定期Pull更新回来)。Twitter/Facebook/MSN Space等等的客户端,也可以用类似的办法监视更新。
但手机后台同时开一大堆客户端(IM,Blog,micro Blog),每个软件单独Push,对稳定的威胁不论,用户本身的迷惑更加致命。

假设你关心Android新机有没有触摸屏,记得有关于他的消息Push到手机上了。闲下来要看看的时候,去UCweb找Push消息?或者Gravity?这种选择,对用户是无用的负担。讨论是在Twitter上或者ifanr的BBS上,本来不重要。

理想的情况下,所有Push过来的消息,都应该汇集到一个地方:Inbox。
他接收和管理所有Push消息。提供按时间,来源,keyword等元素过滤的功能。几个小时前收到的消息,那么往下翻翻。Push消息放的久了?那么搜一搜。
找到关于Android新机触屏讨论的更新消息,Inbox中点开好了。他去启动UCweb或者Gravity,无所谓。

从用户出发,关注内容的方式,对比一堆客户端,对于手机资源的浪费最小,学习成本最低,用户的时间和精力,全部集中于关注的内容上。

其实,上面的东西不是我的原创。多少年前,GTD这本书里面,就把这个概念阐述的非常清晰了。收集各种事情的Inbox。强调完整而集中。不是针对手机和网络而已。
在手机上,RIM中途半端的做了些,就已经让人爱不释手了。如果有厂家把Push Everything to the same destination实现,相信Mobile2.0将会到来:

人不再围绕网络。。。。


网络围绕人。


Mobile2.0

本主题由 streetsmart 于 2009-4-26 11:41 加入精华
收藏分享评分
http://twitter.com/newkhonsou
http://newkhonsou.spaces.live.com/

脚本分享:关闭新标签页的快速拨号

Chrome浏览器每次开启新标签页之后,都会出现类似于Opera的快速拨号功能,也就是显示你当前访问最多的网页,虽然这个功能很好,但是有一些用户却不喜欢,现在已经有了脚本可以实现关闭快速拨号的方法。当然,还是使用user scripts的方式

1、在Chrome.exe 快捷方式后面添加启动参数--enable-user-scripts,开启user script

2、在Chrome安装目录\user data\default\新建一个名为user scripts的文件夹

3、将以下代码保存为NoSpeedDial.user.js,放到user scripts文件夹内。

代码:
复制内容到剪贴板
代码:
// ==UserScript==
// @run-at document-start
// @name NoSpeedDial
// @author Charles
// @description Start Chrome and open each new tab with a clean blank page
// @namespace http://www.adsweep.org/NoSpeedDial.user.js
// @version 1.0
// ==/UserScript==

if(document.location.href=='chrome-ui://newtab/'){
var countTries=0;
function checkDOM(){
if(countTries<100){
if(document.getElementsByTagName("BODY")[0]){
document.getElementsByTagName("BODY")[0].style.display='none';
} else {
countTries++;
window.setTimeout(checkDOM,1);
}
}
}
checkDOM();
}

代码说明:
1、这个只是隐藏了BODY内的内容,不是彻底的办法。
2、15378以后版本需要修改“chrome-ui://”为“chrome://”方可。

彻底转移chrome用户目录

彻底转移chrome用户目录

chrome总是安装在C盘,用户目录也在C盘,但我喜欢把用户目录放在其他盘,看了网友贴的方法,在快捷方式里增加一个参数:
--User-data-dir=F:\\ChromeData
就可以把目录转移,但如果通过其他连接打开浏览器的时候,比如在QQ上点击邮箱的图标时,自动打开的Chrome的用户目录还在C盘,只有通过桌面上的快捷方式打开时,才会在F盘,为了解决这个问题,我修改了注册表,彻底转移了用户目录,把注册表文件贴给大家参考一下:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Applications\chrome.exe\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CLASSES_ROOT\ChromeHTML\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CLASSES_ROOT\ftp\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CLASSES_ROOT\http\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CLASSES_ROOT\https\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CURRENT_USER\Software\Classes\ChromeHTML\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CURRENT_USER\Software\Classes\ftp\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CURRENT_USER\Software\Classes\http\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_CURRENT_USER\Software\Classes\https\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Applications\chrome.exe\shell\open\command]
@="\"C:\\Documents and Settings\\xiaom\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe\" --User-data-dir=F:\\ChromeData -- \"%1\""

另存为REG文件导入即可,记得修改参数中的F:\\ChromeData和C:\\Documents and Settings\\xiaom\\改为你的实际目录



把注册表文件打包了UserData.rar,下载附件后,把REG文件的参数修改一下就可以导入了

星期五, 五月 08, 2009

NetEase Youdao Nanti PWND! [Spoiler]



06

Saw an interesting puzzle

, blogging about it in English is not zhuangbility, but to avoid n00b finding solutions too easily via search engines.

  1. Sniff
  2. No packets transfered when click 'submit', so all answers must be downloaded somewhere to client
  3. Firebug analyze URL requests & responses
  4. Decompiled all .swf files, nothing found
  5. Found it's using PHP-RPC
  6. Tried phprpc-python but didn't work
  7. Worked out puzzle 2. answer is bomb
  8. Dumped all browser memory
  9. Search for keyword bomb
  10. Got all answers to 15 questions
  11. Copy, paste & submit all the right answers, but nothing happend, so just blogging about it http://initiative.yo2.cn/archives/639837
  12. ???
  13. Profit!

有道居然用PHPRPC把所有答案加密了。哼哼。任何加密都挡不住内存dump大法。

So, here's the answers:

以下是剧透,丧失游戏乐趣后果自负,没有自己动脑筋思考的同学请不要手贱,报名参加真正的TopCoder比赛吧:http://www.youdao.com/nanti/apply.php




< ?xml version="1.0" encoding="utf-8" ?>
< radius="220" a="320" b="170" skey="youdao">
< src="questions/q1oioqzvveoalz.fa.swf" answer="o" lowcase="true" visible="true">一样的人物
< src="questions/q80afzfdqrezxc0-rwq.f0.swf" answer="bomb" lowcase="true" visible="true">湖边的回忆
< src="questions/q4098azvhlaql.f-fq53.swf" answer="0441" visible="true">危险之地
< src="questions/q3zlllweafl342laozl.swf" answer="@($" visible="true" locked="true">火星文
< src="questions/q5zpaqa.eop2-f-qe4.swf" answer="也可能" visible="true" locked="true">博客中的线索
< src="questions/q6pkltix.04.-af.swf" answer="本机地址" visible="true" locked="true">IT码农的留言
< src="questions/q70a9fdalqrexc65o.vz.swf" answer="search engine" lowcase="true" visible="true" locked="true">曲径通幽
< src="questions/q909qalzxovaltazt-fq.fq.swf" answer="为" visible="true" locked="true">手机词典的帮助
< src="questions/q10090zvalzp-f.4.swf" answer="3624087915" visible="true" locked="true">古诗中的数字
< src="questions/q2098alzraz.5.ao.swf" answer="12355331" visible="true" locked="true">彩铃包月
< src="questions/q1109zgflqre0f-aw.w2.swf" answer="2月18日||二月十八日" visible="true" locked="true">和智玲的聊天
< src="questions/q120z0fda2r.z0f-a2.swf" answer="cctv" lowcase="true" visible="true" locked="true">黑客是怎样炼成的
< src="questions/q13-zf0w2rzlf0.f43.swf" answer="圆周率||祖冲之" visible="true" open="15" locked="true">Morse的登录
< src="questions/q1409falz-fa.2aof.swf" answer="0731-5310163" visible="true" locked="true">错误的号码
< src="questions/q160z-af.4er0zafwe.swf" answer="LOVE" visible="true" locked="true">数学之美
< src="questions/q170z.gzzf-32zflgpqert.swf" answer="ONLMK" lowcase="true" visible="false">残破的画卷
< /data>



Seems that there's even a hidden puzzle:http://www.youdao.com/nanti/mi/questions/q170z.gzzf-32zflgpqert.swf but I haven't figured out how to invoke it (yet) . Perhaps using some .swf hook :D

转载请注明出处 http://initiative.yo2.cn/archives/639837

仔细看看,就会发现这样找漏洞很有意思

仔细看看,就会发现这样找漏洞很有意思
【调试环境】:Win2000、Ollydbg1.10、ExeScope

【逆向原因】:我有一舍友,十分精于扫雷游戏,闲暇之时与其比试,总是败多胜少,遂逆

向一热键作弊(本文改成了菜单)功能,后常胜 ^_^ 。今日无事,逆向菜

文一篇,希望能对刚入门的菜鸟朋友们有些许帮助。

【逆向过程】:

————————————————————————————————————————
一、分析

1、弄清程序的流程,将自己的处理代码,挂在消息响应流程中。

// 用DefWindowProcW下断,然后向上翻看,即可看到消息响应流程

// 点击菜单后,会来到如下代码处

010019AD > \8B4D 10 mov ecx, dword ptr ss:[ebp+10] ; 点击的菜单ID
010019B0 . 0FB7C1 movzx eax, cx
010019B3 . 3D 0B020000 cmp eax, 20B
010019B8 . 7F 76 jg short winmine.01001A30 ; 大于 0x20B 跳到后边
010019BA . 3D 09020000 cmp eax, 209
010019BF . 7D 2D jge short winmine.010019EE
010019C1 . 2D FE010000 sub eax, 1FE
010019C6 . 74 1C je short winmine.010019E4
010019C8 . 48 dec eax
010019C9 . 48 dec eax
010019CA . 0F85 7C030000 jnz winmine.01001D4C
010019D0 . 33FF xor edi, edi ; Case 200 of switch 010019B3
010019D2 . 57 push edi ; /ShowState => SW_HIDE
010019D3 . FF35 A8520001 push dword ptr ds:[10052A8] ; |hWnd = NULL
010019D9 . FF15 EC100001 call dword ptr ds:[<&USER32.ShowWi>; \ShowWindow
010019DF . E9 A2000000 jmp winmine.01001A86
010019E4 > E8 26190000 call winmine.0100330F ; Case 1FE of switch 010019B3
010019E9 . E9 5E030000 jmp winmine.01001D4C
010019EE > 8D81 F7FDFFFF lea eax, dword ptr ds:[ecx-209] ; Cases 209,20A,20B of switch 010019B3
010019F4 . 66:A3 C0520001 mov word ptr ds:[10052C0], ax
010019FA . 0FB7C0 movzx eax, ax
010019FD . 8D0440 lea eax, dword ptr ds:[eax+eax*2]
01001A00 . C1E0 02 shl eax, 2
01001A03 . 8B88 28500001 mov ecx, dword ptr ds:[eax+100502>
01001A09 . 890D C4520001 mov dword ptr ds:[10052C4], ecx
01001A0F . 8B88 2C500001 mov ecx, dword ptr ds:[eax+100502>
01001A15 . 8B80 30500001 mov eax, dword ptr ds:[eax+100503>
01001A1B . 890D C8520001 mov dword ptr ds:[10052C8], ecx
01001A21 . A3 CC520001 mov dword ptr ds:[10052CC], eax
01001A26 . E8 E4180000 call winmine.0100330F
01001A30 > \B9 4E020000 mov ecx, 24E ;大于 0x20B 跳到这里
01001A35 > 3BC1 cmp eax, ecx
01001A37 . 0F8F D3000000 jg winmine.01001B10
01001A3D . 0F84 C7000000 je winmine.01001B0A
01001A43 . 2D 0C020000 sub eax, 20C

根据上边的代码,可知,只要从01001A30处(其它地方也可以)跳到我们添加的代码中,就可以对消息进行响应了。

2、如何实现作弊?

// 开局,点击一下,到如下代码处,(第一次用PlaySoundW下断,很容易就能来到这里)

01003772 |. 833D F4560001 00 cmp dword ptr ds:[10056F4], 0 ; //开始扫雷了么?
01003779 |. 6A 01 push 1
0100377B |. 5B pop ebx
0100377C |. 75 4A jnz short winmine.010037C8 ; 第一点击?
0100377E |. 833D F0560001 00 cmp dword ptr ds:[10056F0], 0
01003785 |. 75 41 jnz short winmine.010037C8
01003787 |. 53 push ebx
01003788 |. E8 ED050000 call winmine.01003D7A
0100378D |. FF05 F0560001 inc dword ptr ds:[10056F0]
01003793 |. E8 1FF4FFFF call winmine.01002BB7
01003798 |. 6A 00 push 0 ; /Timerproc = NULL
0100379A |. 68 E8030000 push 3E8 ; |Timeout = 1000. ms
0100379F |. 53 push ebx ; |TimerID
010037A0 |. 891D 84510001 mov dword ptr ds:[1005184], ebx ; |
010037A6 |. FF35 A8520001 push dword ptr ds:[10052A8] ; |hWnd = NULL
010037AC |. FF15 6C110001 call dword ptr ds:[<&USER32.SetTim>; \ 设置计时间隔1秒,修改这里进行时间作弊


// 通过跟踪发现,对相应行、列对应的内存中的一个字节进行变换,变换后的值高位为1(0x80),
// 表示该位置是雷,GAME OVER, 如果不是雷,则显示该位置的数字

01003144 /$ 8B4424 08 mov eax, dword ptr ss:[esp+8] ; 行
01003148 |. 53 push ebx
01003149 |. 55 push ebp
0100314A |. 8BC8 mov ecx, eax
0100314C |. 56 push esi
0100314D |. 8B7424 10 mov esi, dword ptr ss:[esp+10] ; 列
01003151 |. C1E1 05 shl ecx, 5
01003154 |. F68431 00570001 80 test byte ptr ds:[ecx+esi+1005700], 80 ; 是雷么
0100315C |. 8D9431 00570001 lea edx, dword ptr ds:[ecx+esi+1005700]
01003163 |. 57 push edi
01003164 |. 74 6B je short winmine.010031D1 ; 不是雷,跳
01003166 |. 833D F4560001 00 cmp dword ptr ds:[10056F4], 0
0100316D |. 75 55 jnz short winmine.010031C4
0100316F |. 8B2D 685A0001 mov ebp, dword ptr ds:[1005A68]
01003175 |. 6A 01 push 1
01003177 |. 58 pop eax
01003178 |. 3BE8 cmp ebp, eax
0100317A |. 7E 70 jle short winmine.010031EC
0100317C |. 8B1D F8560001 mov ebx, dword ptr ds:[10056F8]
01003182 |. BF 20570001 mov edi, winmine.01005720
01003187 |> 6A 01 /push 1
01003189 |. 59 |pop ecx
0100318A |. 3BD9 |cmp ebx, ecx
0100318C |. 7E 0B |jle short winmine.01003199
0100318E |> F60439 80 |/test byte ptr ds:[ecx+edi], 80
01003192 |. 74 0F ||je short winmine.010031A3
01003194 |. 41 ||inc ecx
01003195 |. 3BCB ||cmp ecx, ebx
01003197 |.^ 7C F5 |\jl short winmine.0100318E
01003199 |> 40 |inc eax
0100319A |. 83C7 20 |add edi, 20
0100319D |. 3BC5 |cmp eax, ebp
0100319F |.^ 7C E6 \jl short winmine.01003187
010031A1 |. EB 49 jmp short winmine.010031EC
010031A3 |> FF7424 18 push dword ptr ss:[esp+18] ; /Arg2
010031A7 |. C602 0F mov byte ptr ds:[edx], 0F ; |
010031AA |. C1E0 05 shl eax, 5 ; |
010031AD |. 56 push esi ; |Arg1
010031AE |. 808C08 00570001 80 or byte ptr ds:[eax+ecx+1005700], 80 ; |
010031B6 |. 8D8408 00570001 lea eax, dword ptr ds:[eax+ecx+1005700] ; |
010031BD |. E8 EDFEFFFF call winmine.010030AF ; \winmine.010030AF
010031C2 |. EB 28 jmp short winmine.010031EC
010031C4 |> 6A 4C push 4C
010031C6 |. 50 push eax
010031C7 |. 56 push esi
010031C8 |. E8 53FCFFFF call winmine.01002E20
010031CD |. 6A 00 push 0
010031CF |. EB 16 jmp short winmine.010031E7
// 不是雷跳到这里
010031D1 |> 50 push eax ; 行
010031D2 |. 56 push esi ; 列
010031D3 |. E8 D7FEFFFF call winmine.010030AF ; 显示该位置的数字
010031D8 |. A1 F4560001 mov eax, dword ptr ds:[10056F4] ;
010031DD |. 3B05 FC560001 cmp eax, dword ptr ds:[10056FC]
010031E3 |. 75 07 jnz short winmine.010031EC ; 雷扫完了么
010031E5 |. 6A 01 push 1
010031E7 |> E8 86FDFFFF call winmine.01002F72 ; 游戏成功结束
010031EC |> 5F pop edi ; 没扫完,继续
010031ED |. 5E pop esi
010031EE |. 5D pop ebp
010031EF |. 5B pop ebx
010031F0 \. C2 0800 retn 8

//根据行、列,调用下面过程计算,内存中的值如果高位是1(0x80)就是雷
010033E9 /$ 8B4424 08 mov eax, dword ptr ss:[esp+8] //行
010033ED |. 8B4C24 04 mov ecx, dword ptr ss:[esp+4] //列
010033F1 |. C1E0 05 shl eax, 5
010033F4 |. 8D9408 00570001 lea edx, dword ptr ds:[eax+ecx+1005700] //位置
010033FB |. 8A8408 00570001 mov al, byte ptr ds:[eax+ecx+1005700]
01003402 |. 8AC8 mov cl, al
01003404 |. 83E1 1F and ecx, 1F
01003407 |. 83F9 0D cmp ecx, 0D
0100340A |. 75 05 jnz short winmine.01003411
0100340C |. 6A 09 push 9
0100340E |. 59 pop ecx
0100340F |. EB 07 jmp short winmine.01003418
01003411 |> 83F9 0F cmp ecx, 0F
01003414 |. 75 02 jnz short winmine.01003418
01003416 |. 33C9 xor ecx, ecx
01003418 |> 24 E0 and al, 0E0
0100341A |. 0AC1 or al, cl
0100341C |. 8802 mov byte ptr ds:[edx], al
0100341E \. C2 0800 retn 8

//根据行、列,将上面过程变换后内存中的值恢复
01003421 /$ 8B4424 08 mov eax, dword ptr ss:[esp+8]
01003425 |. 8B4C24 04 mov ecx, dword ptr ss:[esp+4]
01003429 |. C1E0 05 shl eax, 5
0100342C |. 8D9408 00570001 lea edx, dword ptr ds:[eax+ecx+1005700]
01003433 |. 8A8408 00570001 mov al, byte ptr ds:[eax+ecx+1005700]
0100343A |. 8AC8 mov cl, al
0100343C |. 83E1 1F and ecx, 1F
0100343F |. 83F9 09 cmp ecx, 9
01003442 |. 75 04 jnz short winmine.01003448
01003444 |. 6A 0D push 0D
01003446 |. EB 06 jmp short winmine.0100344E
01003448 |> 85C9 test ecx, ecx
0100344A |. 75 03 jnz short winmine.0100344F
0100344C |. 6A 0F push 0F
0100344E |> 59 pop ecx
0100344F |> 24 E0 and al, 0E0
01003451 |. 0AC1 or al, cl
01003453 |. 8802 mov byte ptr ds:[edx], al
01003455 \. C2 0800 retn 8

————————————————————————————————————————
二、逆向

1、用ExeScope为Winmine.exe添加一个菜单项:ID=0x259=601(大于0x20B),Name="过关"。

2、在Winmine.exe的消息循环中作如下修改,使自己的添加的菜单选项能够被响应。

01001A30 > \B9 4E020000 mov ecx, 24E
01001A35 . 3BC1 cmp eax, ecx
==〉 修改成
01001A30 /E9 4B2F0000 jmp winmine.01004980 //跳转到自己的处理代码处
01001A35 . |3BC1 cmp eax, ecx

3、在01004980处写入如下代码:

下面的代码是在Winmine.exe中找了一段全为零的地方写入的,也可以添加一个节后写入节中。

01004980 3D 58020000 cmp eax, 258 ;
01004985 7F 0A jg short winmine.01004991 ; > 0x258
01004987 B9 4E020000 mov ecx, 24E ; 恢复原来的代码
0100498C ^ E9 A4D0FFFF jmp winmine.01001A35 ; 跳回
01004991 3D 59020000 cmp eax, 259 ; 是否选择了“过关”
01004996 7F 40 jg short winmine.010049D8 ; > 0x259 ?跳 继续处理其它
// 下面是过关代码
01004998 B8 01000000 mov eax, 1 ; 双重循环遍历;
0100499D B9 01000000 mov ecx, 1
010049A2 50 push eax
010049A3 51 push ecx
010049A4 50 push eax
010049A5 51 push ecx
010049A6 50 push eax
010049A7 51 push ecx
010049A8 E8 3CEAFFFF call winmine.010033E9 ; 变换
010049AD A8 80 test al, 80 ; 是否为雷
010049AF 75 07 jnz short winmine.010049B8 ;
010049B1 E8 F9E6FFFF call winmine.010030AF ; 不是雷,点开
010049B6 EB 05 jmp short winmine.010049BD
010049B8 E8 64EAFFFF call winmine.01003421 ; 是雷,恢复
010049BD 59 pop ecx
010049BE 58 pop eax
010049BF 41 inc ecx ; 列数增1
010049C0 3B0D CC520001 cmp ecx, dword ptr ds:[10052CC] ; 遍历完该行的所有列?
010049C6 ^ 7E DA jle short winmine.010049A2
010049C8 40 inc eax ; 行数增1
010049C9 3B05 C8520001 cmp eax, dword ptr ds:[10052C8] ; 遍历完所有行?
010049CF ^ 7E CC jle short winmine.0100499D
010049D1 6A 01 push 1
010049D3 E8 9AE5FFFF call winmine.01002F72 ; 游戏过关
010049D8 ^ E9 58D3FFFF jmp winmine.01001D35 ; 返回到原来的消息循环结束处

在OD中写入代码后,复制到可执行文件,保存即可!

4、运行Winmine.exe,选择菜单中的“过关”即可!

扫雷秘技

扫雷秘技
  以前经常和同学比赛扫雷,很是具有挑战性,相信很多人都玩过吧。我第一次和同学玩扫雷的时候,简直把高级当成了不可能完成的任务,分析半天,还是出错。今天一个偶然的机会发现了这个秘技,挺好玩的,放在这里吧



今天要给大家介绍一个超级厉害的破解方法,所有版本的都适用,方法如下:

1、启动“扫雷”游戏,在游戏界面上输入:"xyzzy"5个字母,然后按shift键,就启动了破解;

2、鼠标在“扫描”上的方格上移动,注意看你的“扫雷”界面最左上角(注:有的,一般是NT内核的操作系统话,比如win xp,是在“桌面”的最左上角,而不是游戏界面)一旦出现个“黑点”,就表明当前方格下是地雷。如果没出现个黑点的话,说明就是安全的,你就放心的鼠标左键按下去吧!

PS. 连这样一个小游戏微软都留了后门,由此推知……大家还是好自为之。

  我的电脑和上面的描述有点儿差别,当启动作弊之后,是在电脑屏幕的左上角出现了一个白点儿,当有这个白点的时候,就是安全的,如果没有,呵呵,鼠标下面就是地雷。

  快点儿拿去骗不明所以的同事同学吧,呵呵,“神了”

浅谈脱壳中的Dump技术

标题:浅谈脱壳中的Dump技术
http://tech.ddvip.com/2007-01/116879008718099.html
作者:Lenus Margin
摘要:
本文首先指出了DUMP技术的原理,并尝试着编写一个实现于LordPE的基本功能的dump,并针对dump的编写一层一层的突出了dump过程中所注意的事项,对其加以不断的完善;讨论了一般的anti-dump的原理和解决的办法,用实践说明在脱壳中dump的技巧。最后比较了几种常用的dump工具的优劣。
关键词:
Dump,PEB,Anti-dump,OEP
Abstract:
This paper first point out the principle of the dump skills, and try to write a program to achieve the fundamental function which the LordPE’s dump can do. And pointing to the attention of the writing, I improve my dump program on and on. I try to discuss the normal principle of anti-dump and the way to solve it. Using the example to explain the dump skill in the process of unpacking. Finally , I compare some of usual dump tools.

Keywords:
Dump, PEB,Anti-dump,OEP






目录:
1.前言 (3)
2. 第一章:Dump的原理 (4)
2.1分析
2.2 实现
2.3试验
2.4小结
3. 第二章:Dump程序的改进(7)
3.1获取Imageofsize
3.2对齐节表
3.3小结
4. 第三章:Anti-dump的原理(10)
4.1纠正imagesize
4.2其他anti方式
4.3实战
4.4小结
5. 第四章:Dump的位置(21)
5.1在OEP处dump
5.2不在OEP处dump
5.3他山之石
5.4实战
5.4小结
6. 第五章:Dump工具的比较(29)
6.1比较
6.2小结
7. 结论(32)
谢辞 (32)
参考文献(32)
个人简介(32)














前言:
“知者不言,言者不知”
----老子 《道德经》
你会dump吗?
你还是只在OEP处dump吗?
你知道dump的原理吗?
你遇到过anti-dump的壳吗,你知道如何对付它吗?
你明白几种dump工具的优劣吗?
请原谅我,一开始就这么不识趣的抛出了这几个无里头的问题。我记得龙应台说过:正因为我那时什么都不懂,所以才会写下那些文字。我也是一个言者不知的人,这篇文章我将尝试的帮助大家去理解一个dump的原理和在脱壳中dump的技巧,如果这篇文章让你明白了一些东西,那么这篇文章的目的算是达到了。如果你是高手,也不用嘲笑我的无知,毕竟我们都是这么过来的,指出我的错误和不足是我更愿意看到的。
此篇文章共分为五章。在前三章中,我试图在阐释一些dump的原理,但这需要一些C语言和windows编程的功底。相信这些部分会让很多人头疼。在后面的两章也许你会更加感兴趣,在第四章中我举了三个例子进行说明dump的技巧,而在第五章中我还会针对几种常用的dump工具发表一下自己不成熟的看法。
好了,带着上面的问题,让我们开始我们的dump之旅。





















第一章. Dump的原理
Knowledge is a treasure, but practice is the key to it.
 
知识是一宝库,而实践就是开启宝库大门的钥匙。
——Fuller
如果说要讲dump的原理,那么我们最简单的办法就是自己写一个象LordPE那样的dump程序。下面我就来详细说明一下,一个dump程序是如何工作的,如何把内存中的数据保存到文件中的。

一.分析
对于dump来说,他的英文翻译就是“转存”。也就是说把内存中或者其他的输入转存到另一个位置,当然对于我们现在说的dump就是把内存中运行的PE进程的数据,从内存中抓取出来,然后在用文件的形式保存下来。

根据上面的分析我们基本上得到了一个这样的思维。Dump程序要做的事分几个基本的步骤:
1. 在系统中找到目标进程
2. 在进程中确定进程的大小imagesize
3. 把进程中的数据保存到文件

二.实现
好了,现在我们就可以具体来实现各个部分的功能了。

1. 在系统中找到目标进程就是实现一个基本的任务管理器的把进程列出的功能。
在这个实现中我们采用了BOOL GetProcessListFunc(HWND hDlg,HWND hWindList);这个函数里面调用了几个基本的函数:CreateToolhelp32Snapshot,Process32First, Process32Next
这个列出进程的功能就轻松的实现了。(具体的请看我的代码Lenus_dump_1.cpp)

2. 在进程中确定要dump的大小是一件很有意思的事情,我打算慢慢的讲解寻找这数据的办法,这一节我们先用一个基本的方法。
我们先用一个笨一点的办法,根据PE文件格式可以知道在距离PE文件头(IMAGE_NT_SIGNATURE)0x50的位置是存放imageofsize的地方。所以在int GetSizeOfImage(HWND hDlg,HANDLE hProcess);这个函数中,我们找到了进程以后打开目标进程,然后用ReadProcessMemory来读取imageofsize就可以了。
在这里我还用BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess);函数简单的检查了了两个PE文件的标志(IMAGE_DOS_SIGNATURE和IMAGE_NT_SIGNATURE)
3. 在把进程中的数据保存到硬盘的文件的步骤中,我使用了一个比较笨的办法。
首先,我们用LPCTSTR SaveAsFunc(HWND hDlg);函数把打开一个save as的通用控件,获得要保存的文件名,默认的是dumped.exe。接着,使用前面在获得imageofsize的同时已经用GlobalAlloc函数申请了同样大小的堆空间,把目标进程的数据读到这个空间里面。这里面就有一个空间大小的问题,如果进程过大,我怀疑会申请失败。在这里还有尤其的注意一个地方,就是我们在申请空间的时候用了sizeoffile而不是sizeofimage,这是因为,有的时候sizeofimage的大小并不是一个文件对齐度的大小,而为了让他对齐,我使用下面的算法。
代码:
if(!(sizeofimage%0x1000)) //如果是文件对齐度的整数倍的时候就不处理
sizeoffile=sizeofimage;
else
sizeoffile=(sizeofimage/0x1000+1)*0x1000; //如果不是就增加一个文件对齐度

这样就能保证,imageofsize转化为一个具有文件对齐度的数值。
最后,是使用BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem);这个函数,这个函数的作用是在磁盘中申请一个文件,把从刚刚读到内存空间的数据又全部的写到磁盘上,所以就用了两个函数CreateFile和WriteFile。

三.试验
写好了程序就如下图一样
下面用他来实战使用一下吧
运行用upx压缩过的win98的notepad

让我把他在oep处dump下来

嘿嘿,dump下来才发现,图表都没有了。呵呵,这是因为我们没有把相对虚拟地址(RVA)和文件地址对齐(RA)。
科普一下:在文件中要节约空间,把数据紧密的存储在一切,而靠节表在load到内存中的时候,把在文件中的不同数据分别隐射到不空内存空间中,而不足的地方就用0填充。但是当我把这些数据从内存中完完整整的dump下来的时候,我们同样把这些0也dump了出来。所以我们要做的首先应该是调整节表,让他的RA=RVA。这样我们实际上做的就是把RA这个指针移动过了若干个0的空间,从而指向正确的数据。

下面我们还是用LoadPE打开它,把RA和SIZE修改过来吧!

这么样,回到桌面刷新一下就会发现图标又回来了吧!
好了,现在运行dumped.exe文件,还是不行的。对了,IAT还没有修复呢,我们停在OEP的时候文件的IAT已经全部释放了出来,而IAT只是对于本计算机的函数地址,每一个计算机都可能不一样。这样我们就要用ImportRECl来重新根据这个IAT地址反向构造一个输入表。OK,使用ImportREC修复了以后就可以运行了,不过dump出来的文件大出了好多。我们可以知道原因了。
1. 因为文件对齐的原因我们把残留的0也dump出来了。
2. 因为使用ImportREC他帮我们重新创建了一个区段存放输入表
所以,我们使用LordPE的ReBuiltPe其中的一个功能就是可以把这些残留的0,自动的删除,然后修改节表让他们对齐。

四.小结
经过了一番的讲解,大家应该知道,LordPE的dump功能大概是怎么运作的了吧,当然从我们的程序从还有很多的不足,从上面例子也能知道了。LordPE在dump下程序的同时,直接将节表中的RA=RVA ,RS=RVS。那么在以后我们将继续改进我们的dump程序,让它增加这些功能。万事开头难,如果你有足够的耐心读完了这一章那么下面的章节就很简单了,不要怕继续吧!











第二章. Dump程序的改进
“发展才是硬道理”
----邓小平
正如我们上面一节所做的,一个dump的雏形已经形成了,现在要做的是给它在原来的基础上做进一步的改进。

一. 获取Imageofsize

相信大家也感觉到了,在前面一章介绍的获取ImageofSize的办法实在很不可靠,对于一个加壳程序随意的修改他载入了内存中的PE头,是很稀疏平常的事情。所以用读取目标进程中的PE头并不是一个好主义。
现在我就来介绍一下一般的办法。对于LordPE他的dump是采用什么方法呢?它采用了对Module32Next来获取dump的进程的基本信息的。

BOOL WINAPI Module32First(
HANDLE hSnapshot, //这是先前的CreateToolhelp32Snapshot函数返回的快照
LPMODULEENTRY32 lpme //这个是指向MODULEENTRY32结构的指针
);

下面是MUDULEENTRY32结构:
typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize; //这个是是我们要获取的关键
HMODULE hModule;
TCHAR szModule[MAX_PATH];
TCHAR szExePath[MAX_PATH];
DWORD dwFlags;
} MODULEENTRY32, *PMODULEENTRY32, *LPMODULEENTRY32;

下面就给出重新写的新的GetSizeOfImage的函数
int GetSizeOfImage(HWND hDlg,DWORD IDProcess)
{
//这个函数的作用是获取SizeOfImage的数值
//当函数执行失败返回的是0
//成功返回的是非0
HANDLE hModuleSnap = NULL;
MODULEENTRY32 stModE = {0};
stModE.dwSize = sizeof(MODULEENTRY32);
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess); //快照,对本进程中所有的模块进行snap

if (hModuleSnap == INVALID_HANDLE_VALUE)
{
MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
return FALSE; //返回0
}
if (!Module32First(hModuleSnap, &stModE))
{
MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
CloseHandle (hModuleSnap);
return FALSE;
}
CloseHandle (hModuleSnap);
return stModE.modBaseSize;//初始化为0
}

二. 对齐节表

这个问题在上一节我们已经提出了,我们的程序不完善,现在要自动的实现RA=RVA ,RS=RVS这个功能。那么我们就使用一个函数来完成它吧!我定义了下面这个函数:
BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name)
{
//此函数的将修改dump下来的exe,使其RA=RVA ,RS=RVS
//首先是打开dump文件
HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{

MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
return FALSE;
}
//下面移动到节表前面
IMAGE_DOS_HEADER myDosHeader;
DWORD NumberOfBytesRead;
ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesRead,NULL);
SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
IMAGE_FILE_HEADER myNtHeader;
ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesRead,NULL);
int nSectionCount;
nSectionCount = myNtHeader.NumberOfSections; // 保存Section个数
// 过了IMAGE_NT_HEADERS结构就是IMAGE_SECTION_HEADER结构数组了,注意是结构数组,有几个Section该结构就有几个元素
// 这里动态开辟NumberOfSections个内存来存储不同的Section信息
IMAGE_SECTION_HEADER *pmySectionHeader = (IMAGE_SECTION_HEADER *)calloc(nSectionCount, sizeof(IMAGE_SECTION_HEADER));
SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
ReadFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER)*nSectionCount,
&NumberOfBytesRead,NULL);
//移动回到节表的开始,准备写入
SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
for (int i = 0; i < nSectionCount; i++, pmySectionHeader++)
{
//将RA=RVA ,RS=RVS
pmySectionHeader->SizeOfRawData=pmySectionHeader->Misc.VirtualSize;
pmySectionHeader->PointerToRawData=pmySectionHeader->VirtualAddress;
//将修改好的数值写回
WriteFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesRead,NULL);
}
// 恢复指针
pmySectionHeader -=nSectionCount;

if (pmySectionHeader != NULL) // 释放内存
{
free(pmySectionHeader);
pmySectionHeader = NULL;
}

// 最后不要忘记关闭文件
CloseHandle(hFile);
return TRUE;
}

这个函数是在生成了一个新的dump文件以后,然后在打开它,找到节表。把里面的数据修改过来。

三. 小节
这样,一个改进型的程序就搞定了。现在当我们用他dump下新的程序的时候得到的就是一个有图标的程序了。因为我们已经把节表修改过来了。下面一章我们将介绍anti-dump的技巧。



























第三章.Anti-dump的原理
"我们踏进又踏不进同一条河,我们存在又不存在。"
----赫拉克利特
事物往往就是这样,它在发展中需要正面的也需要反面的,他们是对立统一在一起的。在前面我们的dump已经基本和一个LordPE的功能差不多了,现在我们要分析一下,一个程序是如何anti-dump的,同时我们也会给出一些简单的解决办法。

注意:下面所说的方法都是在NT内核下的执行的,并不涉及9x下的。

一.纠正imagesize
这里我们首先讨论一下关于LordPE的其中一个correct ImageSize的功能。

首先我们要知道,这个anti是如何产生的。根据上面的介绍我们知道,我们获得的image size是通过MODULEENTRY32的结构的快照,但是这个结构是从那里获得这些数据的呢?
答案是通过PEB(Process Environment Block)进程环境模块获得的。下面是PEB的结构。
struct _PEB (sizeof=488)
+000 byte InheritedAddressSpace
+001 byte ReadImageFileExecOptions
+002 byte BeingDebugged
+003 byte SpareBool
+004 void *Mutant
+008 void *ImageBaseAddress
+00c struct _PEB_LDR_DATA *Ldr
+010 struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters
+014 void *SubSystemData
+018 void *ProcessHeap
+01c void *FastPebLock
+020 void *FastPebLockRoutine
+024 void *FastPebUnlockRoutine
+028 uint32 EnvironmentUpdateCount
+02c void *KernelCallbackTable
+030 uint32 SystemReserved[2]
+038 struct _PEB_FREE_BLOCK *FreeList
+03c uint32 TlsExpansionCounter
+040 void *TlsBitmap
+044 uint32 TlsBitmapBits[2]
+04c void *ReadOnlySharedMemoryBase
+050 void *ReadOnlySharedMemoryHeap
+054 void **ReadOnlyStaticServerData
+058 void *AnsiCodePageData
+05c void *OemCodePageData
+060 void *UnicodeCaseTableData
+064 uint32 NumberOfProcessors
+068 uint32 NtGlobalFlag
+070 union _LARGE_INTEGER CriticalSectionTimeout
+070 uint32 LowPart
+074 int32 HighPart
+070 struct __unnamed3 u
+070 uint32 LowPart
+074 int32 HighPart
+070 int64 QuadPart
+078 uint32 HeapSegmentReserve
+07c uint32 HeapSegmentCommit
+080 uint32 HeapDeCommitTotalFreeThreshold
+084 uint32 HeapDeCommitFreeBlockThreshold
+088 uint32 NumberOfHeaps
+08c uint32 MaximumNumberOfHeaps
+090 void **ProcessHeaps
+094 void *GdiSharedHandleTable
+098 void *ProcessStarterHelper
+09c uint32 GdiDCAttributeList
+0a0 void *LoaderLock
+0a4 uint32 OSMajorVersion
+0a8 uint32 OSMinorVersion
+0ac uint16 OSBuildNumber
+0ae uint16 OSCSDVersion
+0b0 uint32 OSPlatformId
+0b4 uint32 ImageSubsystem
+0b8 uint32 ImageSubsystemMajorVersion
+0bc uint32 ImageSubsystemMinorVersion
+0c0 uint32 ImageProcessAffinityMask
+0c4 uint32 GdiHandleBuffer[34]
+14c function *PostProcessInitRoutine
+150 void *TlsExpansionBitmap
+154 uint32 TlsExpansionBitmapBits[32]
+1d4 uint32 SessionId
+1d8 void *AppCompatInfo
+1dc struct _UNICODE_STRING CSDVersion
+1dc uint16 Length
+1de uint16 MaximumLength
+1e0 uint16 *Buffer

我们从FS:[30]就可以获得这个PEB的首地址。然后在0C处的_PEB_LDR_DATA *Ldr是一个关键通过它,我们能访问到

typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

该结构的后三个成员是指向LDR_MODULE链表结构中相应三条双向链表头的指针,分别是按照加载顺序、在内存中的地址顺序和初始化顺序排列的模块信息结构的指针。于是通过它,我们能访问到_LDR_MODULE结构,而这里面包括了本进程的SizeOfImage。
_LDR_MODULE结构如下:
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage; //进程的image size
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;

所以,我们得到关键的代码就是:
////////////////////////////////////////////////////////////////////////////////////
//这里的几个代码是修改PEB的关键
__asm
{
mov eax,fs:[30h] //获得PEB地址
mov eax,[eax+0ch] // +00c struct _PEB_LDR_DATA *Ldr
mov eax,[eax+0ch] // _LDR_MODULE的首地址
mov dword ptr [eax+20h],1000h //eax+20是保存image size的地方

}
////////////////////////////////////////////////////////////////////////////////////
上面的代码的作用就是把image size的大小改为了1000h,这样我们用MODULEENTRY32得到的大小是不准确的。所以大家在跟踪壳的时候特别要小心上面的几行代码。如果看见它就直接跳过吧。

好了,知道他是怎么anti的以后看看LordPE是怎么使用correct ImageSize的,我稍微跟踪了一下它的的代码。发现了下面的API函数:
1. GetProcessBaseSize
2. GetProcessPath
3. CreateFileA
4. ReadFile
看到这里想必大家也知道,怎么回事了吧。他在获得了绝对路径以后打开了磁盘上的PE文件,读取里面PE头的ImageSize,来纠正这个错误了的image size。恩,或许你会说,那么我写一个anti来修改PE文件头的ImageSize,让它获得的还是错误的。呵呵,我不得不提醒你的是,任何对文件的操作都是危险的。我们来设想一下这种情形:当你把原来的PE文件打开了以后,把一个错误的ImageSize写入了这个PE文件里面,使LordPE的correct ImageSize仍然无功而反,而打算在某一个地方,再次打开文件把正确的信息写回PE文件。(否则你下次就别想打开它了)且不说这样的办法很烦琐,我们设想一下这个时候,你的机器死机了!你不得不重起机器。而你的收尾工作还没结束。于是你的PE文件除了anti的作者,别人在不知道为什么的情况下,再也别想打开它了。

所以一个聪明的程序员,都不会做这种危险的事情。好了,来看看我是如何实现这个功能的吧。我在原来的基础上填加了一个按钮“纠正大小”的按钮。那再重新定义了一个函数来实现这个功能:

BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList)
{
//函数能获取文件的PE头部的SizeOfImage,作为正确的SizeOfImage
LPCTSTR File_Name=NULL;
WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
if (tmp==LB_ERR)
{
MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
return FALSE;
}
DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //获得此列单里面的进程ID
ID=IDProcess;//全局变量ID的作用是控制在不同的进程的切换
File_Name=GetFilePath(hDlg,IDProcess);
if(!File_Name)
return FALSE;
//打开文件
HANDLE hFile;
hFile=CreateFile(File_Name,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

if (hFile == INVALID_HANDLE_VALUE )
{
return FALSE ;
}

//创建文件映射内核对象
HANDLE hMapping;
hMapping = CreateFileMapping (hFile, NULL, PAGE_READONLY,0,0,NULL);
if (hMapping == NULL )
{
CloseHandle (hFile ) ;
return FALSE;
}
//创建文件视图
LPVOID ImageBase ;
ImageBase =MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0) ;
if (ImageBase == NULL)
{
CloseHandle (hMapping) ;
return FALSE;
}
//下面的代码就是从文件的PE头找到SizeOfImage的
PIMAGE_DOS_HEADER DosHead = NULL ;
PIMAGE_NT_HEADERS32 pNtHeader = NULL ;
PIMAGE_FILE_HEADER pFileHeader = NULL ;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL ;
PIMAGE_SECTION_HEADER pSectionHeader = NULL ;
DosHead=(PIMAGE_DOS_HEADER)ImageBase;
pNtHeader = ( PIMAGE_NT_HEADERS32 ) ((DWORD)ImageBase + DosHead->e_lfanew ) ;
pOptionalHeader = &pNtHeader->OptionalHeader;
sizeofimage=(int)pOptionalHeader->SizeOfImage;
//找到了以后,输出结果
char szBuffer[100];
char szMsg[]="原来的image size是:%08X\n修整的image size是:%08X";
wsprintf(szBuffer,szMsg,GetSizeOfImage(hDlg,IDProcess),sizeofimage);
MessageBox(hDlg,szBuffer,TEXT("纠正结果"),MB_OK );
CloseHandle (hMapping);
CloseHandle (hFile) ;
Sleep(200);
return TRUE;
}

这个功能实现到是很简单,但是同时要强调的一点是,在我在协调纠正的进程和dump进程是不一样的进程的时候,出了一些小问题。我采用了一个很愚蠢的办法,定义了一个全局变量ID来控制这种事情的发生。

二.实战

这个modify_module_imagesize程序包括了上面的修改PEB里面image size的代码。运行它:


可以看到原来的image size已经被修改了。

好了,现在用我们的dump来试试吧。


这样我们再dump它,就能得到原来C000大小的进程了。


二.其他anti方式
根据上面的MODULEENTRY32结构,我们能得到很多启发。毕竟懂得了原理,那么寻找anti的办法是不难的。首先是基地址,我们能不能修改PEB中的基地址呢?
事实上,这种方法会产生不必要的麻烦。也就是说不可行。
另一种办法是修改文件的路径,这种办法很好。但是只是能在9x中使用,因为目前为止,不知道在NT内核下的这种办法。所以在这里我也不介绍了。有兴趣的读者可以去看看《软件加密技术内幕》P204-P207,在那里hying有完整的介绍。
下面要介绍的是一种非常普遍,也很常用的办法。就是利用内存属性进行anti。
我们知道,如果我们打开了一个进程,那么要读写进程里面的内容,必须要获得应有的权限读权利。实际上,当一个exe文件被load到内存里面的时候,他的所有取段都具有读的权利,而正是这样我们才能把他从内存中读出来,写到磁盘文件上面。所以,我们的anti来了,如果我们将部分不用区段的权限修改一下,把他的读权限取消。会发生什么结果呢?
让我们来做一个简单的实验吧!
/*------------------------------------------------------------------------
Save as Modify_the_read_right.cpp
(c) Lenus Margin 2005 10.15
-------------------------------------------------------------------------*/

#include
#include
#include

void main()
{
DWORD dwOldProtect;
HMODULE ImageBase=GetModuleHandle(NULL);
VirtualProtect(ImageBase,1000,PAGE_NOACCESS,&dwOldProtect);//将PE头改为不可访问的的属性
cout<<"The PE HEAD read right change to no_access!!"< cout<<"U can try to dump at here!"< getchar();
}

等运行了以后,我们用LordPE来dump它吧,发现了什么没有?一个经典的对话框!

这个东西,相信大家一定见过。让我们看看,在这个进程里面的400000处吧!

选中进程---右键---dump region就可以看到上面的窗口了。果然已经变成了NOACCESS的属性了。
那么应该怎么解决呢?这个我们就要请出更强的工具来了。现在我们就用OD的ollydump来dump它!
用OD载入以后,F9运行!好了,这时我们可以用LordPE还是看到上面的NOACCESS的属性,但是我们到OD的内存镜象里面看看是什么结果呢?

我们可以看到,他仍然是可读,可写,可执行的权限。
为什么会这样呢?
因为OD这个调试器需要编辑这个进程的空间中的数据,而如果一个区段因为设置了NOACCESS而得不到访问,不能编辑实在是不可想象的。所以他在我们需要使用这段空间的时候就将它的属性临时的修改到了完整的权限,而当我们使用完毕以后呢?再修改回来,可以说,ring3的调试器老是在干这种事情,比如说int3的放置也是这样的。从这些侧面我们也能体会到OD的强大。

好了,现在用ollydump就可以dump下来了。

四.小结
小结一下。关于anti这一章,以上的这些只是一些常见的手段。事实上,矛盾的较量永远不会停止,正如开篇的赫拉克利特所说"我们踏进又踏不进同一条河,我们存在又不存在。"在哲学上事物是矛盾的统一,没有了此消彼长的竞争,也没有了任何存在的可能。而在加密解密的世界anti-dump也是一个大学问,从而我们在避开anti的同时也会发现很多很巧妙的方法。好了原理就说到这里,下面一章我们将转入实战,让你感受另类的脱壳!




























第四章.Dump的位置
The Golden Rule is that there are no golden rules.
真正的金科玉律就是世上并无金科玉律。——G.B.Shaw
现在,你是不是还认为要dump就一定只能在OEP处呢?你现在是不是还觉得dump是一件没有技术含量的工作呢?好,下面我将详细的说明一下在什么地方dump将会得到事半功倍的效果!
一. 在OEP处dump
一般来说,入门及的菜鸟都知道,在oep处dump下进程是正确的,当OD停在push ebp的时候,我们冲动的神情就连显示器也会害怕。在LordPE,Ollydump…..十八般兵器全用上以后,如果我们得到的是一个不能dump的对话框,那么一切就完了,此壳也因此打入冷宫!
那么,我们首先要知道。为什么我们要在OEP处dump呢?
一般来说,一个简单的压缩壳(比如UPX),它在运行的时候会做什么事情呢?
1. 它要把压缩了的全部的代码数据释放到内存中。
2. 它要把IAT的地址写到适当位置
那么,它什么时候完成这些工作呢?
答案是最后一刻!
对,让我们来看看UPX的最后几句!
0040EA8D 09C0 or eax,eax
0040EA8F 74 07 je short NOTEPAD.0040EA98
0040EA91 8903 mov dword ptr ds:[ebx],eax ; kernel32._lwrite
0040EA93 83C3 04 add ebx,4
0040EA96 ^ EB E1 jmp short NOTEPAD.0040EA79
0040EA98 FF96 A8EC0000 call dword ptr ds:[esi+ECA8]
0040EA9E 61 popad
0040EA9F - E9 2826FFFF jmp NOTEPAD.004010CC
明显它在做“写”的工作,当写完了也就JMP OEP了。所以当它完全解压缩了以后,当它把IAT都写好了以后。我们就可以dump它了。而我们更多的选择在OEP的地方就是这个原因。因为在OEP处,它的代码和IAT一般是会解压完毕了的。
二. 不在OEP处dump
现在我们知道了,dump的原则了以后。那么很好办,我们不一定要在OEP处dump了。好,我们可以在jmp NOTEPAD.004010CC这里dump,也可以在0040EA9E 61 popad这里dump。当然还可以在
004010CC 55 push ebp
004010CD 8BEC mov ebp,esp //在这里
004010CF 83EC 44 sub esp,44 //在这里
哈哈,什么位置都可以dump!为什么一定要在OEP处呢?!
三. 他山之石
下面我们来看看两篇高手的范文,让我们更深刻的理解一下dump的技巧,dump的花样。^-^

1. 首先是fly的《Thinstall V2.501脱壳——Win98的Notepad》
http://bbs.pediy.com/showthread.php?s=&threadid=8158
下面是引用部分

二、获取加壳前程序的PE Header等数据


Ctrl+S 搜索命令序列:
mov eax,dword ptr ds:[eax+3C]
mov ecx,dword ptr ss:[ebp-118]
找到在7FF427BA处,直接F4过去

7FF427A5 8B85 D4FDFFFF mov eax,dword ptr ss:[ebp-22C]
7FF427AB 8B40 18 mov eax,dword ptr ds:[eax+18]
7FF427AE 8985 E8FEFFFF mov dword ptr ss:[ebp-118],eax
7FF427B4 8B85 E8FEFFFF mov eax,dword ptr ss:[ebp-118]
7FF427BA 8B40 3C mov eax,dword ptr ds:[eax+3C]
//F4到这里
7FF427BD 8B8D E8FEFFFF mov ecx,dword ptr ss:[ebp-118]
7FF427C3 8D4401 18 lea eax,dword ptr ds:[ecx+eax+18]
7FF427C7 8985 D0FDFFFF mov dword ptr ss:[ebp-230],eax ; Notepad.004000E8
//在这里可以得到PE Header和The Section Table数据 ★
7FF427CD C705 D05DF97F 020>mov dword ptr ds:[7FF95DD0],2

PE Header:
004000D0 50 45 00 00 4C 01 01 00 D6 57 5A 35 00 00 00 00 PE..L .諻Z5....
004000E0 00 00 00 00 E0 00 0E 01 0B 01 03 0A 00 40 00 00 ....?
..@..
004000F0 00 70 00 00 00 00 00 00 CC 10 00 00 00 10 00 00 .p......?... ..
00400100 00 50 00 00 00 00 40 00 00 10 00 00 00 10 00 00 .P....@.. ... ..
00400110 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ....... .......
00400120 C6 CA 00 00 00 04 00 00 00 00 00 00 02 00 00 00 剖... ...... ...
00400130 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 .. .. .... .. ..
00400140 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 .... ...........
00400150 00 60 00 00 8C 00 00 00 00 70 00 00 C8 42 00 00 .`..?...p..菳..
…… ……

毕竟只是捆绑壳,此时代码已经解压,现在dump则IAT等信息都是未加密的,正是脱壳的最佳时机!

我们可以看到,文中提到在跟踪到7FF427BA的时候整个IAT的信息已经解压完毕,而且还没加密,并且这时的代码也释放完毕了。所以在这里是完美的dump时机。而如果我们在寻找到OEP处的时候,那么一切都已经完了。狡猾的壳,把该隐藏的都隐藏起来了。那时我们再去dump,得到的只是作者想要我们得到的东西,那只能说我们中了圈套了!

2. 接着是csjwaman的《Steel Box 脱壳》
http://bbs.pediy.com/showthread.php?s=&threadid=8356
下面是引用部分
共有4处要修复。其中第4处是程序退出时运行的。004041D5处下断,运行程序,然后退出程序。程序就会断在004041D5处。此时要修复的代码都已解开:
00404100 55 PUSH EBP
00404101 8BEC MOV EBP,ESP
00404103 53 PUSH EBX
00404104 56 PUSH ESI
00404105 57 PUSH EDI
00404106 BB 00704000 MOV EBX,anota2.00407000
0040410B 66:2E:F705 8E47>TEST WORD PTR CS:[40478E],4
00404115 0F85 DB000000 JNZ anota2.004041F6
0040411B 6A 00 PUSH 0
0040411D FF15 D0824000 CALL NEAR DWORD PTR DS:[4082D0]
00404123 E8 9C020000 CALL anota2.004043C4
00404128 C783 08010000 0>MOV DWORD PTR DS:[EBX+108],1////已修复处1
00404132 8D83 94020000 LEA EAX,DWORD PTR DS:[EBX+294]
00404138 50 PUSH EAX
00404139 FF15 84824000 CALL NEAR DWORD PTR DS:[408284]
0040413F 83EC 44 SUB ESP,44
00404142 C70424 44000000 MOV DWORD PTR SS:[ESP],44
00404149 C74424 2C 00000>MOV DWORD PTR SS:[ESP+2C],0
00404151 54 PUSH ESP
00404152 FF15 7C824000 CALL NEAR DWORD PTR DS:[40827C]
00404158 B8 0A000000 MOV EAX,0A
0040415D F74424 2C 01000>TEST DWORD PTR SS:[ESP+2C],1
00404165 74 05 JE SHORT anota2.0040416C
00404167 0FB74424 30 MOVZX EAX,WORD PTR SS:[ESP+30]
0040416C 83C4 44 ADD ESP,44
0040416F 8983 FE000000 MOV DWORD PTR DS:[EBX+FE],EAX
00404175 FF15 60824000 CALL NEAR DWORD PTR DS:[408260]
0040417B E8 21040000 CALL anota2.004045A1
00404180 8983 F4000000 MOV DWORD PTR DS:[EBX+F4],EAX
00404186 FF15 68824000 CALL NEAR DWORD PTR DS:[408268]
0040418C 8983 F0000000 MOV DWORD PTR DS:[EBX+F0],EAX
00404192 6A 00 PUSH 0
00404194 FF15 78824000 CALL NEAR DWORD PTR DS:[408278]
0040419A 8983 04010000 MOV DWORD PTR DS:[EBX+104],EAX
004041A0 E8 07010000 CALL anota2.004042AC
004041A5 B8 FF000000 MOV EAX,0FF////已修复处2
004041AA 72 36 JB SHORT anota2.004041E2
004041AC E8 5E020000 CALL anota2.0040440F
004041B1 E8 3B040000 CALL anota2.004045F1
004041B6 B8 FF000000 MOV EAX,0FF////已修复处3
004041BB 72 07 JB SHORT anota2.004041C4
004041BD 53 PUSH EBX
004041BE E8 57010000 CALL anota2.0040431A
004041C3 5B POP EBX
004041C4 50 PUSH EAX
004041C5 E8 55010000 CALL anota2.0040431F
004041CA E8 37010000 CALL anota2.00404306
004041CF FFB3 F0000000 PUSH DWORD PTR DS:[EBX+F0]////已修复处4
004041D5 FF15 5C824000 CALL NEAR DWORD PTR DS:[40825C]

把这些代码复制后覆盖到主程序404100-4041D4处就完成了。
上面的这段是在说明这么修复代码的段落。这壳在到达OEP处的时候有4个地方,并没有将代码完全的解压。所以我们在OEP处dump就得到4处没有解压的代码,而文中提到的办法是,先把程序dump下。等待程序运行把这些代码解压以后再进行粘贴。
“把这些代码复制后覆盖到主程序404100-4041D4处就完成了。”
这当然是个很好的办法,符合我们脱壳的逻辑。而我们在懂得他的原理以后大可大胆的想象。我们在等待这次处接压完毕以后再dump会如何呢?
四. 实战
下面我们来脱一个比较有趣的壳吧!听说这个壳在9x是秒脱,但是在NT内核下的就不是那么简单的了。^-^
目标:VBExplorer
加壳方式:Shrinker 3.4
OD载入以后
寻找OEP的过程很简单,在找到最后的异常以后
00554E7B 8B1F mov ebx,dword ptr ds:[edi]//最后的异常
00554E7D 85DB test ebx,ebx
00554E7F 74 6C je short VBExplor.00554EED
00554E81 837D F4 00 cmp dword ptr ss:[ebp-C],0
00554E85 74 08 je short VBExplor.00554E8F

我们用F8走几步就能到达JMP OEP的地方
005546AE 8945 E0 mov dword ptr ss:[ebp-20],eax
005546B1 FF75 10 push dword ptr ss:[ebp+10]
005546B4 FF75 0C push dword ptr ss:[ebp+C]
005546B7 FF75 08 push dword ptr ss:[ebp+8]
005546BA FF55 E0 call dword ptr ss:[ebp-20] ; VBExplor.00425342
//上面的就是JMP OEP的地方

下来我们看看OEP是什么东西:
00425342 0000 add byte ptr ds:[eax],al //OEP
00425344 0000 add byte ptr ds:[eax],al
00425346 0000 add byte ptr ds:[eax],al
00425348 0000 add byte ptr ds:[eax],al
0042534A 0000 add byte ptr ds:[eax],al
0042534C 0000 add byte ptr ds:[eax],al
0042534E 0000 add byte ptr ds:[eax],al
00425350 0000 add byte ptr ds:[eax],al
00425352 0000 add byte ptr ds:[eax],al
00425354 0000 add byte ptr ds:[eax],al
00425356 0000 add byte ptr ds:[eax],al

这就是我们得到的OEP,怎么回事呢?怎么没有数据呢?
这个壳在之前并没有解压这个壳的代码和数据,而当我们在运行的时候执行或者读取这些指令那么就会产生异常,而这时壳代码再试图将他们解压。哈哈…现在知道了吧,如果我们在OEP处dump能得到什么呢?!
好了,我们下断点BP ExitProcess。对!我们要在它退出的时候断下来!
OK,我们忽略掉所有的异常,运行它吧!让主界面出现在我们的机器上!这时我们要做的是让他的代码完全的从内存中释放出来!如果你不放心还可以使用一下这个软件的功能^-^

好了,退出吧,让它中断下来!
7C81CAA2 > 8BFF mov edi,edi //断在这里
7C81CAA4 55 push ebp
7C81CAA5 8BEC mov ebp,esp
7C81CAA7 6A FF push -1

行了,我们可以dump了….等等,在这里dump大家都明白它的原理了吧!使用LordPE来看看!出错了,不行吧!好看看是怎么回事呢?

发现了什么没有,原来它在释放数据的过程中,只释放了必要的一部分而另一部分并没有改变它的属性仍然是NOACCESS的属性。那么好吧!用OllyDump上吧。可是你会发现还是不行啊!

确定以后虽然能dump下来,但是却是0K的大小!
呵呵,为什么我们以前举的例子里面就可以,现在就不可以了呢?
答案很简单,我们以前是把整个区段从开头都设置成了不可访问的区段,而现在的只是把其中的一块设置成了NOACCESS,也就是说这个技巧欺骗了OD!
现在怎么办!
Fellow Me!!
用Alt+M打开内存镜象!

看到“完整权限”的字样了没有?我们将每个区段重新都设置它为完整的权限一次!
好了,现在我们再用LordPE看看吧!

看到了吧,不得不佩服OD的强大功能啊!

现在你可以用任何的工具将它dump下来了!注意OEP=425342
用ImportREC可以简单的修复它的输入表。但是遗憾的是,事情还没有结束,在修复完了以后还有些毛病!
那就是运行以后,所有的资源好象都不见了,其实它只是有部分代码没有执行而已。OK,关于dump的我就讲这么多了。以后的完美脱壳,就留给大家去努力吧!
五. 小结
这是一个站在巨人肩膀上的时代,多东西需要借助别人的经验继续工作。而吸收和改进将是这个工作必要的阶段。
也许看了上面的文字,你会问我2个问题:
1. 我怎么知道代码或者IAT什么时候解压完毕?
2. 我怎么确定这个是最佳的dump的时机?
说实在的,这个是一个经验和辛苦的工作,作为菜鸟加懒鸟的我实在不愿意一步一步的跟踪,把整个壳的思想弄清楚。如果你能作到把整个壳的思路弄明白,把这个壳逆向一遍。那么我想上面的问题也是自然而然的解决了。所以,我们要对那些把整个壳的代码逆向的高手们致敬。同时我也要告诉各位,只要大家有时间,应该试这向这个方面努力。毕竟,只有这个方向才是一个真正的cracker应该做的,因为逆向学习才是cracker的第一精神,而破解的成果是第二位的。



































第五章.Dump工具的比较
“今天将要结束,明天也将结束,难以结束的是昨天。”
---安东尼奥•波契亚
在论坛上经常会看到一些人问:什么工具dump好用?为什么要用ollydump来dump呢?等等。下面我来总结一下几种dump工具的优缺点。

注意:下面的文字只是我个人的感觉,由于水平问题很难保持客观性。请三思!

一. 比较

1. ProcDump
这个工具我现在也不太用了,他是使用MODULEENTRY32来获取进程信息的工具,它会读取目标进程的IMAGE_DOS_SIGHATURE和IMAGE_NT_SIGNATURE标志。其他的就不检验了。如果不完整,他会打开进程的原始文件,读取它的原始文件,读取它的文件头以取代进程的文件头。


2. LordPE
这个工具我使用得最多,但并不是说它是最好的。只是一个习惯问题。它基本上也和ProcDump一样使用MODULEENTRY32来获取进程信息。然而我们可以看到LordPE的Options里面的设置

在这里面就有直接从磁盘获取PE Head的默认选项,也就是说基本上他的PE Head都是从磁盘获得的。
同时,在观察区域属性和获取其他的区域数据的时候LordPE的dump region功能非常强大,这点是OllyDump不足的。在以壳解壳的技巧中,LordPE的此功能将是决定意义的。但是它的不足前面也提到过,有些进程在权限上不可读的时候,将是它致命的伤!

3. OllyDump
这是一个OD的插件,基本上的原理和功能差不多。但是,正是由于它是OD的插件,它能通过OD获得更多的权限。在前面的例子中我们能看到,对于内存属性的问题,依靠OD强大的功能OllyDump能dump下一些LordPE所不能dump的进程。而它的“获取EIP值作为OEP”也是一个亮点。

至于缺点那么,最最突出的就是上面的默认的重建输入表选项。我们知道,对于一些加密壳来说,他们的输入表就连ImportREC这样专业的输入表修复工具,它的三个等级的修复也未必能修复出来,而这两个方式1和方式2的确有点添足的意思。其实我并不是反对它增加功能,我只是希望不要做成默认的选项!当然如果还是不改进,有空还是要DIY一下的。^-^
第二个缺点,就是它不能dump下其他区域的数据,这一点很是无奈。这也是为什么我比较习惯使用LordPE的原因。
二.小结
工具的好坏,只是思想和工作程度的问题。只要我们掌握了工具工作的原理和思想,那么加以时日,我们也可以写出自己用着顺手的工具来。“真正的cracker高手的工具都是自己写的,内部使用的”对于前半句,我双手赞成,对于后半句嘛….我觉得cracker的精神里面除了学习(learn)的第一精神以外,还应该是共享(share)的精神。












总结:
Dump技术在脱壳中的重要地位不言而喻,事实上这是由于这个from memory to disk的过程,我们才会把这个“脱壳”这两个字理解得如此形象。
在基本的脱壳概念中,我们似乎一直被公式化,教条化的东西是束缚着,在这里我觉得我这篇文章要教会大家的不仅仅是dump的技巧,而是要在原理的层次上进一步的探索,对于只能照本宣科的人来说,创新是永远不可能的事情。而如果不能真正的理解原理,理解为什么那么我们只能永远的停留在应用的阶段。
由于时间关系,在文中有大量的代码和一些概念由于时间关系没有解释清楚,而对于内存属性的保护,以及模拟LordPE的更多的功能将是以后和各位的工作了。这一点我觉得“砖”能这样抛,相信“玉”也能令人期待了。

致谢:
在国庆期间,我决定写这篇文章的时候。我得到了很多人的帮助和鼓励。首先,要感谢的是朋友,他们是love8909和sen两位兄弟,而发表在成员区的时候也得到了论坛成员的的鼓励。当然,你可能说这些东西都是别人的。没错,基本上我上面所学习到的知识并不新颖,是《软件加密技术内幕》中hying教会了我决大部分东西,要好好的谢谢他(也许他并不这么认为)。当然还有KanXue大哥,在他的支持下我能顺利的发表这篇文章。而在编写代码的同时,对于进程模块的exe确定的问题,也捆饶了我很久,在这里我要谢谢forgot,是他的提醒,我才能坚定了我的做法!还有很多要感谢的人,但是在最后我却想把这篇文章送给一个我最爱的女孩,经管也许她永远不会看到这些话….

参考文献:

《软件加密技术内幕》--- 看雪学院
《windows环境下32位汇编语言程序设计》--- 罗云彬
《Windows 程序设计》---- Charles Petzold
《JIURL玩玩Win2k进程线程篇 PEB》--- JIURL
《病毒编程技术-API函数地址的获取》--- 温玉洁
《PE结构分析软件--SDK开发实践》--- 北极星2003
《PE文件之旅》 --- prince

个人简介:
马骏(Lenus Margin),生于1981.10.12,广西柳州 ,在度过了痛苦6年小学,比较痛苦的6年中学,现在在南京邮电大学度过非常痛苦的7年大学生活。本科专业是电子科学与技术(基本上和电子没有关系,关心的只是光子)。目前于南邮研究生二年级,继续研究“光学工程”专业“光通信与光信息处理” 方向。
于2004年7月跨入密界,经过一年的潜水终于混成了菜鸟。不想上站一看,发现廉颇老矣。密界新人倍出,而老将背影还未曾望见。也罢,写此小文聊以慰籍。望各位看官走过路过,千万不要骂过….阿弥陀佛,善哉,善哉。